链接,加载和剥离二进制:PLT,GOT,进口,修复和函数识别
Type: Build
Languages: Python, C, x86-64 and arm64 assembly
Prerequisites: Phase 13 · 32 (ELF from scratch), 33 (disassembly and control flow), 38 (calling conventions)
Time: ~160 minutes
学习目标
- 根据转移记录和符号表,确定链接编辑器将它用于什么:固定距离,一个带有GOT插槽的PLT输入或动态转移.
- 阅读位置独立的ELF执行器的动态部分,PLT和GOT,并使用psABI公式通过它们进行一次通话.
- 阅读COFF对象 (20字节文件标题,40字节节目录,18字节符号,10字节移动) 和PE32+图像的进口,出口和基部移动目录.
- 单词复制链接,并对应每一个词.
dyld_info现在,我们要去. - 说明什么
strip运行删除的程序,证明它仍然有效. - 恢复一个脱的 arm64 构建的功能启动
LC_FUNCTION_STARTS按前标扫描和呼叫目标,对每种方法进行分数,以对未删减双胞胎的符号表进行分数,并用FLIRT形状的签名命名函数.
问题
你联系了main.c反对libledger.dylib问一个代理人,哪些功能.main电话0x1000005a0经理拆除了指令,并读到bl 0x100000694这个地址包含12个字节:adrp x16现在ldr x16, [x16, #0x38]现在br x16经理跟踪了货物到0x100004038读取8个字节00 00 00 00 00 00 10 80通过一个腐败的指针进入内核空间.
然后你剥夺了同一程序的静态构建,问它有多少功能.nm看到一个名字,__mh_execute_header答了一个问题.
两个答案都读到文件,就像是运行过程.0x100004038字符第63节说绑定,12位说下一个绑定是哪里,低位说哪个进口绑定._puts在之前main删除的文件失去了符号表,并保留了LC_FUNCTION_STARTS列出了所有五个函数,开始于9个字节.
这一课程建立了五个读物标准图书馆Python.一个对我们自己的对象的COFF读物和对进口,出口和基部迁移目录的PE32+读物.一个对载荷命令,片,间接符号表,链接的修复链,以及出口试验的Mach-O读物,对应单词检查.dyld_info动态部分,PLT和GOT的ELF读器,加上一个规划器,说明我们对象的PIE链接会产生什么.一个四种方法和每个方法的分数的剥离构建函数寻找器.一个FLIRT的形状的签名构建器,具有野生卡特移动字节和尾巴的CRC16.
机器上只有Mach-O链接. 机器64执行式和Dlib被连接,脱掉,运行.clang -target由于没有安装ELF或PE链接编辑器,因此这些格式中的两个链接的图像是从固定文件中的两个Python脚本的规格中字节字节字节写的. llvm-objdump图书馆调查也读到了.
链接编辑器在搬迁时做什么
第32课阅读迁移记录,第33课应用S + A - Plea一个记录给了一个地方命名P标志S其他A根据 x86-64 psABI表 4.9 每种类型的配方为一个,L对于符号的PLT输入位置,G对于他的恩赐.GOT对于GOT地址,B图像的负载基.三个结果覆盖我们的物体.
链接编辑器将值输入到该字段中,记录就消失了. R_X86_64_PC32对于输入中定义的符号,这是这样的情况. 图像中的两个字节之间的距离永远不会改变,无论载荷地址如何.
链接编辑器将一个PLT输入和一个GOT插槽. R_X86_64_PLT32现在,我们可以在一个未定义函数上进行攻击.L + A - P进入的距离,进入的距离跳过槽.R_X86_64_JUMP_SLOT记录在.rela.plt指示动态链接器填补空隙.
链接编辑器将转移存储为加载器. R_X86_64_64位置独立的执行式将在加载之前不知道其地址.R_X86_64_RELATIVE配方B + A在.rela.dyn现在,我们要去.
main-x86_64-O1.o拥有18个记录:14个.text两分钟.data.rel.ro两次.rodata现在,我们要去.plan_dynamic_link()在code/main.py它们应与ledger-x86_64-O1.o其他:
| Record | Count | Decision | Why |
|---|---|---|---|
R_X86_64_PC32 to .rodata, .L.str, fns, the table in .rodata | 16 | in place | the symbol is defined in the inputs |
R_X86_64_PLT32 to percent_change, grade, total, grade_label | 4 | direct, no PLT | defined in ledger-x86_64-O1.o, so the link editor reduces PLT32 to PC32 |
R_X86_64_PLT32 to puts twice and printf twice | 4 | PLT entry and GOT slot | undefined, provided by a shared library |
R_X86_64_64 to grade in fns[] | 2 | R_X86_64_RELATIVE | an absolute field in a PIE |
两种进口给出了一个.plt具有48字节 (PLT0加上16的两个输入),.got.plt 40 字节 (三个保留的插槽加上两个),.rela.plt两个相对记录显示一个.rela.dyn它们来自我们记录的规格.tiny-dyn-x86_64实施本计划的PLT部分,用于相同的两种进口,其尺寸与计划相匹配.
编译器写了PLT32在每次通话中,无论是定义或未定义,自2.31 (H.J. Lu, 2018-02-13) 和LLVM 7.0.0 (D43383, 2018-02-20) 后,链接编辑决定.PLT32在一个对象中,因此说"一个呼叫",并没有说 PLT 输入是否会存在.在其他构建中出现了两个类型. R_X86_64_GOTPCRELX其他R_X86_64_REX_GOTPCRELX(G + GOT + A - P) 要求没有PLT的 GOT插槽,-fno-plt链接编辑可以放松到直接的lea或call如果符号是本地 (psABI附件B). R_X86_64_32其他R_X86_64_32S没有任何类型发生在我们的两个物体中.
动态部门,PLT和GOT
根据GABI第5章,参与动态链接的可执行器具有一个PT_INTERP程序标题,必须先于任何可加载的段子,PT_DYNAMIC其他国家.dynamic部分是数组的Elf64_Dyn双,签名标签和值,结束于 DT_NULL现在,我们要去.
tiny-dyn-x86_64是由 编写的 5,184 字节build_tiny_dyn_elf.py它们的动态部分包含12个对.0x1000其他:
| Tag | Number | Value | Meaning |
|---|---|---|---|
DT_NEEDED | 1 | libc.so.6 | a string table offset, 0xb, the library to load |
DT_HASH | 4 | 0x1a0 | the SysV hash table |
DT_STRTAB | 5 | 0x188 | .dynstr |
DT_SYMTAB | 6 | 0x140 | .dynsym, 24 bytes per entry |
DT_STRSZ | 10 | 0x15 | 21 bytes of strings |
DT_SYMENT | 11 | 0x18 | 24 |
DT_PLTGOT | 3 | 0x10c0 | .got.plt |
DT_PLTRELSZ | 2 | 0x30 | two 24-byte records |
DT_PLTREL | 20 | 7 | the records are DT_RELA |
DT_JMPREL | 23 | 0x1b8 | .rela.plt |
DT_FLAGS_1 | 0x6ffffffb | 0x08000000 | DF_1_PIE, a GNU tag |
DT_NULL | 0 | 0 | end |
士说为什么DT_JMPREL区分这些区分输入使动态链接器在进程初始化时忽略它们,如果惰的绑定是启用的.DF_BIND_NOW在DT_FLAGS现在DF_1_NOW在DT_FLAGS_1其他LD_BIND_NOW在环境中,惰的绑定器关闭,而动态链接器在启动时处理它们.
子和子的条目
在图 5.2 中,psaBI 5.2 节目给出了两个形状.
text 1f0: ff 35 d2 0e 00 00 pushq 0xed2(%rip) # 0x10c8 = GOT+8
1f6: ff 25 d4 0e 00 00 jmpq *0xed4(%rip) # 0x10d0 = GOT+16
1fc: 0f 1f 40 00 nopl (%rax)
200: ff 25 d2 0e 00 00 jmpq *0xed2(%rip) # 0x10d8 = GOT+24, the slot for puts
206: 68 00 00 00 00 pushq $0x0 # index 0 into DT_JMPREL
20b: e9 e0 ff ff ff jmp 0x1f0 # PLT0按RIP相对移动是相对于下一个指令.0x1f6 + 0xed2 = 0x10c8现在,.got.plt + 8现在,我们要去.0x1fc + 0xed4 = 0x10d0现在,.got.plt + 16现在,我们要去.0x206 + 0xed2 = 0x10d8现在,我们要做什么?0x210 - 0x20 = 0x1f0每个输入为16字节,所以输入为exit开始于0x210推进$0x1读到0x10e0现在,我们要去.
州政府保留了三个空. psABI 5.2: 零入口包含了 _DYNAMIC总体的偏移表中的1,2个入口也保留..got.plt读书0x1000, 0, 0, 0x206, 0x216Glibc用其填充第一个插槽struct link_map两个插槽_dl_runtime_resolve随着文件的加载,三分钟的插槽可以保持.0x206在文件中,pushq在里面puts@plt按下列命令的地址.
所以第一次电话就开始了call 0x200现在jmpq *0x10d8陆地pushq $0现在jmp 0x1f0按GOT+8跳过GOT+16进入解析器,该解析器读取0的记录DT_JMPREL现在R_X86_64_JUMP_SLOT puts现在看起来puts进去libc.so.6写入了其地址0x10d8接下来,第二次电话就响了.call现在jmpq *0x10d8德雷珀写道:"除了第一次电话之外,所有通用费用都是"只"一个间接的跳跃.%r11课38中解释了一个注册表选择:它是一个"零碎注册表",因此"PLT中的代码不需要泄露任何注册表".
电话站.text读书e8 d4 ff ff ff在0x227其他0x22c - 0x2c = 0x200现在,我们要去.llvm-objdump印记callq 0x200 <puts@plt>对于它,并命名从 .rela.plt如何?plt_entries()也给它一个名字.
文件所没有的内容
文件没有PT_GNU_RELRO. 雅库布·杰林克在2004-01-06年提出了RELRO的二元件列表:仅在搬迁期间写的部分将移动到可写的部分开始,以便动态链接器可以 mprotect根据GNU id的默认脚本,.init_array现在.fini_array现在.data.rel.ro现在.dynamic其他.got只有三个保留的插槽..got.plt其他插槽是惰的. 这就是部分RELRO.-z now组件DF_BIND_NOW移动整个.got.plt没有什么在启动后写出来._dl_protect_relro电话mprotect(start, end - start, PROT_READ)在页面一致的范围. -fno-plt在编译方面,更进一步:通话通过jmpq *name@GOTPCREL(%rip)惰的结合是不可能的, 湾岸委员会手册说"所有外部符号都在装载时得到解决".
文件也没有版本部分.libc.so.6增加.gnu.version一个Elfxx_Half按.dynsym进入.gnu.version_r其他Verneed对于每一个图书馆Vernaux记录每个版本名称. printf@GLIBC_2.2.5方式: 输入printf在.gnu.version具有指数,Vernaux哪个vna_other相当于指数的vna_name根据"GLIBC_2.2.5"的标题,Verneed哪个vn_file索引0是本地,1是全球,和位15 (VERSYM_HIDDEN现在0x8000) 隐藏了链接编辑器的版本.
哈希表是一个SysVDT_HASH动态链接器需要24字节.DT_HASH或DT_GNU_HASH文件中没有任何输出,并且没有运行在这里,所以Glibc是否接受单桶表未经验证.
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
果是什么?.dynsym其他.dynstr分配部分和.symtab其他.strtab动态链接器读取第一对,而永远不会读取第二. 缩器:"正常符号表在运行时不使用,因此不需要剥离二进制.它对性能没有影响.
广州strip --strip-all删除.symtab现在.strtab现在.debug_*其他.comment每个分配的部分都保持:.interp现在.dynsym现在.dynstr现在.gnu.hash现在.gnu.version*现在.rela.dyn现在.rela.plt现在.plt现在.text现在.rodata现在.eh_frame现在.init_array现在.dynamic现在.got现在.got.plt现在.data现在.bss部分负责人留下,除非你添加--strip-section-headers(二.四.四1) 或运行llvm-strip --strip-sections现在,我们要去.--strip-debug保持这些符号.--only-keep-debug写出了该文件--add-gnu-debuglink除了除的名称,.gnu_debuglink其他ld --build-id写道.note.gnu.build-id它们可以被调试.
科夫对象
clang -target x86_64-pc-windows-msvc -O1 -fno-asynchronous-unwind-tables -c生产main-x86_64.obj其他ledger-x86_64.obj.PE格式页面定义对象:一个20字节文件标题,没有可选标题,一个40字节条目的节目表,一个18字节记录的符号表,每个节目10字节的迁移记录.
CoffObject在code/main.py文件标题说机器0x8664十个部分,37个符号记录,SizeOfOptionalHeader0. 部分表名称.text现在.data现在.bss六个部分称为.rdata其他/16一个以""开始的名字/是对字符串表的十分数偏移,它以其自己的四字节大小开始;/16解决问题.llvm_addrsig现在,这是这个名字.llvm-objdump -h他们的指纹.
部分符号 (存储类3) 包含一个包含部分长度,迁移数量,检查总和和COMDAT选项..rdata部分有选择2",任何",每个部分都包含一个字符串字母,以MSVC错误的名称: ??_C@_04JDDJEAHN@even?$AA@链接编辑器保留每一个副本.最后的记录是.file附加"main.c"的记录.llvm-objdump -t打印这些为[ 0]现在AUX scnlen ...现在[ 2]测试将我们的索引和名称对比到该文本.
搬迁时间: 14 IMAGE_REL_AMD64_REL32在.text其他IMAGE_REL_AMD64_ADDR64在.rdata现在,我们要去.REL32是"移动后的字节中的32位相对地址".R_X86_64_PC32随着-4包含:ELF记录包含附加值-4因为记录中有一个附加字段,而COFF记录没有一个. ledger-x86_64.obj只有三个REL32在0x36现在0x84其他0x93让我们知道,ledger-x86_64-O1.o只有三个.R_X86_64_PC32代码相同,格式只因写四个格式而不同.ADDR64只有一个8字节的绝对地址,labels图像中的基调转移,如R_X86_64_64成为R_X86_64_RELATIVE现在,我们要去.
ELF Elf64_Rela | COFF relocation | Mach-O relocation_info | |
|---|---|---|---|
| record size | 24 bytes | 10 bytes | 8 bytes |
| fields | offset, type and symbol in r_info, addend | offset, symbol index, type | address, then a 32-bit word with symbolnum:24, pcrel:1, length:2, extern:1, type:4 |
| addend | explicit | none, lives in the field | none, or an ARM64_RELOC_ADDEND record before the real one |
the call to puts | R_X86_64_PLT32 puts-0x4 | IMAGE_REL_AMD64_REL32 puts | ARM64_RELOC_BRANCH26 _puts |
欧洲经济体的形象:进口,出口和基地转移
tiny-x86_64.exe是由 编写的 2,560 字节build_tiny_pe.py它和小的ELF一样工作:puts其他exit其他msvcrt.dll导出一个名称,运输一个基地转移,所以这三个目录需要课程.pefile其他国家llvm-objdump -p两者都读了,两者都被捕获.
按标题进行阅读.MZ并且在抵消0x3c保持e_lfanew现在0x40在0x40签名PE\0\0然后,COFF文件标题:机器0x8664其他部分SizeOfOptionalHeader240,特征 0x22选项标题开始于 Magic领域0x20b意思是PE32+.AddressOfEntryPoint是0x1000现在ImageBase是0x140000000现在SectionAlignment 0x1000现在FileAlignment 0x200现在SizeOfImage 0x5000现在Subsystem其他类型:DllCharacteristics 0x8160解码HIGH_ENTROPY_VA 0x0020现在DYNAMIC_BASE 0x0040现在NX_COMPAT 0x0100其他TERMINAL_SERVER_AWARE 0x8000然后设置16个RVA和尺寸数据目录.0x2080尺寸0x4d进口0x2000尺寸0x28基地转移0x4000尺寸0xc 0x2040尺寸0x18现在,我们要去.
图像中的地址是RVA,是从负载基的偏移..text车0x1000在文件中0x200现在.rdata车0x2000在0x400现在.data车0x3000在0x600现在.reloc车0x4000在0x800现在,我们要去.PeImage.rva_to_offset()是那个桌子.
进口目录
在RVA0x2000进口查找表是我们一个的,0x2028时间标签0,运送链0,名称 0x2068进口地址表0x2040.名称RVA指向"msvcrt.dll".搜索表包含以零结束的64位入口.比特63集意味着通过序列进口,序列在15到0位.比特63清晰意味着比特30到0是提示/名称输入的RVA:一个16位提示",一个索引进入出口名称指标表",然后是ASCII名称.我们的是0x2058子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子,子0x2060(提示0,"出口").
根据规格,IAT"与进口查找表的内容相同,直到图像被绑定".0x2040测试检查24字节的值0x2028等于24字节0x2040. "在绑定过程中,进口地址表中的输入被覆盖,将被进口的符号的32位 (PE32) 或64位 (PE32+) 地址. "Windows加载器在输入点运行之前这样做.没有惰的绑定和没有.代码直接进入插槽:
text 100b: ff 15 2f 10 00 00 call qword ptr [rip + 0x102f] # 0x1011 + 0x102f = 0x2040, the IAT slot of puts
1013: ff 15 2f 10 00 00 call qword ptr [rip + 0x102f] # 0x1019 + 0x102f = 0x2048, the IAT slot of exitFF /2两个指令具有相同的字节,因为每个字节都坐落在另一个字节后八个字节,并且读取一个字节在另一个字节后八个字节. iat_calls()扫描代码部分ff 15增加6个和移动,并将进口目录中的空隙命名.
出口目录
在RVA0x2080排列表 40 字节的出口目录表:旗,时间印,版本,一个指向"tiny-x86_64.exe"的名称RVA,序列基础 1,一个地址表入口,一个名称指标.0x20a8具有一个RVA,0x1000出口名称指标表0x20ac输出常规表在 0x20b0具有一个16位无偏见的指数,0.名称 k图片以地址登录ordinals[k]并且出口的定位是ordinals[k] + ordinal base规格增加了一个我们不使用的规则:一个指向出口部分的地址输入是发送器字符串,如MYDLL.expfunc标签表是"以词汇顺序排列,允许二进制搜索".
基地迁移
.reloc每个块开始一个页面RVA和一个块大小,然后16位输入,上面的4位是类型,下一个页面是12位.我们的一个块:页面0x3000尺寸12号,入口0xa000类型10IMAGE_REL_BASED_DIR64,除了0号,以及0号型入,IMAGE_REL_BASED_ABSOLUTE八个字节在RVA0x3000保持0x1400020d0图像文件的位置是"图像文件的位置" (图像文件的位置是"图像文件的位置"),图像的位置是"手动链接"的位置.如果载体将图像放置在其他地方,则它将差异添加到该字段.规格:"图像文件不含COFF迁移...图像包含以 .reloc部分的位置位置迁移形式的迁移信息 (除非图像具有IMAGE_FILE_RELOCS_STRIPPED属性)."/DYNAMICBASE在当前link.exe中默认开启,所以图像带有.reloc其他DYNAMIC_BASE现在,我们要去.
文件标题是"图像"的标题表.PointerToSymbolTable"因为COFF调试信息已经过时了",而这些名称存留在一个独立的PDB中..pdata谁的RUNTIME_FUNCTION输入指分配堆或调用其他函数的每个函数的开始和结束.
马赫-奥图像:载荷命令,子和间接符号表
ledger_main是链接的 arm64 执行式.otool -h打印19个载荷命令和旗0x00200085现在,我们要去.MH_NOUNDEFS 0x1现在MH_DYLDLINK 0x4现在MH_TWOLEVEL 0x80其他MH_PIE 0x200000总和到这个值.LC_SEGMENT_64(__PAGEZERO现在__TEXT现在__DATA_CONST现在__LINKEDIT),两LC_LOAD_DYLIB(@rpath/libledger.dylib然后/usr/lib/libSystem.B.dylib),以及每一个LC_DYLD_CHAINED_FIXUPS现在LC_DYLD_EXPORTS_TRIE现在LC_SYMTAB现在LC_DYSYMTAB现在LC_LOAD_DYLINKER现在LC_UUID现在LC_BUILD_VERSION现在LC_SOURCE_VERSION现在LC_MAIN现在LC_RPATH现在LC_FUNCTION_STARTS现在LC_DATA_IN_CODE其他LC_CODE_SIGNATURE现在,我们要去.MachoFile.command_names()返回相同列表,按相同的顺序otool -l现在,我们要去.
__TEXT开始于0x100000000现在,我们要去.__text是 256 字节的0x100000558现在main其他任何东西都没有,因为四个账本功能都在dylib中生活.__stubs是 84 字节的0x100000658七个12字节的块.__DATA_CONST开始于0x100004000随着__got 64 字节, 8 个分类的指针插槽S_NON_LAZY_SYMBOL_POINTERS其他__const 32 字节,fns[]其他names[]表main.c现在,我们要去.
一个,手工
为了_puts现在0x100000694其他:
text 30 00 00 90 adrp x16, 0x100004000
10 1e 40 f9 ldr x16, [x16, #0x38]
00 02 1f d6 br x160x90000030是adrp现金库是低的五位,16位.immlo只有30到29位.immhi值是 immhi << 2 | immlo = 4目标页面是指令页面,0x100000000另外0x4000现在,我们要去.0xf9401e10是ldr没有签名的12位偏移量为8: 21 位到 10 位是 7,乘以 8 位是0x38现在,我们要做什么?0x100004038现在,我们要去.0xd61f0200是br x16据说,这就是Id64的源头.StubAtom现在,我们要去.
哪个名字属于插槽来自间接符号表. LC_DYSYMTAB标志表中15个指数.reserved1在哪里开始指数的运行reserved2每个子的尺寸 (12).reserved1并且每个插槽有8个字节.__stubs开始于指数0和__got在指数7上,所以切5图到符号表入口10, _puts其他地方__got图片与相同的输入.otool -Iv印记了两条路,indirect_table()复制每一行.
两个级别的名称空间
nm -m印记(undefined) external _puts (from libSystem)其他(undefined) external _grade (from libledger)图书馆来自于符号记录本身.nlist.h定义GET_LIBRARY_ORDINAL(n_desc)作为15到8个位n_desc首先一个LC_LOAD_DYLIB两次的第二次,0xfe动态搜索0xff对于可执行的.ld(1): "用 -two_levelnamespace (默认),链接器只在命令行搜索符号中的dylib,并记录它们在哪里找到dylib. "nm_lines()通过" "的12张录像n_type现在n_sect现在n_desc其他n_value测试将其输出线与捕获的线进行比较nm -m连接到-Wl,-flat_namespace阅读otool -h为了看到MH_TWOLEVEL它们会消失.
链接的修复,字节对字节
LC_DYLD_CHAINED_FIXUPS是一个linkedit_data_command其他dataoff 0x8000现在datasize 208接下来是208字节.fixup-chains.h现在,我们要去.
标题为7个32位字段:fixups_version 0现在starts_offset 32现在imports_offset 80现在symbols_offset 112现在imports_count 8现在imports_format 1(DYLD_CHAINED_IMPORT), symbols_format 0没有压缩.llvm-objdump --macho --chained-fixups测试将这些7个值打印,然后将它们比较.
在32场的补偿中,dyld_chained_starts_in_image其他seg_count 4只有一个零件,没有修复的零件.__DATA_CONST没有一个.dyld_chained_starts_in_segment读书size 24现在page_size 0x4000现在pointer_format 6(DYLD_CHAINED_PTR_64_OFFSET目标是vm抵消),segment_offset 0x4000现在max_valid_pointer 0现在page_count 1一个page_start值为 0.DYLD_CHAINED_PTR_START_NONE现在0xffff没有任何事.
链接开始于文件的偏移0x4000 + 0每个64位字都有两个东西:写什么,下一个字是哪里.
text dyld_chained_ptr_64_bind ordinal:24 addend:8 reserved:19 next:12 bind:1 = 1
dyld_chained_ptr_64_rebase target:36 high8:8 reserved:7 next:12 bind:1 = 0读一下第一句话,0x8010000000000000位63设置,结合.next它们的数量为 62 到 51 位.0x002字母为0. 读出第十二个字符在0x4058现在0x00000000000006db打了63分,打了1分.next目标是0x6db像图的基础0x100000000让它成功0x1000006db,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,__cstring在前面的词,0x00100000000006d6股names[0]现在,我们可以说"甚至".
步行中,有12个字:8个字结合了__got具有0到7的序列,两个结合__const具有序列0 (两个输入的fns[]点在_grade),以及两项补贴names[]现在,我们要去.chained_fixups()检测结果将12个数据进行比较.(offset, raw)配对dyld_info -fixup_chain_details它们的印花raw: 0x8010000000000000 bind: (next: 002, bindOrdinal: 0x000000)首先,我会看到你.
抵消80的进口表有八个32位入口: lib_ordinal:8现在weak_import:1现在name_offset:23总数0到3是_grade现在_grade_label现在_percent_change现在_total图书馆1号和4号至7号___stack_chk_fail现在___stack_chk_guard现在_printf现在_puts图书馆 2.fixup_rows()连接两个表格到行列中dyld_info -fixups图像,如__DATA_CONST __got 0x100004038 bind libSystem/_puts测试将所有12个都比较起来.
现在,我们要做什么?0x100004038鱼在运载时,一次走过这个链条,并写出了_puts进入槽. 果在WWDC22会议上这样说:"在DATA中的64位指针位置, 另外,还包含了一个说明是否是绑定或重新基础的小部分".Xcode 13使该格式成为macOS 12或iOS 15的部署目标的默认格式, 在这种格式中,没有惰的结合. 每个插槽都被绑定在前面main走了.
它所取代的布局
ledger_main_nochain是与-Wl,-no_fixup_chains没有.LC_DYLD_INFO_ONLY通过4个字节编码表:重建8字节,绑定72,惰绑定120,出口64.__TEXT,__stub_helper号码:__got只有16个字节的存储量___stack_chk_guard其他dyld_stub_binder其他__DATA,__la_symbol_ptr它们的位置是:dyld_info -fixups列表七个lazy-bind七个杆,指着每个惰的槽在自己的道上.__stub_helper轮推出指数,跳到dyld_stub_binder现在,我们可以看到一个字符,__la_symbol_ptr是.got.plt现在__stub_helper是PLT0和pushq $index其他dyld_stub_binder是_dl_runtime_resolve现在,我们要去.code/main.py报告本文件的偏移和部分,并不会解码绑定代码;实践实验室要求这样做.
进口的情况
LC_DYLD_EXPORTS_TRIE每个节点是 uleb128终端大小,然后,如果它不是零, uleb128 旗和 uleb128 偏移,然后一个字节的孩子数量,每个孩子,一个字符串边缘和 uleb128 节点偏移. exports_trie()走着它.libledger.dylib它回来了._percent_change 0x410现在_grade 0x428现在_total 0x464现在_grade_label 0x498现在,我们要做什么?dyld_info -exports在执行式上,它返回了__mh_execute_header现在_fns其他_main现在,我们要去.
什么条纹的叶子
果公司strip(1)没有选择在动态链接的执行器上,它表现得像-u -r:保存未定义的符号和动态引用的符号. -x删除本地符号.-N移除整个符号表和字符串表. "对于动态共享库,最大的剥离水平通常是 - x",因为"动态链接过程只使用全球符号".
我们的灯具上观察到.strip ledger_main接着LC_SYMTAB已删除12至10份记录_main现在_fns其他_names八个未确定进口仍然存在,因为他们需要他们的名称结合,__mh_execute_header现在,它是因为它是REFERENCED_DYNAMICALLY第十个记录是个类型的刺伤.N_OPT名称radr://5614542果的工具写下来,让桌子永远没有空.nm截图的程序将打印与原始的三行相同,然后输出0.LC_FUNCTION_STARTS现在还有一个开始,main现在,我们要去.
strip libledger.dylib没有选择,比男人的平常走得更远.-x其他LC_SYMTAB具有0个记录,nm输出三角形仍然列出了四个函数,因为Dild通过三角形结合,从来没有通过.LC_SYMTAB现在,我们要去.LC_FUNCTION_STARTS实验室C连接一个新的main.c为了证明链接编辑器也被试用.
| What | ELF after --strip-all | PE image | Mach-O after strip |
|---|---|---|---|
| static symbol table | gone | never present in an image | local and defined names gone, undefined imports stay |
| names the loader binds | .dynsym and .dynstr stay | import directory stays | imports stay in LC_SYMTAB and in the chained imports table |
| exported names | .dynsym stays | export directory stays | exports trie stays |
| function starts | .eh_frame FDEs, unless built without unwind tables | .pdata for non-leaf functions | LC_FUNCTION_STARTS stays |
| debug information | .debug_* gone | in the PDB, never in the image | in the dSYM, never in the image |
| section or load command headers | stay unless --strip-section-headers | stay | stay |
找出没有符号的功能
static-arm64-O2链接main-arm64-O2.o其他ledger-arm64-O2.o转化为一个可执行的五个函数__text现在,我们要去.nm -n没有删除的文件是基本的真理:_main在0x100000500其他类型:_percent_change在0x1000005f4其他国家_grade在0x10000060c其他国家_total在0x100000648其他国家_grade_label在0x100000740(72) static-arm64-O2.stripped没有N_SECT内部的符号__text现在,我们要去.FunctionFinder运行四种方法,score()对于每一个名单,都会与真相相比较.
开始
命令点在9个字节中__LINKEDIT其他80 0a f4 01 18 3c f8 01 00格式是Uleb128deltas,这是从__TEXT其他地方80 0a是0x500所以第一开始是0x100000500现在,我们要去.f4 01是0xf4现在,0x1000005f4现在,我们要去.18给出0x10000060c现在3c给出0x100000648现在f8 01给出0x100000740其他00五分五,没有虚假阳性,-O0在-O2链接编辑写这张表,用于机和机记者,strip吉德拉在他的分析器中读到了"Mach-O功能开始".dyld_info -function_starts,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function_starts现在,我们要去.
序幕扫描
吉德拉的AARCH64开始模式从fd 7b bf a9现在,stp x29, x30, [sp, #-0x10]!在我们的文件中,它在两个层面都发现了5分之一.sub sp, sp, #N首先,然后stp x29, x30, [sp, #M]面具为 ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,sub sp, sp, #imm现在(word & 0xff0003ff) == 0xd10003ff现在,我们在 5 中找到 5-O0并且在 5 个函数中,-O2在哪里percent_change现在grade现在total其他grade_label它们是没有框架的叶子函数.percent_change开始于cbz w0, ...编译器选择发射什么,课38显示,这有多么频繁是什么.
由于程序很小,所以这两种扫描都没有产生错误正值.55其他push rbp由于Ghidra的x86-64模式文件被合到其他命令中,55 48 89 e5具有类似的预示模式ret或nop这必须结束之前的功能.
呼叫目标
每一个bl在__text函数开始的名称,因为bl在函数中部中,是编译器不发射的东西.0x94000000通过26位字符的字符.bl在0x1000005a0读书3d 00 00 94其他0x3d乘以4是61.0xf4所以目标是0x100000694在ledger_main这就是子的子._puts在静态构造中,call_targets()收集了目标的内部__text让那些落地的人落下__stubs增加了LC_MAIN进入,因为没有什么叫main核子 (安德里斯,斯洛文斯卡,博斯,2017) 基于编译器生成代码的这个想法建立了其函数检测,安德里斯和其他人在2016年测量了函数边界是拆除的最困难部分,比指令边界更难.
一个的签名
六射线在文件中描述了FLIRT:一个模式是"一个函数的第32字节,其中所有变体字节都标记",变体字节来自固定和外部引用,模式生活在树上,并且在模式之外"我们计算了从位置33开始到第一个变体字节的字节的CRC16",并存储了这一长度. 两个具有相同的模式和CRC的函数在不同位置的尾巴字节分开,然后由它们所指的名称分开. 任何分离不出来的碰撞"都是自然智能能解决的". 没有外部引用的四字节短的函数被排除在外,并且"签名文件不包含原始库中的字节,除了函数名称".
signatures_from_object()建立了这个从ledger-arm64-O2.o其他main-arm64-O2.o像FLAIR从图书馆的物体中构建的.relocation_info记录包含一个4字节的单词,然后这些字节变得变异..pat图表,CRC长度,CRC16长度,函数长度,:0000名称,以及^offset name对于每个引用名称:
textA00000342800004B890C8052087D091B000DC01AC0035FD6 00 0000 0018 :0000 _percent_change
E8CC8C52C8CCAC72087C289B09FD7FD308FD62930801090B081900511F110071 04 9AAF 003C :0000 _grade ^0000 l_switch.table.grade ^0001 l_switch.table.grade
E8CC8C52C8CCAC72087C289B09FD7FD308FD62930801090B081900511F110071 04 9AAF 0048 :0000 _grade_label ^0000 l_switch.table.grade_label ^0001 l_switch.table.grade_label ^0002 _labels ^0003 _labels_percent_change并且CRC长度为0._grade据了解,在CRC的位置是60字节,第一次转移到36字节,所以CRC覆盖了4字节..pat解析器使用:多项式0x8408起始值0xffff对于ASCII字符串"123456789"的值是0x6e90试验针是这样的.
match_signatures()在每一个函数开始的每一个签名试验, 脱离的构建, 首先模式,然后CRC.-O0五个函数都得到了它们的名字,没有什么碰撞.-O2两条线上碰撞.grade_label入grade两个函数共享了它们的第36字节和4个CRC字节.0x10000060c在0x100000740尾巴将它们分开: 44 字节_grade没有20其他_grade_label没有28现在,ldr w0, [x9, w8, uxtw #2]反对ldr x8, [x9, w8, uxtw #3]报告记录了碰撞和决定它们的字节.
测试test_object_bytes_equal_linked_bytes_outside_relocated_words检查前提:每个对象函数的每个非变量字节均等于链接可执行中的相同的偏移字节,因此链接编辑器只改变了移动的单词. _total没有转移,没有变量字节.
工具能用同一个想法做什么
通过 IDA Pro 9.4 发送每个处理器的FLIRT签名sigmake工具用于构建它们;IDA Free仅涵盖x86和x86-64并且用于非商业用途. 路米纳是一个服务器,通过 md5 键键键键函数的 metadata,并跳过32字节以下的函数. 吉德拉 12.1.4 (Apache-2.0) 有功能 ID:一个完整的哈希在 mnemonics 和解决模式上是"强大的由于链接的变化"和一个特定的哈希,添加了恒定操作数,分分为每匹配的指示,父母和孩子的功能作为结合打破者;它的运输数据库只覆盖视觉工作室图书馆,你从自己的二进制中填充自己的. 双色球忍者6.0克里普顿 (商业版,免费版) 使用WARP (Apache-2.0):函数 GUID是基本区块 GUID 上 UUIDv5,每个区块字节 上 UUIDv5 零化后"所有可移动说明"和放下 NOP,与调用,调用和邻近函数作为限制. 二进制器8 (Apache-2.0) 匹配两个双数之间的函数,步骤的定序,从哈希匹配通过流量图的MD指数调称序列匹配,并从匹配的边缘,块,说明和流量图的MD指数中重量相似性. 图形3.4.3 (仅AGPL-3.0,IDA) 添加小素数产品,每个mnemonic图为一个素数和函数是产品,一个无序多组指纹. 雷达2.6.2.4 (LGPL-3.0) 有签名 (zg现在z/现在zb) 并且读为FLIRT .sig文件zfs酸 0.9.1 (LGPL-3.0) 产生了.pat或.sig通过一个开放的二进制Fc并且使用一个Fs现在,我们要去.
机器上没有安装Radar2和Rizin,他们的命令是从他们的书籍和来源中引用的.这些设备上运行的工具是clang现在ld现在llvm-objdump现在otool现在nm现在strip其他dyld_info通过探测器,LIEF阅读了所有格式,包括自0.17.0以来支持的COFF对象,并报告了10个绑定和2个依据.ledger_main文件只读取ELF文件. 文件拒绝main-x86_64.obj随着PEFormatError: 'DOS Header magic not found.'读一读这个小图片.
建立它
运行演示:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries
python3 code/main.py输出是一个五部分的JSON报告. elf两个物体的迁移数量,链接计划和动态部分,PLT和GOTtiny-dyn-x86_64现在,我们要去.coff具有两个对象的部分,迁移,功能和未定义名称. pe拥有该公司的标题,目录,进口,出口,基地转移和国际通用电话tiny-x86_64.exe现在,我们要去.macho保持ledger_main由于其,链接的固定,固定行,和出口,ledger_main_nochain,,和两份漫画报告.identification通过四种方法,并通过分数,-O0其他-O2现在,我们要去.
预期的形状:
json{
"elf": {"linkPlan": {"pltSymbols": ["puts", "printf"], "sizes": {".plt": 48, ".got.plt": 40, ".rela.plt": 48, ".rela.dyn": 48}, "...": "..."}, "tiny-dyn-x86_64": {"...": "..."}},
"coff": {"main-x86_64.obj": {"...": "..."}},
"pe": {"imports": [{"dll": "msvcrt.dll", "iat": "0x2040", "...": "..."}], "...": "..."},
"macho": {"ledger_main": {"flags": "0x200085", "fixupRows": ["__DATA_CONST __got 0x100004000 bind libledger/_grade", "..."], "...": "..."}, "strip": {"...": "..."}},
"identification": {"O2": {"methods": {"functionStarts": {"recall": "5/5"}, "prologueSub": {"recall": "1/5"}, "...": "..."}, "correctNames": 5}}
}阅读code/main.py在此顺序:
ElfFile阅读标题,部分,象征表,以及转移,dynamic_entries()现在jump_slots()现在plt_entries()其他plt0()阅读链接的图像.plan_dynamic_link()适用于移动物体移动记录的4.9表和5.2节.CoffObject读取文件标题,部分表,18字节符号及其辅助记录,以及10字节移动.PeImage阅读MS-DOS的图片,PE签名,可选标题,数据目录,部分以及进口,出口和基调迁目录,iat_calls()解码ff 15现在,我们要去.MachoFile阅读载荷命令,段子,nlist记录,间接符号表,片,链接的固定,出口试验和物体转移.FunctionFinder运行四个开始发现器和score()对于每一个人,都是对真理的.signatures_from_object()其他match_signatures()建立和应用FLIRT形状的签名.- 其他
parse_*测试对比的捕获工具文本.
更多命令:python3 code/main.py elf FILE现在coff FILE现在pe FILE其他macho FILE打印一个报告.python3 code/main.py plan OBJECT...打印任何ELF对象的链接计划. python3 code/main.py functions STRIPPED UNSTRIPPED OBJECT...给你一个新建的新建物,
用它
在你构建的程序中,用读者按此顺序.
- 读取标题,并指定格式和类型:可移动的物体,可执行的物体,或共享的图书馆.
- 列出进口与供应商:
DT_NEEDED其他.dynsym进口目录或链接进口表LC_LOAD_DYLIB每次呼叫名单上的每一个名字都会留下图像. - 随着一个通话的手续到其插槽. 写下通话字节,插槽或PLT字节,插槽地址,文件中的字节,以及载体所写的内容. 如果文件中的字节看起来像垃圾,然后把它解码为一个修复,然后把它称为垃圾.
- 运输器需要存活的名称
strip只有一个调试器需要的名字,不需要. - 在一个剥离的构建中,运行可以运行的搜索器,然后与未剥离的双胞胎进行比分,
LC_FUNCTION_STARTS现在.pdata其他.eh_frame通过字节模式,使用呼叫目标填补空白. - 根据两个签名的要求,一个名字是未知的,直到尾巴或引用的名字决定.
随着工具链的变化,重新捕获,因为一个改变的列表是改变的固定.
互动实验室
实验室A:手工 PLT
关于code开放字符串:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries/code
python3 -q运行:
pythonfrom main import *
elf = load_elf("tiny-dyn-x86_64")
[(e.tag_name, e.text) for e in elf.dynamic_entries()]
elf.plt0()
[(e.symbol, hex(e.address), hex(e.slot), hex(e.slot_initial)) for e in elf.plt_entries()]
[hex(w) for w in elf.got_plt_words()]在每次电话之前,从上面的文字中计算答案.IMPORTS在build_tiny_dyn_elf.py为了使我们能够实现这一目标,.plt现在.got.plt其他.rela.plt在你读之前.
实验室B:一个链接的修复
pythonexe = load_macho("ledger_main")
stub = next(s for s in exe.stubs() if s.symbol == "_puts")
stub.lines()
raw = int.from_bytes(exe.read(stub.slot, 8), "little")
hex(raw), raw >> 63, (raw >> 51) & 0xfff, raw & 0xffffff
[(i.index, i.name, i.library) for i in exe.chained_fixups()["imports"]]首先,在纸上解码绑定字:绑定字,next然后再读8字节的字,确认这是下一个修复.然后读取链接的最后一个字在0x100004058解释为什么它是next没有任何东西.
实验室C:脱离和重新连接
在安装目录中,将一个新程序与完全脱掉的dylib连接起来:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries/code/fixtures
clang -target arm64-apple-macos -O1 main.c -L. -lledger.stripped -Wl,-rpath,@executable_path -o /tmp/relinked
nm /tmp/relinked | grep ' U '
dyld_info -exports libledger.stripped.dylib通过一个图书馆进行了成功的链接.LC_SYMTAB链接编辑器读出出口试图.python3 ../main.py macho libledger.stripped.dylib并且比较symtab现在exports其他functionStarts然后用 片去除静态构造.-N现在我们要看看什么.dyld_info -function_starts仍然有印记.
实验室D:碰撞
pythonfrom main import *
stripped = load_macho("static-arm64-O2.stripped")
sigs = signatures_from_object(load_macho("ledger-arm64-O2.o"))
[s.pat_line() for s in sigs]
starts = FunctionFinder(stripped).function_starts()
match_signatures(sigs, stripped, starts)读出两次碰撞记录,然后编写来自ledger-arm64-O0.o打对阵他们static-arm64-O0.stripped通过".pat没有发生碰撞的原因-O0然后缩短PATTERN_LENGTH它们在一个位置上,
实践实验室
解码已旧的结合表,并添加PE探测器.
要求:
- 解码
LC_DYLD_INFO_ONLY基于,绑定,和惰绑定的opcode流ledger_main_nochain按段子,段子,地址,类型和目标的行列,并使它们等于捕获的21行dyld_info -fixups给我一个短信. - 添加一个
PT_GNU_RELRO项目标题和一个DT_FLAGS随着DF_BIND_NOW为了build_tiny_dyn_elf.py移动.got.plt在覆盖范围内,并做llvm-objdump -p在 README 装置中,请说明这部份的全RELRO是什么部分,只有glibc才能执行. - 写一个
.pdata读取PE32+图像和使用函数查找器RUNTIME_FUNCTION开始地址. 建立一个对象clang -target x86_64-pc-windows-msvc -c没有-fno-asynchronous-unwind-tables阅读它.pdata转移,并说明表中遗漏的其函数是页面函数. - 写一个对每个读者进行测试,并与捕获的工具文本进行比较.
成功意味着python3 code/main.py macho ledger_main_nochain打印出相同的21个固定行dyld_info印记,小的ELF携带一个PT_GNU_RELRO这llvm-objdump其他国家.pdata搜索器将其能执行的功能命名,并列出其不能执行的页面功能.
教训艺术品
这一课包括outputs/skill-linking-loading-and-stripped-binaries.md通过它将你构建的程序变成了其进口的列表,strip需要一个未删除的双胞胎作为地面真理,一个捕获的工具列表每一个索赔,和记录每一次碰撞.
检查
运行演示和确定性套件:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries
python3 code/main.py
python3 code/tests/test_main.py验证应证明:
- 动态部分
tiny-dyn-x86_64匹配llvm-objdump -p标签以标签,DT_FLAGS_1运载DF_1_PIE - 两个人
R_X86_64_JUMP_SLOT记录一致llvm-objdump -R名称puts其他exit - PLT0 推 GOT+8 跳过 GOT+16,每个输入都读取其插槽,推出其索引,每个插槽都包含其自己的地址
pushq - 微小的ELF比赛的部分和节目标题
llvm-objdump -h -p - 两种ELF物体的迁移记录相匹配
llvm-objdump -r - 链接计划发现了两个PLT符号,四个直接电话,两个相对移动,以及小图像的尺寸
- 根据COFF部分表,37个符号记录与辅助记录,以及迁移相匹配
llvm-objdump -h -t -r其他/16解决问题.llvm_addrsig - 三个 COFF
REL32记录ledger坐在三上.PC32抵消 - 专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专业专
DllCharacteristics数据目录和数据目录相匹配llvm-objdump -p - 进口目录,提示/名称条目和IAT匹配
llvm-objdump其他pefile结前的查找表等于IAT - 出口目录和
DIR64移动指标将字符串命名为"手动链接" ff 15在RVA0x100b解决了IAT的插槽puts- 马赫-奥旗,19个载荷命令名称,两个图书馆相匹配
otool - 七个子将解码为八个
__got插槽和间接符号表匹配otool -Iv - 链接的头相匹配
llvm-objdump --macho --chained-fixups - 链路行程将所有12个原始字体复制成
dyld_info -fixup_chain_details - 固定行和进口表相匹配
dyld_info -fixups其他-imports - 出口量相匹配
dyld_info -exports对于子和其剥离的副本, nm_lines()相当于nm -m执行可及其剥离副本strip删除_main现在_fns其他_names保持了9个名字和一个名字radr://5614542打刺,然后剥离的程序打印出相同的输出- 传统的布局已经
LC_DYLD_INFO_ONLY七个惰的指针,__stub_helper - 亚马64对象移动匹配
llvm-objdump -r LC_FUNCTION_STARTS在两级和等级中,恢复了五分五.nm其他dyld_info -function_starts- 其他
sub sp扫描结果是5个5个-O0五个人的一个-O2其他stp图像扫描发现没有 bl目标加上LC_MAIN进入回收五个五个- 签名将在两个层面上列出所有五个函数,并记录
grade碰撞分为44字节 - "123456789"的CRC16是
0x6e90在 GOT 载荷对下面的8个字节main是变体 - 每个非变量对象字节均等于链接的字节
- 演示和数字数据报告相同的地址,网站模块将相同的数据嵌入ES5中
生产失败模式
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
A bl or call lands in a stub | "calls an unnamed function at 0x694" | the stub bytes, the slot it loads, and the import the slot binds |
| A GOT word read from the file | "corrupt pointer 0x8010000000000007" | bit 63 is bind, next is 2, ordinal 7 is _puts from library 2 |
PLT32 in an object | "every call goes through the PLT" | defined symbols are reduced to a direct distance, only undefined ones get an entry |
| Partial RELRO read as full | "GOT is read-only" | .got.plt slots after the three reserved ones sit outside PT_GNU_RELRO without -z now |
COFF REL32 compared with ELF PC32 | "the Windows build has different relocations" | same offsets, the -4 lives in the ELF addend and in the COFF field |
| IAT read before load | "imports point at strings" | the IAT equals the lookup table until the loader binds, and the strings are hint/name entries |
| A stripped dylib | "no symbols, nothing exported" | the exports trie and LC_FUNCTION_STARTS list every function |
nm on a stripped executable | "one function" | LC_FUNCTION_STARTS lists five starts, nine bytes long |
A prologue scan at -O2 | "one function" | four leaf functions have no frame, call targets and the starts table find them |
| Two functions share 36 bytes | "grade_label is grade" | the tail byte at 44 and the lengths 60 and 72 split the collision |
| A tool run on another machine's build | the numbers in this text | recapture every listing from your own fixtures |
运营规则
按照调用站点到充电器填充的插槽的每一个外部调用,然后在调用地址之前,将该插槽中的字体解码为格式定义的. 只有在记录了装载器仍然需要的东西之后才脱掉. 恢复功能从链接编辑器在扫描字节之前编写的表开始,
进一步阅读
- System V gABI, chapter 5: Program Loading and Dynamic Linking
- System V Application Binary Interface, AMD64 Architecture Processor Supplement: section 5.2 and Table 4.9
- Drepper, "How To Write Shared Libraries", version 4.1.2
- Drepper, "ELF Symbol Versioning"
- Jelinek, "Little hardening DSOs/executables against exploits", binutils list, 2004-01-06
- Taylor, "Linker relro", 2008
- binutils commit bd7ab16b: PLT32 for calls, 2018-02-13其他LLVM D43383
- GNU Binutils: strip其他llvm-objcopy
- Microsoft, PE Format
- CALL instruction reference, FF /2
- Apple, mach-o/loader.h其他mach-o/nlist.h
- Apple dyld, mach-o/fixup-chains.h
- Apple ld64, stub_arm64.hpp
- Xcode 13 release notes: chained fixups其他WWDC22 session 110362, "Link fast: Improve build and launch times"
- strip(1)现在ld(1)其他dyld(1)
- Hex-Rays, IDA F.L.I.R.T. Technology: In-Depth其他Lumina overview
- Ghidra Function ID help其他AARCH64 function start patterns
- Vector 35, WARP其他Binary Ninja WARP guide
- BinDiff manual其他BinDiff source
- Diaphora
- rizin pat.c, the .pat parser现在radare2 flirt.c其他radare2 book: Zignatures
- Andriesse et al., "An In-Depth Analysis of Disassembly on Full-Scale x86/x64 Binaries" (USENIX Security 2016)其他Andriesse, Slowinska, Bos, "Compiler-Agnostic Function Detection in Binaries" (EuroS&P 2017)
- Bao et al., ByteWeight (USENIX Security 2014)其他Shin, Song, Moazzezi, "Recognizing Functions in Binaries with Neural Networks" (USENIX Security 2015)
- LIEF changelog现在pyelftools其他pefile
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.