Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入了解 Go ELF 信息

深入了解 Go ELF 信息

作者头像
Jintao Zhang
发布于 2022-12-07 06:26:50
发布于 2022-12-07 06:26:50
83200
代码可运行
举报
文章被收录于专栏:MoeLoveMoeLove
运行总次数:0
代码可运行

大家好,我是张晋涛。

我们用 Go 构建的二进制文件中默认包含了很多有用的信息。例如,可以获取构建用的 Go 版本:

(这里我使用我一直参与的一个开源项目 KIND[1] 为例)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ go version ./bin/kind 
./bin/kind: go1.16

或者也可以获取该二进制所依赖的模块信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ go version -m ./bin/kind
./bin/kind: go1.16
        path    sigs.k8s.io/kind
        mod     sigs.k8s.io/kind        (devel)
        dep     github.com/BurntSushi/toml      v0.3.1
        dep     github.com/alessio/shellescape  v1.4.1
        dep     github.com/evanphx/json-patch/v5        v5.2.0
        dep     github.com/mattn/go-isatty      v0.0.12
        dep     github.com/pelletier/go-toml    v1.8.1  h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
        dep     github.com/pkg/errors   v0.9.1
        dep     github.com/spf13/cobra  v1.1.1
        dep     github.com/spf13/pflag  v1.0.5
        dep     golang.org/x/sys        v0.0.0-20210124154548-22da62e12c0c      h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
        dep     gopkg.in/yaml.v2        v2.2.8
        dep     gopkg.in/yaml.v3        v3.0.0-20210107192922-496545a6307b      h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
        dep     k8s.io/apimachinery     v0.20.2
        dep     sigs.k8s.io/yaml        v1.2.0

查看 KIND 代码仓库中的 go.mod文件,都包含在内了。

其实 Linux 系统中二进制文件包含额外的信息并非 Go 所特有的,下面我将具体介绍其内部原理和实现。当然,用 Go 构建的二进制文件仍是本文的主角。

Linux ELF 格式

ELF 是 Executable and Linkable Format 的缩写,是一种用于可执行文件、目标文件、共享库和核心转储(core dump)的标准文件格式。ELF 文件 通常 是编译器之类的输出,并且是二进制格式。以 Go 编译出的可执行文件为例,我们使用 file 命令即可看到其具体类型 ELF 64-bit LSB executable

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ file ./bin/kind 
./bin/kind: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

本文中我们来具体看看 64 位可执行文件使用的 ELF 文件格式的结构和 Linux 内核源码中对它的定义。

使用 ELF 文件格式的可执行文件是由 ELF 头(ELF Header) 开始,后跟 程序头(Program Header) 或 节头(Section Header) 或两者均有组成的。

ELF 头

ELF 头始终位于文件的零偏移(zero offset)处(即:起点位置),同时在 ELF 头中还定义了程序头和节头的偏移量。

我们可以通过 readelf 命令查看可执行文件的 ELF 头,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ readelf -h ./bin/kind 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x46c460
  Start of program headers:          64 (bytes into file)
  Start of section headers:          400 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         6
  Size of section headers:           64 (bytes)
  Number of section headers:         15
  Section header string table index: 3

从上面的输出我们可以看到,ELF 头是以某个 Magic 开始的,此 Magic 标识了有关文件的信息,即:前四个 16 进制数,表示这是一个 ELF 文件。具体来说,将它们换算成其对应的 ASCII 码即可:

45 = E

4c = L

46 = F

7f 是其前缀,当然,也可以直接在 Linux 内核源码[2]中拿到此处的具体定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L340-L343
#define ELFMAG0  0x7f  /* EI_MAG */
#define ELFMAG1  'E'
#define ELFMAG2  'L'
#define ELFMAG3  'F'

接下来的数 02 是与 Class 字段相对应的,表示其体系结构,它可以是 32 位(=01) 或是 64 位(=02)的,此处显示 02 表示是 64 位的,再有 readelf 将其转换为 ELF64 进行展示。这里的取值同样可以在 Linux 内核源码[3]中找到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L347-L349
#define ELFCLASSNONE 0  /* EI_CLASS */
#define ELFCLASS32 1
#define ELFCLASS64 2

再后面的两个 01 01 则是与 Data 字段和 Version 字段相对应的,Data 有两个取值分别是 LSB(01)和 MSB(02),这里倒没什么必要展开。另外就是 Version 当前只有一个取值,即 01 。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L352-L358
#define ELFDATANONE 0  /* e_ident[EI_DATA] */
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2

#define EV_NONE  0  /* e_version, EI_VERSION */
#define EV_CURRENT 1
#define EV_NUM  2

接下来需要注意的就是我前面提到的关于偏移量的内容,即输出中的以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  Start of program headers:          64 (bytes into file)
  Start of section headers:          400 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         6
  Size of section headers:           64 (bytes)
  Number of section headers:         15

ELF 头总是在起点,在此例中接下来是程序头(Program Header),随后是节头(Section Header),这里的输出显示程序头是从 64 开始的,所以节头的位置就是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
64 + 56 * 6 = 400

与上述输出符合,同理,节头的结束位置是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
400 + 15 * 64 = 1360

下一节内容中将用到这部分知识。

程序头

通过 readelf -l 可以看到其程序头,包含了若干段(Segment),内核看到这些段时,将调用 mmap syscall 来使用它们映射到虚拟地址空间。这部分不是本文的重点,我们暂且跳过有个印象即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ readelf -l ./bin/kind 

Elf file type is EXEC (Executable file)
Entry point 0x46c460
There are 6 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000150 0x0000000000000150  R      0x1000
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000333a75 0x0000000000333a75  R E    0x1000
  LOAD           0x0000000000334000 0x0000000000734000 0x0000000000734000
                 0x00000000002b3be8 0x00000000002b3be8  R      0x1000
  LOAD           0x00000000005e8000 0x00000000009e8000 0x00000000009e8000
                 0x0000000000020ac0 0x00000000000552d0  RW     0x1000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x8
  LOOS+0x5041580 0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000         0x8

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .text 
   02     .rodata .typelink .itablink .gosymtab .gopclntab 
   03     .go.buildinfo .noptrdata .data .bss .noptrbss 
   04     
   05     

节头

使用 readelf -S 即可查看其节头,其结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L317-L328
typedef struct elf64_shdr {
  Elf64_Word sh_name;  /* Section name, index in string tbl */
  Elf64_Word sh_type;  /* Type of section */
  Elf64_Xword sh_flags;  /* Miscellaneous section attributes */
  Elf64_Addr sh_addr;  /* Section virtual addr at execution */
  Elf64_Off sh_offset;  /* Section file offset */
  Elf64_Xword sh_size;  /* Size of section in bytes */
  Elf64_Word sh_link;  /* Index of another section */
  Elf64_Word sh_info;  /* Additional section information */
  Elf64_Xword sh_addralign; /* Section alignment */
  Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;

对照实际的命令输出,含义就很明显了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ readelf -S ./bin/kind 
There are 15 section headers, starting at offset 0x190:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000401000  00001000
       0000000000332a75  0000000000000000  AX       0     0     32
  [ 2] .rodata           PROGBITS         0000000000734000  00334000
       000000000011f157  0000000000000000   A       0     0     32
  [ 3] .shstrtab         STRTAB           0000000000000000  00453160
       00000000000000a4  0000000000000000           0     0     1
  [ 4] .typelink         PROGBITS         0000000000853220  00453220
       00000000000022a0  0000000000000000   A       0     0     32
  [ 5] .itablink         PROGBITS         00000000008554c0  004554c0
       0000000000000978  0000000000000000   A       0     0     32
  [ 6] .gosymtab         PROGBITS         0000000000855e38  00455e38
       0000000000000000  0000000000000000   A       0     0     1
  [ 7] .gopclntab        PROGBITS         0000000000855e40  00455e40
       0000000000191da8  0000000000000000   A       0     0     32
  [ 8] .go.buildinfo     PROGBITS         00000000009e8000  005e8000
       0000000000000020  0000000000000000  WA       0     0     16
  [ 9] .noptrdata        PROGBITS         00000000009e8020  005e8020
       0000000000017240  0000000000000000  WA       0     0     32
  [10] .data             PROGBITS         00000000009ff260  005ff260
       0000000000009850  0000000000000000  WA       0     0     32
  [11] .bss              NOBITS           0000000000a08ac0  00608ac0
       000000000002f170  0000000000000000  WA       0     0     32
  [12] .noptrbss         NOBITS           0000000000a37c40  00637c40
       0000000000005690  0000000000000000  WA       0     0     32
  [13] .symtab           SYMTAB           0000000000000000  00609000
       0000000000030a20  0000000000000018          14   208     8
  [14] .strtab           STRTAB           0000000000000000  00639a20
       000000000004178d  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

Go 二进制文件探秘

本文中,我们重点关注名为 .go.buildinfo 的部分。 使用 objdump 查看其具体内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s -j .go.buildinfo ./bin/kind

./bin/kind:     file format elf64-x86-64

Contents of section .go.buildinfo:
 9e8000 ff20476f 20627569 6c64696e 663a0800  . Go buildinf:..
 9e8010 a0fc9f00 00000000 e0fc9f00 00000000  ................

这里我们按顺序来,先看到第一行的 16 个字节。

  • 前 14 个字节是魔术字节,必须为 \xff Go buildinf:
  • 第 15 字节表示其指针大小,这里的值为 0x08,表示 8 个字节;
  • 第 16 字节用于判断字节序是大端模式还是小端模式,非 0 为大端模式,0 为小端模式。

我们继续看第 17 字节开始的内容。

Go 版本信息

前面我们也看到了当前使用的字节序是小端模式,这里的地址应该是 0x009ffca0

我们来取出 16 字节的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address 0x009ffca0 --stop-address 0x009ffcb0 ./bin/kind   

./bin/kind:     file format elf64-x86-64

Contents of section .data:
 9ffca0 f5027d00 00000000 06000000 00000000  ..}.............

这里前面的 8 个字节是 Go 版本的信息,后 8 个字节是版本所占的大小(这里表示占 6 个字节)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address  0x007d02f5 --stop-address 0x007d02fb ./bin/kind

./bin/kind:     file format elf64-x86-64

Contents of section .rodata:
 7d02f5 676f31 2e3136                        go1.16

所以,如上所示,我们拿到了构建此二进制文件所用的 Go 版本的信息,是用 Go 1.16 进行构建的。

Go Module 信息

前面我们使用了 17~24 字节的信息,这次我们继续往后使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address  0x009ffce0 --stop-address 0x009ffcf0 ./bin/kind       

./bin/kind:     file format elf64-x86-64

Contents of section .data:
 9ffce0 5a567e00 00000000 e6020000 00000000  ZV~.............

与前面获取 Go 版本信息时相同,前 8 个字节是指针,后 8 个字节是其大小。也就是说从 0x007e565a 开始,大小为 0x000002e6 ,所以我们可以拿到以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address  0x007e565a --stop-address 0x7e5940 ./bin/kind

./bin/kind:     file format elf64-x86-64

Contents of section .rodata:
 7e565a 3077 af0c9274 080241e1 c107e6d6 18e6 0w...t..A.......
 7e566a 7061 74680973 6967732e 6b38732e 696f path.sigs.k8s.io
 7e567a 2f6b 696e640a 6d6f6409 73696773 2e6b /kind.mod.sigs.k
 7e568a 3873 2e696f2f 6b696e64 09286465 7665 8s.io/kind.(deve
 7e569a 6c29 090a6465 70096769 74687562 2e63 l)..dep.github.c
 7e56aa 6f6d 2f427572 6e745375 7368692f 746f om/BurntSushi/to
 7e56ba 6d6c 0976302e 332e3109 0a646570 0967 ml.v0.3.1..dep.g
 7e56ca 6974 6875622e 636f6d2f 616c6573 7369 ithub.com/alessi
 7e56da 6f2f 7368656c 6c657363 61706509 7631 o/shellescape.v1
 7e56ea 2e34 2e31090a 64657009 67697468 7562 .4.1..dep.github
 7e56fa 2e63 6f6d2f65 76616e70 68782f6a 736f .com/evanphx/jso
 7e570a 6e2d 70617463 682f7635 0976352e 322e n-patch/v5.v5.2.
 7e571a 3009 0a646570 09676974 6875622e 636f 0..dep.github.co
 7e572a 6d2f 6d617474 6e2f676f 2d697361 7474 m/mattn/go-isatt
 7e573a 7909 76302e30 2e313209 0a646570 0967 y.v0.0.12..dep.g
 7e574a 6974 6875622e 636f6d2f 70656c6c 6574 ithub.com/pellet
 7e575a 6965 722f676f 2d746f6d 6c097631 2e38 ier/go-toml.v1.8
 7e576a 2e31 0968313a 314e6638 336f7270 726b .1.h1:1Nf83orprk
 7e577a 4a79 6b6e5436 68377a62 75454755 456a JyknT6h7zbuEGUEj
 7e578a 6379 566c4378 53554754 454e6d4e 4352 cyVlCxSUGTENmNCR
 7e579a 4d3d 0a646570 09676974 6875622e 636f M=.dep.github.co
 7e57aa 6d2f 706b672f 6572726f 72730976 302e m/pkg/errors.v0.
 7e57ba 392e 31090a64 65700967 69746875 622e 9.1..dep.github.
 7e57ca 636f 6d2f7370 6631332f 636f6272 6109 com/spf13/cobra.
 7e57da 7631 2e312e31 090a6465 70096769 7468 v1.1.1..dep.gith
 7e57ea 7562 2e636f6d 2f737066 31332f70 666c ub.com/spf13/pfl
 7e57fa 6167 0976312e 302e3509 0a646570 0967 ag.v1.0.5..dep.g
 7e580a 6f6c 616e672e 6f72672f 782f7379 7309 olang.org/x/sys.
 7e581a 7630 2e302e30 2d323032 31303132 3431 v0.0.0-202101241
 7e582a 3534 3534382d 32326461 36326531 3263 54548-22da62e12c
 7e583a 3063 0968313a 56777967 55726e77 396a 0c.h1:VwygUrnw9j
 7e584a 6e38 38633475 38474433 725a5162 7172 n88c4u8GD3rZQbqr
 7e585a 502f 74676173 38387450 55624278 5172 P/tgas88tPUbBxQr
 7e586a 6b3d 0a646570 09676f70 6b672e69 6e2f k=.dep.gopkg.in/
 7e587a 7961 6d6c2e76 32097632 2e322e38 090a yaml.v2.v2.2.8..
 7e588a 6465 7009676f 706b672e 696e2f79 616d dep.gopkg.in/yam
 7e589a 6c2e 76330976 332e302e 302d3230 3231 l.v3.v3.0.0-2021
 7e58aa 3031 30373139 32393232 2d343936 3534 0107192922-49654
 7e58ba 3561 36333037 62096831 3a683871 446f 5a6307b.h1:h8qDo
 7e58ca 7461 4550754a 4154724d 6d573034 4e43 taEPuJATrMmW04NC
 7e58da 7767 37763232 61484832 38777770 6175 wg7v22aHH28wwpau
 7e58ea 5568 4b394f6f 3d0a6465 70096b38 732e UhK9Oo=.dep.k8s.
 7e58fa 696f 2f617069 6d616368 696e6572 7909 io/apimachinery.
 7e590a 7630 2e32302e 32090a64 65700973 6967 v0.20.2..dep.sig
 7e591a 732e 6b38732e 696f2f79 616d6c09 7631 s.k8s.io/yaml.v1
 7e592a 2e32 2e30090a f9324331 86182072 0082 .2.0...2C1.. r..
 7e593a 4210 4116d8f2                        B.A...          

我们成功的拿到了其所依赖的 Modules 相关的信息,这与我们在文章开头执行 go version -m ./bin/kind 是可以匹配上的,只不过这里的内容相当于是做了序列化。

具体实现

在前面的内容中,关于如何使用 readelf 和 objdump 命令获取二进制文件的的 Go 版本和 Module 信息就已经涉及到了其具体的原理。这里我来介绍下 Go 代码的实现。

节头的名称是硬编码在代码中的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//src/cmd/go/internal/version/exe.go#L106-L110
 for _, s := range x.f.Sections {
  if s.Name == ".go.buildinfo" {
   return s.Addr
  }
 }

同时,魔术字节也是通过如下定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var buildInfoMagic = []byte("\xff Go buildinf:")

获取 Version 和 Module 相关信息的逻辑如下,在前面的内容中也已经基本介绍过了,这里需要注意的也就是字节序相关的部分了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 ptrSize := int(data[14])
 bigEndian := data[15] != 0
 var bo binary.ByteOrder
 if bigEndian {
  bo = binary.BigEndian
 } else {
  bo = binary.LittleEndian
 }
 var readPtr func([]byte) uint64
 if ptrSize == 4 {
  readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
 } else {
  readPtr = bo.Uint64
 }
 vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
 if vers == "" {
  return
 }
 mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
 if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
  // Strip module framing.
  mod = mod[16 : len(mod)-16]
 } else {
  mod = ""
 }

总结

我在这篇文章中分享了如何从 Go 的二进制文件中获取构建它时所用的 Go 版本及它依赖的模块信息。如果对原理不感兴趣的话,直接通过 go version -m 二进制文件 即可获取相关的信息。

具体实现还是依赖于 ELF 文件格式中的相关信息,同时也介绍了 readelf 和 objdump 工具的基本使用,ELF 格式除了本文介绍的这种场景外,还有很多有趣的场景可用,比如为了安全进行逆向之类的。

另外,你可能会好奇从 Go 的二进制文件获取这些信息有什么作用。最直接的来说,可以用于安全漏洞扫描,比如检查其依赖项是否有安全漏洞;或是可以对依赖进行分析(主要指:接触不到源代码的场景下)会比较有用。


参考资料

[1]KIND 项目地址: https://github.com/kubernetes-sigs/kind

[2]ELF Magic 内核源码: https://github.com/torvalds/linux/blob/2c85ebc57b3e1817b6ce1a6b703928e113a90442/include/uapi/linux/elf.h#L340-L343

[3]ELF Class 内核定义: https://github.com/torvalds/linux/blob/2c85ebc57b3e1817b6ce1a6b703928e113a90442/include/uapi/linux/elf.h#L347-L350

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MoeLove 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
数据分析师薪资有多高?爬了29个城市的数据告诉你答案
想要从事数据分析师这个岗位,那自然首先需要对这个岗位有所了解。最直接、最真实的方式就是从企业那里获得需求讯息,这样才最能够指导自己的学习方向和简历准备。本文即是要利用爬虫爬取拉勾网上数据分析这一岗位的信息,然后进行一些探索和分析,以数据分析来了解‘数据分析’。 数据来源 本项目所使用的数据集全部来自拉勾网,是通过集搜客这一网络爬虫工具来爬取的。之所以选择拉勾网作为本项目的数据源,主要是因为相对于其他招聘网站,拉钩网上的岗位信息非常完整、整洁,极少存在信息的缺漏。并且几乎所有展现出来的信息都是非常规范化的,极
钱塘数据
2018/03/06
3K0
数据分析师薪资有多高?爬了29个城市的数据告诉你答案
数据分析师最不能错过的数据是什么?
作为一名数据分析师最不能错过的数据是什么?当然是和每一位数据分析师息息相关的,决定大家是吃土还是吃面包的招聘数据。
1480
2019/05/21
6310
数据分析师还是算法工程师|用数据多角度解读如何选择
疫情即将散去,又到了求职季。学习Python的各位该如何选择自己的职业方向,算法工程师还是数据分析师?跟随本文一起看看吧!
刘早起
2020/04/22
1.3K0
数据分析师职业发展白皮书(2015版)第一——三部分
数据分析师职业发展白皮书(2015版) 目 录 一、是技术也是艺术——CDA研究院和业界前沿公司和对数据分析的认识 二、数据分析师职业发展历程 1.国外数据分析行业发展历程 2.国内数据分析师职业发展 三、数据分析师人才行业现状 1.人才模型/岗位划分 2.国内数据分析师人才薪资水平 3.国内数据分析师人才分布 4.国内数据分析师人才需求 四、数据分析师人才职业规划 1.目前人才职业现状 2.数据分析人才学习路径 3.总结 五、CDA数据分析师培训及认证 1.考试简介 2.为什么选择CDA
小莹莹
2018/04/20
1.4K0
数据分析师职业发展白皮书(2015版)第一——三部分
如何分析“数据分析师”的岗位?
在《手把手带你抓取智联招聘的“数据分析师”岗位!》一期中我们分享了如何抓取智联招聘中“数据分析师”岗位的数据信息(数据截止到2018年11月4日),在本期我们将基于已有的数据对其作进一步的分析和探索。在探索过程中,我们将围绕如下几个主题进行问题的回答:
1480
2019/08/05
6490
如何分析“数据分析师”的岗位?
数据导入与预处理-第8章-实战演练-数据分析师岗位分析
本案例数据为招聘网站上收集的有关数据分析师岗位的数据,对该数据集从数据分析的角度出发,利用pandas、pyecharts库处理与展现数据,开发一个完整的数据分析项目。
IT从业者张某某
2022/11/12
1.2K0
数据导入与预处理-第8章-实战演练-数据分析师岗位分析
用数据分析告诉你数据分析师能挣多少钱
随着大数据时代的到来和数据的市场价值得到认可,数据分析师、进阶一点的还有数据挖掘工程师、甚至是金字塔顶尖的数据科学家,这些作为21世纪最性感的职业已成功吸引无数像笔者这样的热血小青年,阿里的一句“开启AI时代”的口号就足以让我等激动的准备把此身奉献给高大上的数据科学行业。除去像计算机、数学和统计学这些科班出身的童鞋,想要转行投身数据分析的其他行业人士也绝不在少数。但数据分析到底是什么、想要成为一名数据行业的从业者又要具备哪些素质,恐怕这才是大家真正需要关注的焦点。笔者花了一些时间,从数据采集到清洗、分析,从可视化到数据的深度挖掘,一整套数据分析处理流程给大家展示一下目前国内关于数据行业的招聘信息到底有些什么。
IT阅读排行榜
2018/08/16
5960
用数据分析告诉你数据分析师能挣多少钱
听说数据分析师挺火,我们来数据分析一下
经常看见各种数据分析师培训的运营推荐,那么数据分析师的就业行情究竟如何?让我们用数据说话,一探究竟!
luanhz
2020/04/01
5980
数据分析行业招聘职位分析报告--基于拉勾网
大数据时代的到来让数据在公司决策上发挥了越来越大的作用,数据分析师也成为了各大企业的标配,那么各大企业又会愿意花多少代价来为数据买单呢?本文将通过从拉勾网爬取到的职位信息来展现「数据分析」职位究竟「钱」景如何:
Awesome_Tang
2018/09/11
1.5K0
数据分析行业招聘职位分析报告--基于拉勾网
只会 Python,如何拿下科技企业的高薪 Offer?
大数据因为其背后蕴含的价值,被《经济学杂志》在2017年誉为“新的石油”,数据导向的工作也成为很多人的向往之一,特别是数据分析。
Python中文社区
2018/07/26
5180
只会 Python,如何拿下科技企业的高薪 Offer?
重磅发布!2020上半年数据分析人才及CDA持证人行业报告
2020年5月6日,人力资源和社会保障部发布《新职业—大数据工程技术人员就业景气现状分析报告》,报告显示:预计2020年中国大数据行业人才需求规模将达210万,2025年前大数据人才需求仍将保持30%—40%的增速,需求总量在2000万人左右,数据分析人才是市场上迫切需要的高端型人才。
CDA数据分析师
2020/07/28
1.2K0
重磅发布!2020上半年数据分析人才及CDA持证人行业报告
不会数据分析技能的你,正在失去竞争力
大数据因为其背后蕴含的价值,被《经济学杂志》在2017年誉为“新的石油”,数据导向的工作也成为很多人的向往之一,特别是数据分析。
Python中文社区
2018/09/21
5080
不会数据分析技能的你,正在失去竞争力
不会数据分析技能的你,正在失去竞争力
大数据因为其背后蕴含的价值,被《经济学杂志》在2017年誉为“新的石油”,数据导向的工作也成为很多人的向往之一,特别是数据分析。
Crossin先生
2018/10/22
4230
不会数据分析技能的你,正在失去竞争力
Day01-数据分析图鉴
最近流传一句话,不会数据分析的程序员,不是好程序员。 其实,不仅仅程序员,无论你未来准备从事什么职业:产品、运营、销售、HR、财务、金融、电商,还是做研发、系统架构,你都会发现,在数不清的岗位需求中,公司对数据分析的能力要求越来越普遍! 有人说,毕业生学数据分析很占优势,因为学得快 有人说,毕业生没有工作经验是优势,可以直接上岗… 有人说,数据分析行业前景好,薪资高,是工作的好选择… 有人说,学数据分析永不过时… 但!数据分析到底是什么?离我们远吗 恰恰相反,数据正在变得越来越常见,小到我们每个人的社交网络、消费信息、运动轨迹,大到企业的销售、运营数据,产品的生产数据,再看看我们每天在做的事情,上知乎、上微博、逛淘宝,上Google,所有的地方都是高度需要数据分析 数据分析当然重要,一般单位还是用excel表格在统计数据 而数据分析,就是就是将数据的价值最大化 借助数据来做决策,而不是盲目地拍脑袋
IT从业者张某某
2023/10/16
2460
Day01-数据分析图鉴
被BAT疯抢的数据分析师,是怎么精进技术的?
自从做公众号以来,一直都有学生问我 现在数据分析那么火热,现在入行迟吗? 会不会刚等我都出来了,行业对数据分析师的需求也接近饱和了? 我的答案是: 不会。 “数据分析的人才需求每年都在增长,而每年的高
用户1621951
2018/04/19
8420
被BAT疯抢的数据分析师,是怎么精进技术的?
从拉勾网数据看深圳数据分析师的职业前景
利用这篇文章提到的方法对拉勾爬虫,然后进行数据分析。通过对局部地区某一岗位的总体分析,找出该地区该职位的职业发展前景规律。本文基于拉勾上2016年12月到2017年1月深圳地区数据分析师招聘数据,为这
小莹莹
2018/04/24
9950
从拉勾网数据看深圳数据分析师的职业前景
【求职】做数据分析师也有三重境界
最近,“大数据”成了媒体解读两会的“利器”。活泼的数据图表一出现,枯燥的政府报告、政策解读都变得有趣和易懂了。   将大数据当作金矿的话,那数据分析师就是掘金人——作为这一新兴产业的弄潮人,他们在人才市场上也是独领风骚哦!普通数据专员月薪3000元以上,高级数据专员年薪可达40万元到50万元。   而最重要的是,通过数据参与企业管理和市场营销,数据分析师成长为企业高层也充满各种可能。   数据分析员究竟工作内容是什么?他们如何工作?进入这行要具备哪些职业素养?本期行当版为你一一解答。   高级数据分析师不好
小莹莹
2018/04/18
1.1K0
Python+BI分析5000条招聘数据,原来数据分析工作在这儿最好找
这两年的大数据热潮带火了数据分析这个职业,很多人想转行干数据分析,但是又不知道现在这个行业的求职环境和前景如何,动了心却不敢贸然行动。
数据分析的那些事儿
2020/02/20
6810
“数据分析”岗位的数据分析项目
“数据分析”岗位的分析 项目介绍 该项目选用了和鲸社区关于数据分析岗位的数据集来进行分析。项目主要使用“job.csv”文件作为数据源,其中数据文件的主要栏位有职位、城市、公司、薪资、学历、工作经验、行业标签。其中本项目所使用的可计算的栏位为最低薪资、最高薪资、平均薪资、奖金率。本项目所使用的可分类的栏位为职位、城市、学历、工作经验、行业标签。通过对数据进行清洗重塑和分析,再使用plotly等工具进行绘图,实现图表的交互式数据可视化,最后使用flask框架(利用了bootstrap)进行网页上的可视化展示
润森
2022/12/20
1K0
“数据分析”岗位的数据分析项目
BAT的数据分析师,是如何精进技术的?
最近常听到的一个观点是,未来十年内 AI 可能会取代 50% 的工作岗位,但早 AI 一步取代你的,可能是邻桌懂数据分析的同事。很多人掌握基本的 Excel,但你真的懂数据么?
挖数
2019/09/17
5800
BAT的数据分析师,是如何精进技术的?
推荐阅读
相关推荐
数据分析师薪资有多高?爬了29个城市的数据告诉你答案
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验