从ELF文件头到机器码:手把手带你用objdump解剖Linux可执行文件

news2026/5/21 7:33:58
从ELF文件头到机器码手把手带你用objdump解剖Linux可执行文件在计算机的世界里每个可执行程序都像一本精心编写的书而ELFExecutable and Linkable Format就是这本书的标准格式。当我们编译一个简单的Hello World程序时编译器会将我们的源代码转换成这种格式包含了程序运行所需的所有信息。但你知道吗通过objdump这个强大的工具我们可以像法医解剖一样一层层揭开可执行文件的神秘面纱从文件头到节头再到实际的机器指令完整地理解一个程序在磁盘和内存中的真实形态。对于中高级开发者和计算机专业学生来说理解ELF格式和反汇编技术不仅是满足好奇心更是深入系统底层原理的必经之路。它能帮助你在调试时更准确地定位问题在性能优化时更高效地分析瓶颈在安全领域更深入地理解漏洞原理。接下来我们将从最基础的ELF结构开始逐步深入到反汇编层面用实际的例子展示如何用objdump工具进行二进制分析。1. ELF文件基础与objdump工具准备ELF文件是Linux系统中可执行文件、目标文件和共享库的标准格式。它就像是一个容器包含了程序运行所需的所有信息代码、数据、符号表、重定位信息等。理解ELF结构是进行二进制分析的第一步。1.1 ELF文件的基本结构ELF文件由以下几部分组成ELF头(ELF Header)位于文件开头描述了整个文件的组织结构节头表(Section Header Table)描述了文件中各个节(section)的信息程序头表(Program Header Table)描述了段(segment)信息用于程序加载节(Sections)包含实际的代码、数据等信息段(Segments)运行时加载的单位通常由一个或多个节组成我们可以用以下命令查看一个简单C程序编译后的ELF文件基本信息# 编译一个简单的C程序 echo #include stdio.h int main() { printf(Hello, World!\n); return 0; } hello.c gcc -o hello hello.c # 查看ELF文件头信息 objdump -f hello输出示例hello: 文件格式 elf64-x86-64 体系结构i386:x86-64标志 0x00000150 HAS_SYMS, DYNAMIC, D_PAGED 起始地址 0x00000000004010401.2 objdump工具简介objdump是GNU binutils工具集中的一个强大工具主要用于显示目标文件的信息。它的主要功能包括显示文件头信息显示节头信息反汇编代码段显示符号表显示重定位信息显示调试信息在Ubuntu/Debian系统上可以通过以下命令安装binutilssudo apt-get install binutils2. 从文件头到节头解析ELF结构2.1 分析ELF文件头ELF文件头包含了描述整个文件的关键信息。使用objdump的-f选项可以查看文件头摘要objdump -f hello更详细的信息可以使用readelf工具查看readelf -h hello典型的输出包含以下重要字段字段描述MagicELF魔数标识这是一个ELF文件Class文件类(32位/64位)Data字节序(小端/大端)Type文件类型(可执行/共享库/目标文件)Machine目标机器架构Entry point address程序入口点地址Start of program headers程序头表在文件中的偏移Start of section headers节头表在文件中的偏移2.2 查看节头信息节头表描述了文件中各个节的信息。使用objdump的-h选项可以查看objdump -h hello输出示例部分hello: 文件格式 elf64-x86-64 节 Idx Name Size VMA LMA File off Algn 0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .gnu.hash 0000001c 0000000000400298 0000000000400298 00000298 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .dynsym 00000060 00000000004002b8 00000000004002b8 000002b8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .dynstr 0000003f 0000000000400318 0000000000400318 00000318 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .gnu.version 00000008 0000000000400358 0000000000400358 00000358 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA 7 .gnu.version_r 00000020 0000000000400360 0000000000400360 00000360 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .rela.dyn 00000018 0000000000400380 0000000000400380 00000380 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 9 .rela.plt 00000030 0000000000400398 0000000000400398 00000398 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .init 0000001a 00000000004003c8 00000000004003c8 000003c8 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 11 .plt 00000030 00000000004003f0 00000000004003f0 000003f0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 12 .text 00000192 0000000000400420 0000000000400420 00000420 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .fini 00000009 00000000004005b4 00000000004005b4 000005b4 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 14 .rodata 00000011 00000000004005c0 00000000004005c0 000005c0 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 15 .eh_frame_hdr 00000034 00000000004005d4 00000000004005d4 000005d4 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 16 .eh_frame 000000f4 0000000000400608 0000000000400608 00000608 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 17 .init_array 00000008 0000000000400e10 0000000000400e10 00000e10 2**3 CONTENTS, ALLOC, LOAD, DATA 18 .fini_array 00000008 0000000000400e18 0000000000400e18 00000e18 2**3 CONTENTS, ALLOC, LOAD, DATA 19 .dynamic 000001d0 0000000000400e20 0000000000400e20 00000e20 2**3 CONTENTS, ALLOC, LOAD, DATA 20 .got 00000008 0000000000400ff0 0000000000400ff0 00000ff0 2**3 CONTENTS, ALLOC, LOAD, DATA 21 .got.plt 00000028 0000000000400ff8 0000000000400ff8 00000ff8 2**3 CONTENTS, ALLOC, LOAD, DATA 22 .data 00000010 0000000000401020 0000000000401020 00001020 2**3 CONTENTS, ALLOC, LOAD, DATA 23 .bss 00000008 0000000000401030 0000000000401030 00001030 2**0 ALLOC 24 .comment 0000002a 0000000000000000 0000000000000000 00001030 2**0 CONTENTS, READONLY2.3 查看特定节的内容使用-s选项可以查看特定节的内容。例如查看.rodata节通常包含只读数据objdump -s -j .rodata hello输出示例hello: 文件格式 elf64-x86-64 Contents of section .rodata: 4005c0 01000200 48656c6c 6f2c2057 6f726c64 ....Hello, World 4005d0 2100 !.可以看到我们的Hello, World!字符串确实存储在这个节中。3. 深入反汇编从机器码到汇编指令3.1 基本反汇编使用-d选项可以对代码节进行反汇编objdump -d hello输出会显示.text节中的所有函数包括我们的main函数。典型的main函数反汇编结果如下0000000000400526 main: 400526: 55 push %rbp 400527: 48 89 e5 mov %rsp,%rbp 40052a: 48 83 ec 10 sub $0x10,%rsp 40052e: bf c0 05 40 00 mov $0x4005c0,%edi 400533: e8 d8 fe ff ff callq 400410 putsplt 400538: b8 00 00 00 00 mov $0x0,%eax 40053d: c9 leaveq 40053e: c3 retq 40053f: 90 nop3.2 带源代码的反汇编如果程序是用-g选项编译的包含调试信息可以使用-S选项将源代码与汇编代码混合显示gcc -g -o hello hello.c objdump -S hello输出示例0000000000400526 main: #include stdio.h int main() { 400526: 55 push %rbp 400527: 48 89 e5 mov %rsp,%rbp 40052a: 48 83 ec 10 sub $0x10,%rsp printf(Hello, World!\n); 40052e: bf c0 05 40 00 mov $0x4005c0,%edi 400533: e8 d8 fe ff ff callq 400410 putsplt return 0; 400538: b8 00 00 00 00 mov $0x0,%eax } 40053d: c9 leaveq 40053e: c3 retq 40053f: 90 nop3.3 理解反汇编输出让我们逐行分析main函数的反汇编输出push %rbp保存旧的基址指针mov %rsp,%rbp设置新的基址指针sub $0x10,%rsp在栈上分配16字节空间mov $0x4005c0,%edi将字符串地址(0x4005c0)放入edi寄存器callq 400410 putsplt调用puts函数mov $0x0,%eax将返回值0放入eax寄存器leaveq恢复栈指针retq从函数返回注意编译器优化了printf调用为puts因为我们的字符串以换行符结尾且没有格式参数。4. 高级分析与实战技巧4.1 动态符号表分析动态链接的可执行文件会使用动态符号表来解析外部函数。使用-T选项可以查看动态符号表objdump -T hello输出示例部分hello: 文件格式 elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 puts 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __gmon_start__ 0000000000400410 g DF .text 0000000000000000 Base _init 0000000000400440 g DF .text 0000000000000000 Base _start 0000000000400470 g DF .text 0000000000000000 Base deregister_tm_clones 00000000004004a0 g DF .text 0000000000000000 Base register_tm_clones 00000000004004e0 g DF .text 0000000000000000 Base __do_global_dtors_aux 0000000000400500 g DF .text 0000000000000000 Base frame_dummy 0000000000400526 g DF .text 0000000000000000 Base main 0000000000400540 g DF .text 0000000000000000 Base __libc_csu_init 00000000004005b0 g DF .text 0000000000000000 Base __libc_csu_fini 00000000004005b4 g DF .text 0000000000000000 Base _fini4.2 查看重定位信息重定位信息对于理解动态链接过程非常重要。使用-R选项可以查看objdump -R hello输出示例hello: 文件格式 elf64-x86-64 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 0000000000400ff8 R_X86_64_JUMP_SLOT putsGLIBC_2.2.5 0000000000401000 R_X86_64_JUMP_SLOT __libc_start_mainGLIBC_2.2.5 0000000000401008 R_X86_64_JUMP_SLOT __gmon_start__4.3 分析函数调用图虽然objdump本身不直接生成调用图但我们可以通过分析反汇编代码手动构建。例如查找所有callq指令objdump -d hello | grep callq输出示例400533: e8 d8 fe ff ff callq 400410 putsplt 4004e7: e8 34 ff ff ff callq 400420 deregister_tm_clones 4004f7: e8 44 ff ff ff callq 400440 register_tm_clones 40051a: e8 f1 fe ff ff callq 400410 putsplt 400540: e8 cb ff ff ff callq 400510 frame_dummy 400576: e8 95 fe ff ff callq 400410 putsplt4.4 比较不同编译选项的影响不同的编译选项会生成不同的机器码。让我们比较一下有无优化选项的区别# 无优化编译 gcc -o hello_noopt hello.c objdump -d hello_noopt noopt.dis # 使用-O2优化编译 gcc -O2 -o hello_opt hello.c objdump -d hello_opt opt.dis # 比较差异 diff -u noopt.dis opt.dis优化后的代码通常会更短小精炼使用更高效的指令消除冗余操作内联小函数4.5 调试信息分析如果程序是用-g选项编译的可以使用--dwarf选项查看DWARF调试信息objdump --dwarfinfo hello输出会包含丰富的调试信息包括编译单元信息数据类型定义变量位置描述源代码行号映射5. 实际案例分析破解简单Crackme为了更好地理解这些概念让我们分析一个简单的crackme程序一种合法的逆向工程练习程序。假设我们有如下程序// crackme.c #include stdio.h #include string.h int check_password(const char* pass) { return strcmp(pass, secret) 0; } int main(int argc, char** argv) { if (argc ! 2) { printf(Usage: %s password\n, argv[0]); return 1; } if (check_password(argv[1])) { printf(Congratulations! You cracked it!\n); } else { printf(Wrong password!\n); } return 0; }编译它gcc -o crackme crackme.c5.1 定位关键函数首先我们反汇编整个程序objdump -d crackme crackme.dis然后搜索check_password函数0000000000400646 check_password: 400646: 55 push %rbp 400647: 48 89 e5 mov %rsp,%rbp 40064a: 48 83 ec 10 sub $0x10,%rsp 40064e: 48 89 7d f8 mov %rdi,-0x8(%rbp) 400652: 48 8b 45 f8 mov -0x8(%rbp),%rax 400656: 48 8d 35 a7 00 00 00 lea 0xa7(%rip),%rsi # 400704 _IO_stdin_used0x4 40065d: 48 89 c7 mov %rax,%rdi 400660: e8 bb fe ff ff callq 400520 strcmpplt 400665: 85 c0 test %eax,%eax 400667: 0f 94 c0 sete %al 40066a: 0f b6 c0 movzbl %al,%eax 40066d: c9 leaveq 40066e: c3 retq关键点在lea 0xa7(%rip),%rsi这一行它将一个地址加载到rsi寄存器中。这个地址(0x400704)就是字符串secret的存储位置。5.2 查看字符串数据我们可以验证这一点objdump -s -j .rodata crackme输出中会显示Contents of section .rodata: 400700 01000200 73656372 65740000 436f6e67 ....secret..Cong 400710 72617475 6c617469 6f6e7321 20596f75 ratulations! You 400720 20637261 636b6564 20697421 0057726f cracked it!.Wro 400730 6e672070 61737377 6f726421 00557361 ng password!.Usa 400740 67653a20 2573203c 70617373 776f7264 ge: %s password 400750 3e00 .确实地址0x400704处存储着字符串secret。5.3 绕过密码检查理解了程序的工作原理后我们可以通过修改二进制文件或使用调试器来绕过密码检查。虽然这超出了本文的范围但它展示了反汇编和二进制分析的实际应用价值。6. 扩展工具与技术虽然objdump非常强大但在实际二进制分析工作中我们通常会结合其他工具使用6.1 readelfreadelf是专门用于分析ELF文件的工具比objdump在某些方面更专业# 查看ELF头 readelf -h hello # 查看节头表 readelf -S hello # 查看符号表 readelf -s hello # 查看动态段信息 readelf -d hello6.2 nmnm工具用于查看符号表对于分析函数和变量非常有用nm hello6.3 stringsstrings工具可以提取文件中的所有可打印字符串strings hello6.4 GDBGNU调试器不仅可以用于调试还可以用于二进制分析gdb hello (gdb) disassemble main (gdb) x/s 0x4005c0 # 查看地址处的字符串6.5 二进制分析框架对于更复杂的分析可以考虑使用专门的二进制分析框架radare2开源逆向工程框架GhidraNSA开发的逆向工程工具IDA Pro商业逆向工程软件7. 安全注意事项与最佳实践在进行二进制分析时需要注意以下安全事项合法性只分析你有权限分析的程序不要逆向专有软件除非你有明确的授权隔离环境在虚拟机或专用环境中分析未知二进制文件版本控制对分析的二进制文件进行哈希校验确保分析的一致性文档记录详细记录分析过程和发现便于后续参考工具验证确保使用的分析工具来自可信来源提示对于生产环境的关键二进制文件建议保留调试符号和编译选项记录这将大大简化后续的调试和分析工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2542888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…