upx 源码分析
主程序入口: main.cpp --->upx_main 对选项进行处理-
work.cpp: do_files() --> work.cpp: do_one_file()--: 先对要压缩的文件进行检查,使用 pm.pack(&fo); 调用pack 函数对文件进行
packmaster.cpp: pack() ---> getPacker ()--> try_pack()--> 对各种pack header 进行初始化--》do_pack()
参考如下:
【源码分析】UPX 源码分析 - binLep's Blog
Using reverse engineering techniques to see how a common malware packer works - Detectify Labs
UPX源码分析——加壳篇_江西省遂川县杰出青年代表的博客-CSDN博客_linux upx 加壳
elf 文件解析
程序的本质之二ELF文件的文件头、section header和program header_tanglinux的博客-CSDN博客
pack1() :
PackLinuxElf64::generateElfHdr (this=0x5555557952c0, fo=0x7fffffffd1e0, proto=0x5555556ab160 <stub_amd64_linux_elf_fold>, brka=24696) at p_lx_elf.cpp:3030 #1 0x00005555555c21c5 in PackLinuxElf64amd::pack1 (this=0x5555557952c0, fo=0x7fffffffd1e0, ft=...) at p_lx_elf.cpp:3808 #2 0x00005555555f3d8a in PackUnix::pack (this=0x5555557952c0, fo=0x7fffffffd1e0) at p_unix.cpp:293 #3 0x000055555560e030 in Packer::doPack (this=0x5555557952c0, fo=0x7fffffffd1e0) at packer.cpp:100 #4 0x0000555555618315 in PackMaster::pack (this=0x7fffffffd2b0, fo=0x7fffffffd1e0) at packmast.cpp:262 #5 0x0000555555635f42 in do_one_file (iname=0x7fffffffe1ba "a", oname=0x7fffffffd890 "a.000") at work.cpp:158 #6 0x0000555555636377 in do_files (i=1, argc=2, argv=0x7fffffffdde8) at work.cpp:271 #7 0x00005555555a55a6 in main (argc=2, argv=0x7fffffffdde8) at main.cpp:1539
源码分析
stub/amd64-linux.elf-entry.h:0x1850,到文件尾部内容如下读取出来的内容,用于loader 初始化section,symbols, relocations
Sections: Idx Name Size VMA LMA File off Algn Flags 0 ELFMAINX 0000000f 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, RELOC, READONLY 1 NRV_HEAD 00000066 0000000000000000 0000000000000000 0000004f 2**0 CONTENTS, READONLY 2 NRV2E 000000ba 0000000000000000 0000000000000000 000000b5 2**0 CONTENTS, RELOC, READONLY 3 NRV2D 000000a1 0000000000000000 0000000000000000 0000016f 2**0 CONTENTS, RELOC, READONLY 4 NRV2B 00000093 0000000000000000 0000000000000000 00000210 2**0 CONTENTS, RELOC, READONLY 5 LZMA_ELF00 00000064 0000000000000000 0000000000000000 000002a3 2**0 CONTENTS, RELOC, READONLY 6 LZMA_DEC10 000009f7 0000000000000000 0000000000000000 00000307 2**0 CONTENTS, READONLY 7 LZMA_DEC20 000009f7 0000000000000000 0000000000000000 00000cfe 2**0 CONTENTS, READONLY 8 LZMA_DEC30 00000018 0000000000000000 0000000000000000 000016f5 2**0 CONTENTS, READONLY 9 NRV_TAIL 00000000 0000000000000000 0000000000000000 0000170d 2**0 CONTENTS, READONLY 10 ELFMAINY 0000003a 0000000000000000 0000000000000000 0000170d 2**0 CONTENTS, RELOC, READONLY 11 ELFMAINZ 000000ef 0000000000000000 0000000000000000 00001747 2**0 CONTENTS, RELOC, READONLY SYMBOL TABLE: 0000000000000000 l d NRV_HEAD 0000000000000000 NRV_HEAD 0000000000000000 l d LZMA_DEC30 0000000000000000 LZMA_DEC30 0000000000000000 l d ELFMAINY 0000000000000000 ELFMAINY 0000000000000000 l d ELFMAINZ 0000000000000000 ELFMAINZ 0000000000000000 l d ELFMAINX 0000000000000000 ELFMAINX 0000000000000000 l d NRV2E 0000000000000000 NRV2E 0000000000000000 l d NRV2D 0000000000000000 NRV2D 0000000000000000 l d NRV2B 0000000000000000 NRV2B 0000000000000000 l d LZMA_ELF00 0000000000000000 LZMA_ELF00 0000000000000000 l d LZMA_DEC10 0000000000000000 LZMA_DEC10 0000000000000000 l d LZMA_DEC20 0000000000000000 LZMA_DEC20 0000000000000000 l d NRV_TAIL 0000000000000000 NRV_TAIL 0000000000000000 g ELFMAINX 0000000000000000 _start 0000000000000000 *UND* 0000000000000000 O_BINFO RELOCATION RECORDS FOR [ELFMAINX]: OFFSET TYPE VALUE 0000000000000003 R_X86_64_PC32 ELFMAINZ+0x00000000000000d2 RELOCATION RECORDS FOR [NRV2E]: OFFSET TYPE VALUE 00000000000000af R_X86_64_PC32 NRV_HEAD+0x0000000000000021 000000000000005c R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [NRV2D]: OFFSET TYPE VALUE 0000000000000096 R_X86_64_PC32 NRV_HEAD+0x0000000000000021 000000000000005c R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [NRV2B]: OFFSET TYPE VALUE 000000000000008b R_X86_64_PC32 NRV_HEAD+0x0000000000000021 0000000000000053 R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [LZMA_ELF00]: OFFSET TYPE VALUE 0000000000000006 R_X86_64_PC32 LZMA_DEC30+0x0000000000000012 RELOCATION RECORDS FOR [ELFMAINY]: OFFSET TYPE VALUE 0000000000000018 R_X86_64_PC32 ELFMAINZ+0x0000000000000003 RELOCATION RECORDS FOR [ELFMAINZ]: OFFSET TYPE VALUE 00000000000000eb R_X86_64_32 O_BINFO
-
原有二进制文件progarm header 内容,
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000002d8 0x00000000000002d8 R 0x8 INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318 0x000000000000001c 0x000000000000001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000e70 0x0000000000000e70 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000002cf5 0x0000000000002cf5 R E 0x1000 LOAD 0x0000000000004000 0x0000000000004000 0x0000000000004000 0x0000000000000688 0x0000000000000688 R 0x1000 LOAD 0x0000000000004c98 0x0000000000005c98 0x0000000000005c98 0x00000000000003c8 0x00000000000003e0 RW 0x1000 DYNAMIC 0x0000000000004ca8 0x0000000000005ca8 0x0000000000005ca8 0x0000000000000210 0x0000000000000210 RW 0x8 NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000020 0x0000000000000020 R 0x8 NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358 0x0000000000000044 0x0000000000000044 R 0x4 GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000020 0x0000000000000020 R 0x8 GNU_EH_FRAME 0x0000000000004260 0x0000000000004260 0x0000000000004260 0x00000000000000d4 0x00000000000000d4 R 0x4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 GNU_RELRO 0x0000000000004c98 0x0000000000005c98 0x0000000000005c98 0x0000000000000368 0x0000000000000368 R 0x1
pack() 函数分析
pack1():
p_unix.cpp:277:
#0 PackLinuxElf64::pack1 (this=0x5555557952c0, fo=0x7fffffffd1e0) at p_lx_elf.cpp:3496 #1 0x00005555555c2154 in PackLinuxElf64amd::pack1 (this=0x5555557952c0, fo=0x7fffffffd1e0, ft=...) at p_lx_elf.cpp:3805 #2 0x00005555555f3d8a in PackUnix::pack (this=0x5555557952c0, fo=0x7fffffffd1e0) at p_unix.cpp:293 #3 0x000055555560e030 in Packer::doPack (this=0x5555557952c0, fo=0x7fffffffd1e0) at packer.cpp:100 #4 0x0000555555618315 in PackMaster::pack (this=0x7fffffffd2b0, fo=0x7fffffffd1e0) at packmast.cpp:262 #5 0x0000555555635f42 in do_one_file (iname=0x7fffffffe1c3 "a", oname=0x7fffffffd890 "a.upx") at work.cpp:158 #6 0x0000555555636377 in do_files (i=1, argc=2, argv=0x7fffffffdde8) at work.cpp:271 #7 0x00005555555a55a6 in main (argc=2, argv=0x7fffffffdde8) at main.cpp:1539
具体生成代码位于p_lx_elf.cpp:2943
先从stub/amd64-linux.elf-fold.h读取头部内容,并根据原有二进制文件内容进行赋值
memcpy(h3, proto, sizeof(*h3)); // reads beyond, but OK //proto = stub_amd64_linux_elf_fold,读取244 长度内容, PackLinuxElf64::cprElfHdr2 * h3 h3->ehdr.e_type = ehdri.e_type; // ET_EXEC vs ET_DYN (gcc -pie -fPIC),ehdri 为原始二进制头部内容。长度64 h3->ehdr.e_ident[Elf64_Ehdr::EI_OSABI] = ei_osabi;
p/x *h3 $30 = {ehdr = {e_ident = {0x7f, 0x45, 0x4c, 0x46, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, e_type = {d = {0x3, 0x0}}, e_machine = {d = {0x3e, 0x0}}, e_version = {d = {0x1, 0x0, 0x0, 0x0}}, e_entry = {d = {0xbc, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_phoff = { d = {0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_shoff = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_flags = {d = {0x0, 0x0, 0x0, 0x0}}, e_ehsize = {d = {0x40, 0x0}}, e_phentsize = {d = {0x38, 0x0}}, e_phnum = {d = {0x3, 0x0}}, e_shentsize = {d = {0x40, 0x0}},e_shnum = {d = {0x0, 0x0}}, e_shstrndx = {d = {0x0, 0x0}}}, phdr = {{p_type = {d = {0x1, 0x0, 0x0, 0x0}}, p_flags = {d = {0x5, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0xbc, 0x0, 0x0,0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x1, 0x0, 0x0, 0x0}}, p_flags = {d = {0x6, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = { d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x78, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = { d = {0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x51, 0xe5, 0x74, 0x64}}, p_flags = {d = {0x6, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}}, linfo = {l_checksum = {d = {0x2c, 0xe8, 0x3c, 0x1}}, l_magic = {d = {0x77, 0xe4, 0x48, 0x39}}, l_lsize = {d = {0xce, 0x73}}, l_version = 0x16, l_format = 0x56}}
生成header 部分, 1个elfheader+3个program header+l_info 大小为 244=64+56*3+sizeof(l_info)=64+168+12=244
(gdb) p/x hbuf $3 = {p_progid = 0x0, p_filesize = 0x6aa8, p_blocksize = 0x6aa8} 00000000: 7F45 4C46 0201 0100 0000 0000 0000 0000 .ELF............ 00000010: 0300 3E00 0100 0000 BC00 1000 0000 0000 ..>............. 00000020: 4000 0000 0000 0000 0000 0000 0000 0000 @............... 00000030: 0000 0000 4000 3800 0300 4000 0000 0000 ....@.8...@..... 00000040: 0100 0000 0500 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: BC00 0000 0000 0000 BC00 0000 0000 0000 ................ 00000070: 0010 0000 0000 0000 0100 0000 0600 0000 ................ 00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000a0: 7860 0000 0000 0000 0010 0000 0000 0000 x`.............. 000000b0: 51E5 7464 0600 0000 0000 0000 0000 0000 Q.td............ 000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000e0: 1000 0000 0000 0000 0000 0000 0000 0000 ................ 000000f0: 0000 0000
然后结构p_info ,blocksize 为后续压缩时使用,p_filesize 为压缩前文件大小为12
p/x hbuf $3 = {p_progid = 0x0, p_filesize = 0x6aa8, p_blocksize = 0x6aa8}
000000e0: 1000 0000 0000 0000 0000 0000 0000 0000 ................ 000000f0: 0000 0000 0000 0000 A86A 0000 A86A 0000 .........j...j..
a.upx 大小为 244+12=256;
pack2():
-
对PT_LOAD 段进行压缩: 对原有二进制中PT_LOAD 段内容进行压缩,压缩后加壳文件大小为6480,代码位于p_lx_elf.cpp:int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) ,该函数会调用p_unix.cpp:319 void PackUnix::packExtent( const Extent &x, unsigned &total_in, unsigned &total_out, Filter *ft, OutputFile fo, unsigned hdr_u_len)函数 , 该函数根据是否是最长可执行PT_LOAD 段,采取不同的压缩方法:
a) 对最长可执行PT_LOAD 段采用 compressWithFilters(ft, OVERHEAD, NULL_cconf, filter_strategy, 0, 0, 0, hdr_ibuf, hdr_u_len); //pakcer.cpp:1603 进行压缩,并进行loader 初始化操作,loader 会在pack3()中用到
b) 对于非可执行段采用,r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len, 0, ph.method, 10, NULL, NULL); 进行压缩。
详细分析如下:
1)
a) 压缩前先进行检查,读取头部内容到hdr_ibuf中, 大小为 elfheader+program header 大小为64+56×13=792。
对第一个段数据压缩时,去除头部elfheader 内容及 elfheader+program header 大小为64+56×13=792,从该地址开始读取PT_LOAD 内容,
$ readelf -a a.out 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: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x1460 Start of program headers: 64 (bytes into file) Start of section headers: 25320 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 13 Size of section headers: 64 (bytes) Number of section headers: 31 Section header string table index: 30
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000002d8 0x00000000000002d8 R 0x8 INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318 0x000000000000001c 0x000000000000001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000e70 0x0000000000000e70 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000002cf5 0x0000000000002cf5 R E 0x1000 LOAD 0x0000000000004000 0x0000000000004000 0x0000000000004000 0x0000000000000688 0x0000000000000688 R 0x1000 LOAD 0x0000000000004c98 0x0000000000005c98 0x0000000000005c98 0x00000000000003c8 0x00000000000003e0 RW 0x1000 DYNAMIC 0x0000000000004ca8 0x0000000000005ca8 0x0000000000005ca8 0x0000000000000210 0x0000000000000210 RW 0x8 NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000020 0x0000000000000020 R 0x8 NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358 0x0000000000000044 0x0000000000000044 R 0x4 GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338 0x0000000000000020 0x0000000000000020 R 0x8 GNU_EH_FRAME 0x0000000000004260 0x0000000000004260 0x0000000000004260 0x00000000000000d4 0x00000000000000d4 R 0x4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 GNU_RELRO 0x0000000000004c98 0x0000000000005c98 0x0000000000005c98 0x0000000000000368 0x0000000000000368 R 0x1
读取大小为0xe70-0x318=0xB58 大小为2904,对读取的数据进行压缩,因为第一个PT_LOAD 段为只读段,非可执行段,因此采用 compress(ibuf, ph.u_len, obuf); 该函数进行压缩。压缩完的数据存储在obuf中。
b) 由于首次压缩,对hdr_ibuf 内容进行压缩,r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len, 0, ph.method, 10, NULL, NULL); 压缩内容存储在hdr_obuf 中;压缩完头部后,使用binfo 存储压缩前后一些信息,
(gdb) l ph.saved_u_adler = upx_adler32(hdr_ibuf, hdr_u_len, init_u_adler);// 进行校验和计算, ph.saved_c_adler = upx_adler32(hdr_obuf, hdr_c_len, init_c_adler); ph.u_adler = upx_adler32(ibuf, ph.u_len, ph.saved_u_adler); ph.c_adler = upx_adler32(obuf, ph.c_len, ph.saved_c_adler); end_u_adler = ph.u_adler; memset(&tmp, 0, sizeof(tmp)); set_te32(&tmp.sz_unc, hdr_u_len); set_te32(&tmp.sz_cpr, hdr_c_len); tmp.b_method = (unsigned char) ph.method;//存储压缩使用的方法 (gdb) p/x tmp $31 = {sz_unc = 0x318, sz_cpr = 0xde, b_method = 0x2, b_ftid = 0x0, b_cto8 = 0x0, b_unused = 0x0}
写入binfo 信息,写入压缩后的header 内容。
00000100: 1803 0000 DE00 0000 0200 0000 F6FB 21FF ..............!. 00000110: 7F45 4C46 0201 0100 0300 3E00 0D60 140F .ELF......>..`.. 00000120: 77C9 0E76 4017 E862 2213 3800 0D2C 60DD w..v@..b".8..,`. 00000130: 7705 1F00 1E00 060F 0427 074B 7621 8FD8 w........'.Kv!.. 00000140: 0208 6737 C823 9B3D 1803 0F07 1C00 2BEC ..g7.#.=......+. 00000150: 805C 0104 0000 B0CB 5ED8 700E 0700 1037 .\......^.p....7 00000160: 050F BD90 4716 07F5 2C00 1061 87B0 906F ....G...,..a...o 00000170: E088 0607 3DFB 0D6C 3712 984C 0F98 5C16 ....=..l7..L..\. 00000180: E4E4 8507 C803 E037 EC20 831C 02A8 A85C .......7. .....\ 00000190: 070E 6121 2F10 024F 041E 59B0 C103 384F ..a!/..O..Y...8O 000001a0: 0720 0082 1DD9 4237 5827 2017 F2C8 0744 . ....B7X' ....D 000001b0: 0004 073B 20FB 53E5 7464 6F50 3760 4216 ...; .S.tdoP7`B. 000001c0: F2C8 660F 07D4 0056 60BB C36F 5137 0600 ..f....V`..oQ7.. 000001d0: 0084 10EC 10DE 526F 8760 180B 7B68 0307 ......Ro.`..{h.. 000001e0: 6700 0000 0000 0090 00FF g.........
然后写入binfo 结构tmp,写入压缩后的PT_LOAD 数据内容
(gdb) p/x tmp $35 = {sz_unc = 0xb58, sz_cpr = 0x3d5, b_method = 0x2, b_ftid = 0x0, b_cto8 = 0x0, b_unused = 0x0}
000001e0: 6700 0000 0000 0090 00FF 580B 0000 D503 g.........X..... 000001f0: 0000 0200 0000 DB7F BBFD 2F6C 6962 3634 ........../lib64 ..... 000005a0: 1F19 9001 19A8 20B0 0119 9001 21B8 9001 ...... .....!... 000005b0: 1990 22C0 230D 8C01 19C8 24FF 0008 C880 ..".#.....$..... 000005c0: 1725 0000 0000 0000 0024 FF .%.......$.
到此地一个PT_LOAD 段压缩完成,计算目前的加壳文件大小为 256+sizeof(b_info)+sizeof(hdr_obuf)+sizeof(b_info)+sizeof(obuf)=256+12+0xde+12+0x3d5=0x5cb.
(gdb) n 444 total_in += ph.u_len; (gdb) n 445 total_out += ph.c_len;
2) 压缩第二个段,为可执行段, 采用该函数进行压缩
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align .... LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000002cf5 0x0000000000002cf5 R E 0x1000 compressWithFilters(ft, OVERHEAD, NULL_cconf, filter_strategy, 0, 0, 0, hdr_ibuf, hdr_u_len); //pakcer.cpp:1603 (gdb) p/x tmp $41 = {sz_unc = 0x2cf5, sz_cpr = 0xe7d, b_method = 0x2, b_ftid = 0x49, b_cto8 = 0x2, b_unused = 0x0}
同样写入binfo结构,写入压缩后的数据
000005c0: 1725 0000 0000 0000 0024 FFF5 2C00 007D .%.......$..,..} 000005d0: 0E00 0002 4902 00BE FDFF FFF3 0F1E FA48 ....I..........H ....... 00001440: 0689 9FC3 0700 80AB 78E7 D901 0000 0000 ........x....... 00001450: 0000 20FF
(gdb) n 444 total_in += ph.u_len; (gdb) n 445 total_out += ph.c_len;
更新total_in,total_out. total_in ,为已经被压缩的长度总和, total_out 为目前压缩完成长度总和
到此a.upx 长度为:0x5cb+12+0xe7d=0x1454:p_lx_elf.cpp:4026
3) 压缩第3个段:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align .... LOAD 0x0000000000004000 0x0000000000004000 0x0000000000004000 0x0000000000000688 0x0000000000000688 R 0x1000 (gdb) p/x tmp $11 = {sz_unc = 0x688, sz_cpr = 0x3cd, b_method = 0x2, b_ftid = 0x0, b_cto8 = 0x0, b_unused = 0x0}
同理该段为只读段,采用压缩方法(void) compress(ibuf, ph.u_len, obuf); 进行压缩,压缩后存储到obuf中,
同上,还是先写入一个binfo结构,然后再写入压缩的数据。
if (ph.c_len < ph.u_len) { (gdb) n 436 fo->write(obuf, ph.c_len); (gdb) n 438 verifyOverlappingDecompression(ft); (gdb) n 444 total_in += ph.u_len; (gdb) n 445 total_out += ph.c_len; (gdb) p total_in $12 = (unsigned int &) @0x7fffffffcf14: 16877 (gdb) p total_out $13 = (unsigned int &) @0x7fffffffcf18: 4912 (gdb) p ph.c_len $14 = 973 (gdb) n 338 for (off_t rest = x.size; 0 != rest; ) { (gdb) p total_out $15 = (unsigned int &) @0x7fffffffcf18: 5885
00001450: 0000 20FF 8806 0000 CD03 0000 0200 0000 .. ............. 00001460: BFFD FFF3 0100 0200 7262 0068 696E 666F ........rb.hinfo ... 000017f0: 2920 8D04 0428 8C05 440E 3086 0648 DAAD ) ...(..D.0..H.. 00001800: 6DFF 0E38 8307 470E 406E 0741 0F02 2842 m..8..G.@n.A..(B 00001810: 1FF8 6865 D902 1810 48D3 3C00 01A3 6B63 ..he....H.<...kc 00001820: 6847 05DF 0000 0000 0000 8004 FF hG...........
a.upx 长度为0x1454+12+0x3cd=0x182D。
4) 对第4个段进行压缩
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align .... LOAD 0x0000000000004c98 0x0000000000005c98 0x0000000000005c98 0x00000000000003c8 0x00000000000003e0 RW 0x1000 (gdb) n 428 fo->write(&tmp, sizeof(tmp)); (gdb) p/x tmp $20 = {sz_unc = 0x3c8, sz_cpr = 0x10e, b_method = 0x2, b_ftid = 0x0, b_cto8 = 0x0, b_unused = 0x0} (gdb) n 429 b_len += sizeof(b_info); (gdb) n 431 if (ft) { (gdb) n 435 if (ph.c_len < ph.u_len) { (gdb) n 436 fo->write(obuf, ph.c_len); (gdb) n 438 verifyOverlappingDecompression(ft); (gdb) n 444 total_in += ph.u_len; (gdb) n 445 total_out += ph.c_len; (gdb) n 338 for (off_t rest = x.size; 0 != rest; ) { (gdb) p total_in $21 = (unsigned int &) @0x7fffffffcf14: 17845 (gdb) p total_out $22 = (unsigned int &) @0x7fffffffcf18: 6155
00001820: 6847 05DF 0000 0000 0000 8004 FFC8 0300 hG.............. 00001830: 000E 0100 0002 0000 0002 39D8 E440 1500 ..........9..@.. ... 00001920: 9041 C0D0 E032 D841 06F0 0012 7F12 0509 .A...2.A........ 00001930: 830C 1212 4FA8 840B EC08 6017 80A7 0000 ....O.....`..... 00001940: 0040 0020 0100 FF .@. ...
a.upx 长度为:0x182D+12+0x10e=0x1947 ,
total_in= 16877+0x3c8=17845
total_out=5885+0x10e=6155
到此PT_LOAD 段压缩完成。
5) sz_pack2a = fpad4(fo); //p_lx_elf.cpp:93 ,该函数其实是为了对齐,目前长度为6471% 4=3,因此 对齐代码如下:
off_t len = fo->st_size(); (gdb) n 95 unsigned d = 3u & (0 - len); (gdb) p len $1 = 6471 (gdb) n 96 unsigned zero = 0; (gdb) p d $2 = 1 (gdb) n 97 fo->write(&zero, d); (gdb) pd Undefined command: "pd". Try "help". (gdb) p d $3 = 1 (gdb) n 98 return d + len; a.upx 内容如下: 00001940: 0040 0020 0100 FF00 .@. ....
需要产长度+1 即可,sz_pack2a=0x1948
loader组成分析
参考 手脱定制版的android SO UPX壳_江西省遂川县杰出青年代表的博客-CSDN博客
loader 初始化位于pack2 comresswithfilter() 函数中,源码调用流程:p_lx_elf.cpp:3989:int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) ,对最大的可执行段 调用:
if (k == nk_f || !is_shlib) { // nk_f 记录的是最长的可执行的PT_LOAD 段,在此判断,并进行压缩。 packExtent(x, total_in, total_out, (k==nk_f ? &ft : 0 ), fo, hdr_u_len);// 传入参数不同,可执行段传入Filter地址,否则传入0,p_unix.cpp:327(gdb 调试 x86_64) }
p_unix.cpp:327 , void PackUnix::packExtent( const Extent &x, unsigned &total_in, unsigned &total_out, Filter *ft, OutputFile *fo, unsigned hdr_u_len)h函数调用compressWithFilters():
if (ft) { // 如果是最大的可执行pt_load 段,在该方法进行压缩。 // compressWithFilters() updates u_adler _inside_ compress(); // that is, AFTER filtering. We want BEFORE filtering, // so that decompression checks the end-to-end checksum. end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler); ft->buf_len = l; // compressWithFilters() requirements? ph.filter = 0; ph.filter_cto = 0; ft->id = 0; ft->cto = 0; compressWithFilters(ft, OVERHEAD, NULL_cconf, filter_strategy, 0, 0, 0, hdr_ibuf, hdr_u_len); //pakcer.cpp:1603 }
Packer::compressWithFilters (this=0x5555557952c0, i_ptr=0x55555579c340 "\363\017\036\372H\203\354\bH\213\005\311O", i_len=11509, o_ptr=0x5555557a2e10 "/lib64/ld-linux-x86-64.so.2", f_ptr=0x55555579c340 "\363\017\036\372H\203\354\bH\213\005\311O", f_len=11509, hdr_ptr=0x0, hdr_len=0, parm_ft=0x7fffffffcfa0, overlap_range=2048, cconf=0x0, filter_strategy=2, inhibit_compression_check=false) at packer.cpp:1390 #1 0x0000555555612ea1 in Packer::compressWithFilters (this=0x5555557952c0, ft=0x7fffffffcfa0, overlap_range=2048, cconf=0x0, filter_strategy=2, filter_off=0, ibuf_off=0, obuf_off=0, hdr_ptr=0x0, hdr_len=0, inhibit_compression_check=false) at packer.cpp:1614 #2 0x00005555555f424b in PackUnix::packExtent (this=0x5555557952c0, x=..., total_in=@0x7fffffffcf14: 3696, total_out=@0x7fffffffcf18: 1203, ft=0x7fffffffcfa0, fo=0x7fffffffd1e0, hdr_u_len=0) at p_unix.cpp:366 #3 0x00005555555c2e5e in PackLinuxElf64::pack2 (this=0x5555557952c0, fo=0x7fffffffd1e0, ft=...) at p_lx_elf.cpp:4055 #4 0x00005555555f3e34 in PackUnix::pack (this=0x5555557952c0, fo=0x7fffffffd1e0) at p_unix.cpp:302 #5 0x000055555560e030 in Packer::doPack (this=0x5555557952c0, fo=0x7fffffffd1e0) at packer.cpp:100 #6 0x0000555555618315 in PackMaster::pack (this=0x7fffffffd2b0, fo=0x7fffffffd1e0) at packmast.cpp:262 #7 0x0000555555635f42 in do_one_file (iname=0x7fffffffe1c3 "a", oname=0x7fffffffd890 "a.upx") at work.cpp:158 #8 0x0000555555636377 in do_files (i=1, argc=2, argv=0x7fffffffdde8) at work.cpp:271 #9 0x00005555555a55a6 in main (argc=2, argv=0x7fffffffdde8) at main.cpp:1539
具体初始化流程如下:
-
使用 stub_amd64_linux_elf_entry 初始化linker ,packer.cpp:1131,初始化流程位于linker.cpp:123,stub_amd64_linux_elf_entry:0x1850(6224)-----9547 文件尾部内容如下:
Sections: Idx Name Size VMA LMA File off Algn Flags 0 ELFMAINX 0000000f 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, RELOC, READONLY 1 NRV_HEAD 00000066 0000000000000000 0000000000000000 0000004f 2**0 CONTENTS, READONLY 2 NRV2E 000000ba 0000000000000000 0000000000000000 000000b5 2**0 CONTENTS, RELOC, READONLY 3 NRV2D 000000a1 0000000000000000 0000000000000000 0000016f 2**0 CONTENTS, RELOC, READONLY 4 NRV2B 00000093 0000000000000000 0000000000000000 00000210 2**0 CONTENTS, RELOC, READONLY 5 LZMA_ELF00 00000064 0000000000000000 0000000000000000 000002a3 2**0 CONTENTS, RELOC, READONLY 6 LZMA_DEC10 000009f7 0000000000000000 0000000000000000 00000307 2**0 CONTENTS, READONLY 7 LZMA_DEC20 000009f7 0000000000000000 0000000000000000 00000cfe 2**0 CONTENTS, READONLY 8 LZMA_DEC30 00000018 0000000000000000 0000000000000000 000016f5 2**0 CONTENTS, READONLY 9 NRV_TAIL 00000000 0000000000000000 0000000000000000 0000170d 2**0 CONTENTS, READONLY 10 ELFMAINY 0000003a 0000000000000000 0000000000000000 0000170d 2**0 CONTENTS, RELOC, READONLY 11 ELFMAINZ 000000ef 0000000000000000 0000000000000000 00001747 2**0 CONTENTS, RELOC, READONLY SYMBOL TABLE: 0000000000000000 l d NRV_HEAD 0000000000000000 NRV_HEAD 0000000000000000 l d LZMA_DEC30 0000000000000000 LZMA_DEC30 0000000000000000 l d ELFMAINY 0000000000000000 ELFMAINY 0000000000000000 l d ELFMAINZ 0000000000000000 ELFMAINZ 0000000000000000 l d ELFMAINX 0000000000000000 ELFMAINX 0000000000000000 l d NRV2E 0000000000000000 NRV2E 0000000000000000 l d NRV2D 0000000000000000 NRV2D 0000000000000000 l d NRV2B 0000000000000000 NRV2B 0000000000000000 l d LZMA_ELF00 0000000000000000 LZMA_ELF00 0000000000000000 l d LZMA_DEC10 0000000000000000 LZMA_DEC10 0000000000000000 l d LZMA_DEC20 0000000000000000 LZMA_DEC20 0000000000000000 l d NRV_TAIL 0000000000000000 NRV_TAIL 0000000000000000 g ELFMAINX 0000000000000000 _start 0000000000000000 *UND* 0000000000000000 O_BINFO RELOCATION RECORDS FOR [ELFMAINX]: OFFSET TYPE VALUE 0000000000000003 R_X86_64_PC32 ELFMAINZ+0x00000000000000d2 RELOCATION RECORDS FOR [NRV2E]: OFFSET TYPE VALUE 00000000000000af R_X86_64_PC32 NRV_HEAD+0x0000000000000021 000000000000005c R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [NRV2D]: OFFSET TYPE VALUE 0000000000000096 R_X86_64_PC32 NRV_HEAD+0x0000000000000021 000000000000005c R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [NRV2B]: OFFSET TYPE VALUE 000000000000008b R_X86_64_PC32 NRV_HEAD+0x0000000000000021 0000000000000053 R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [LZMA_ELF00]: OFFSET TYPE VALUE 0000000000000006 R_X86_64_PC32 LZMA_DEC30+0x0000000000000012 RELOCATION RECORDS FOR [ELFMAINY]: OFFSET TYPE VALUE 0000000000000018 R_X86_64_PC32 ELFMAINZ+0x0000000000000003 RELOCATION RECORDS FOR [ELFMAINZ]: OFFSET TYPE VALUE 00000000000000eb R_X86_64_32 O_BINFO
loader 初始化
代码分析如下:由于pack3()返回的loader内容为linker->output内容,这里重点关注下output赋值操作。
void Packer::initLoader(const void *pdata, int plen, int small) { delete linker; linker = newLinker(); gdb) s PackLinuxElf64amd::newLinker (this=0x1e8500000e7d) at p_lx_elf.cpp:802 802 { (gdb) s 803 return new ElfLinkerAMD64; assert(bele == linker->bele); linker->init(pdata, plen);//linker.cpp:123 pdata= unsigned size; char const * const ident = getIdentstr(&size, small); linker->addSection("IDENTSTR", ident, size, 0); } (gdb) p pdata $15 = (const void *) 0x5555556a8c20 <stub_amd64_linux_elf_entry> (gdb) p plen $16 = 9547 (gdb) n 1136 linker->init(pdata, plen);//linker.cpp:123
-
linker.cpp:123, stub_amd64_linux_elf_entry 数组,二进制数据保存在linker input 中,并且初始化output 长度为0x4000=16384
const upx_byte *pdata = (const upx_byte *) pdata_v; .... inputlen = plen; input = new upx_byte[inputlen + 1]; if (inputlen) memcpy(input, pdata, inputlen);
output = new upx_byte[inputlen ? inputlen : 0x4000]; //output 初始化大小为0x4000 outputlen = 0;
int pos = find(input, inputlen, "Sections:\n", 10); //pos=6624 assert(pos != -1); char *psections = (char *) input + pos; //找到sections 信息开始位置 char *psymbols = strstr(psections, "SYMBOL TABLE:\n");//找到symbols 开始位置 assert(psymbols != NULL); char *prelocs = strstr(psymbols, "RELOCATION RECORDS FOR ");//找到RELOCATION 开始位置。 assert(prelocs != NULL); preprocessSections(psections, psymbols); //linker.cpp:182 preprocessSections(),将位于amd64-linux.elf-entry.h,如上中11个section,添加到sections[]数组中 preprocessSymbols(psymbols, prelocs); //linker.cpp:204(去除注释部分内容),preprocessSymbols(),将symbol 添加到symbols[]数组中 preprocessRelocations(prelocs, (char *) input + inputlen);//preprocessRelocations()同上,初始化relocations[]数组 addLoader("*UND*");
-
void ElfLinker::preprocessSections(char *start, char *end) 代码位于linker.cpp:182, 基本思想为 从psections开始到psymbols结束,将获取到的section信息 添加到linker sections 数组中,并在末尾添加section ABS 及UND section。
for (nsections = 0; start < end; start = 1 + nextl) { .... if (sscanf(start, "%*d %1023s %x %*d %*d %x 2**%d", name, &size, &offset, &align) == 4) { char *n = strstr(start, name); n[strlen(name)] = 0; addSection(n, input + offset, size, align);//linker.cpp:314 ... } } addSection("*ABS*", NULL, 0, 0); addSection("*UND*", NULL, 0, 0);
Sections: Idx Name Size VMA LMA File off Algn Flags 0 ELFMAINX 0000000f 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, RELOC, READONLY 1 NRV_HEAD 00000066 0000000000000000 0000000000000000 0000004f 2**0 CONTENTS, READONLY 2 NRV2E 000000ba 0000000000000000 0000000000000000 000000b5 2**0 CONTENTS, RELOC, READONLY 3 NRV2D 000000a1 0000000000000000 0000000000000000 0000016f 2**0 CONTENTS, RELOC, READONLY 4 NRV2B 00000093 0000000000000000 0000000000000000 00000210 2**0 CONTENTS, RELOC, READONLY 5 LZMA_ELF00 00000064 0000000000000000 0000000000000000 000002a3 2**0 CONTENTS, RELOC, READONLY 6 LZMA_DEC10 000009f7 0000000000000000 0000000000000000 00000307 2**0 CONTENTS, READONLY 7 LZMA_DEC20 000009f7 0000000000000000 0000000000000000 00000cfe 2**0 CONTENTS, READONLY 8 LZMA_DEC30 00000018 0000000000000000 0000000000000000 000016f5 2**0 CONTENTS, READONLY 9 NRV_TAIL 00000000 0000000000000000 0000000000000000 0000170d 2**0 CONTENTS, READONLY 10 ELFMAINY 0000003a 0000000000000000 0000000000000000 0000170d 2**0 CONTENTS, RELOC, READONLY 11 ELFMAINZ 000000ef 0000000000000000 0000000000000000 00001747 2**0 CONTENTS, RELOC, READONLY
sec 共初始化了14个section ,包含ABS ,UND section 信息。
-
同理处理ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section, upx_uint64_t offset) 代码位于linker.cpp:330 ,将获取到的symbols 信息添加到linker 数组symbols中
SYMBOL TABLE: 0000000000000000 l d NRV_HEAD 0000000000000000 NRV_HEAD 0000000000000000 l d LZMA_DEC30 0000000000000000 LZMA_DEC30 0000000000000000 l d ELFMAINY 0000000000000000 ELFMAINY 0000000000000000 l d ELFMAINZ 0000000000000000 ELFMAINZ 0000000000000000 l d ELFMAINX 0000000000000000 ELFMAINX 0000000000000000 l d NRV2E 0000000000000000 NRV2E 0000000000000000 l d NRV2D 0000000000000000 NRV2D 0000000000000000 l d NRV2B 0000000000000000 NRV2B 0000000000000000 l d LZMA_ELF00 0000000000000000 LZMA_ELF00 0000000000000000 l d LZMA_DEC10 0000000000000000 LZMA_DEC10 0000000000000000 l d LZMA_DEC20 0000000000000000 LZMA_DEC20 0000000000000000 l d NRV_TAIL 0000000000000000 NRV_TAIL 0000000000000000 g ELFMAINX 0000000000000000 _start 0000000000000000 *UND* 0000000000000000 O_BINFO
共有14个symbol ,其中最后一列为 symbol 的名称,第4列为symbol对应的section 名称。
for (nsymbols = 0; start < end; start = 1 + nextl) { .... else if (sscanf(start, "%x%*c%*c%*c%*c%*c%*c%*c%*c %1023s %*x %1023s", #endif &offset, section, symbol) == 3) { char *s = strstr(start, symbol); s[strlen(symbol)] = 0; if (strcmp(section, "*UND*") == 0) offset = 0xdeaddead; assert(strcmp(section, "*ABS*") != 0); addSymbol(s, section, offset); }
在 symbol 名称为O_BINFO 时,section 名称为" *UND* ", offset=0xdeaddead,其他symbol offset 为0
-
对linker relocations 数组初始化,
RELOCATION RECORDS FOR [ELFMAINX]: OFFSET TYPE VALUE 0000000000000003 R_X86_64_PC32 ELFMAINZ+0x00000000000000d2 RELOCATION RECORDS FOR [NRV2E]: OFFSET TYPE VALUE 00000000000000af R_X86_64_PC32 NRV_HEAD+0x0000000000000021 000000000000005c R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [NRV2D]: OFFSET TYPE VALUE 0000000000000096 R_X86_64_PC32 NRV_HEAD+0x0000000000000021 000000000000005c R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [NRV2B]: OFFSET TYPE VALUE 000000000000008b R_X86_64_PC32 NRV_HEAD+0x0000000000000021 0000000000000053 R_X86_64_PC32 ELFMAINY+0xfffffffffffffffc RELOCATION RECORDS FOR [LZMA_ELF00]: OFFSET TYPE VALUE 0000000000000006 R_X86_64_PC32 LZMA_DEC30+0x0000000000000012 RELOCATION RECORDS FOR [ELFMAINY]: OFFSET TYPE VALUE 0000000000000018 R_X86_64_PC32 ELFMAINZ+0x0000000000000003 RELOCATION RECORDS FOR [ELFMAINZ]: OFFSET TYPE VALUE 00000000000000eb R_X86_64_32 O_BINFO
if (sscanf(start, "RELOCATION RECORDS FOR [%[^]]", sect) == 1) section = findSection(sect); .... if (sscanf(start, "%x %99s %1023s", &offset, type, symbol) == 3) { char *t = strstr(start, type); t[strlen(type)] = 0; ..... if (section) { addRelocation(section->name, offset, t, symbol, add); // printf("relocation %s %s %x %llu preprocessed\n", section->name, symbol, offset, // (unsigned long long) add); } ... }
以第一个reloc 为例: section->name="ELFMAINX" ,offset=3,t="R_X86_64_PC32",add=d2 ,symbol="ELFMAINZ"
ElfLinker::Relocation *ElfLinker::addRelocation(const char *section, unsigned off, const char *type, const char *symbol, upx_uint64_t add) { if (update_capacity(nrelocations, &nrelocations_capacity)) relocations = static_cast<Relocation **>( realloc(relocations, (nrelocations_capacity) * sizeof(Relocation *))); assert(relocations != NULL); Relocation *rel = new Relocation(findSection(section), off, type, findSymbol(symbol), add); relocations[nrelocations++] = rel; return rel; }
最后一个reloction ,add 为0
RELOCATION RECORDS FOR [ELFMAINZ]: OFFSET TYPE VALUE 00000000000000eb R_X86_64_32 O_BINFO symbol="O_BINFO",不包含+0x 也不包含-0x char *p = strstr(symbol, "+0x"); if (p == NULL) p = strstr(symbol, "-0x"); if (p) { char sign = *p; *p = 0; // terminate the symbol name p += 3; assert(strlen(p) == 8 || strlen(p) == 16); #if (ACC_CC_MSC && (_MSC_VER < 1800)) unsigned a = 0, b = 0; if (sscanf(p, "%08x%08x", &a, &b) == 2) add = ((upx_uint64_t) a << 32) | b; else add = a; #else char *endptr = NULL; add = strtoull(p, &endptr, 16); assert(endptr && *endptr == '\0'); #endif if (sign == '-') add = 0 - add; } //
共初始化10个relocation。到此loader 初始化完成。
lodar 组成内容
返回到linker.cpp 178
addLoader("*UND*"); // 该section 为upx 最后添加的size=0,该初始化只是初始化了linker->head=linker->tail 指向了该section,output内容没有改变 outputlen=0。
返回到packer.cpp:1139
char const * const ident = getIdentstr(&size, small); //添加版权信息,作为IDENTSTR section。packer.cpp:1054 linker->addSection("IDENTSTR", ident, size, 0); //长度129
p_lx_elf.cpp:1206:
struct b_info h; memset(&h, 0, sizeof(h)); unsigned fold_hdrlen = 0; cprElfHdr1 const *const hf = (cprElfHdr1 const *)fold; fold_hdrlen = umax(0x80, sizeof(hf->ehdr) + get_te16(&hf->ehdr.e_phentsize) * get_te16(&hf->ehdr.e_phnum) + sizeof(l_info) ); h.sz_unc = ((szfold < fold_hdrlen) ? 0 : (szfold - fold_hdrlen));// fold_hdrlen=188,hf->ehdr.e_phentsize=56,hf->ehdr.e_phnum=2,sizeof(l_info)=12 h.b_method = (unsigned char) ph.method; // h {sz_unc = 2063, sz_cpr = 2576, b_method = 2 '\002', b_ftid = 73 'I', h.b_ftid = (unsigned char) ph.filter; // b_cto8 = 2 '\002', b_unused = 0 '\000'} h.b_cto8 = (unsigned char) ph.filter_cto;// unsigned char const *const uncLoader = fold_hdrlen + fold;// h.sz_cpr = MemBuffer::getSizeForCompression(h.sz_unc + (0==h.sz_unc)); //h.sz_unc=2063 h.sz_cpr长度为2063+2063/8+256=2576 unsigned char *const cprLoader = New(unsigned char, sizeof(h) + h.sz_cpr); //compress.cpp:76 { unsigned h_sz_cpr = h.sz_cpr; int r = upx_compress(uncLoader, h.sz_unc, sizeof(h) + cprLoader, &h_sz_cpr, NULL, ph.method, 10, NULL, NULL );//compress:76,对uncLoader 进行压缩,压缩后长度为h_sz_cpr,1644.压缩后内容存储在sizeof(h) + cprLoader 位置。 h.sz_cpr = h_sz_cpr; if (r != UPX_E_OK || h.sz_cpr >= h.sz_unc) throwInternalError("loader compression failed"); } #if 0 //{ debugging only if (M_IS_LZMA(ph.method)) { ucl_uint tmp_len = h.sz_unc; // LZMA uses this as EOF unsigned char *tmp = New(unsigned char, tmp_len); memset(tmp, 0, tmp_len); int r = upx_decompress(sizeof(h) + cprLoader, h.sz_cpr, tmp, &tmp_len, h.b_method, NULL); if (r == UPX_E_OUT_OF_MEMORY) throwOutOfMemoryException(); printf("\n%d %d: %d %d %d\n", h.b_method, r, h.sz_cpr, h.sz_unc, tmp_len); for (unsigned j=0; j < h.sz_unc; ++j) if (tmp[j]!=uncLoader[j]) { printf("%d: %x %x\n", j, tmp[j], uncLoader[j]); } delete[] tmp; } #endif //} unsigned const sz_cpr = h.sz_cpr; set_te32(&h.sz_cpr, h.sz_cpr); set_te32(&h.sz_unc, h.sz_unc); memcpy(cprLoader, &h, sizeof(h));//cprLoader 内容为 h 内容+uncLoader 压缩后内容。// h{sz_unc = 2063, sz_cpr = 1644, b_method = 2 '\002', b_ftid = 73 'I', //b_cto8 = 2 '\002', b_unused = 0 '\000'} // This adds the definition to the "library", to be used later. linker->addSection("FOLDEXEC", cprLoader, sizeof(h) + sz_cpr, 0);//linker.cpp:314 ,长度为 1644+12=1656 delete [] cprLoader;
以上代码大致意思为 uncloader 内容为 读取 stub_amd64_linux_elf_fold+188 以后内容,长度为2251-188=2063 。binfo+对uncloader 进行压缩,作为cprloader 内容,并且将 cprloader 内容作为 FOLDEXEC section
p_lx_elf.cpp:1257
addStubEntrySections(ft);// p_lx_elf.cpp:625
#0 ElfLinker::addLoader (this=0x7fffffffc050, sname=0x0) at linker.cpp:363 #1 0x00005555555a0267 in ElfLinker::addLoader (this=0x5555557945a0, s=0x55555565b94f "ELFMAINX", ap=0x7fffffffbfe0) at linker.cpp:424 #2 0x0000555555611215 in Packer::addLoaderVA (this=0x5555557952c0, s=0x55555565b94f "ELFMAINX") at packer.cpp:1173 #3 0x0000555555610e72 in Packer::addLoader (this=0x5555557952c0, a=0x55555565b94f "ELFMAINX", b=0x0) at packer.cpp:1149 #4 0x00005555555b4b2a in PackLinuxElf::addStubEntrySections (this=0x5555557952c0) at p_lx_elf.cpp:626
"ELFMAINX” ,将section->input 内容添加到output中长度为15.
memcpy(output + outputlen, section->input, section->size); section->output = output + outputlen;
同理依次添加
NRV_HEAD,NRV2B,NRV_TAIL
outputlen=0xf+0x66+0x93+0=0x108
addLoader("ELFMAINY,IDENTSTR", NULL); ELFMAINY,IDENTSTR
outputlen=0x108+0x3a+129=0x1c3
"+40,ELFMAINZ"
if (sect[0] == '+') // alignment { assert(tail); unsigned l = hex(sect[2]) - tail->offset - tail->size;// unsigned m = hex(sect[1]); if (m) { l %= hex(sect[1]); //+40 , m=4 ,l=1 } if (l) { if (sect[3] == 'D') alignData(l); else alignCode(l); tail->size += l; } }
linker.h:101 virtual void alignCode(unsigned len) { alignWithByte(len, 0); } linker.cpp:539: void ElfLinker::alignWithByte(unsigned len, unsigned char b) { memset(output + outputlen, b, len); outputlen += len; }
"+40" 会对output 内容修改,并增加outplen长度。
outputlen=0x1c3+1+0xef=0x2b3
addLoader("FOLDEXEC", NULL);
outputlen=0x2b3+1656=2347 ,即loader大小。
loader组成部分分析完成。p_lx_elf.cpp:1259
pack3() :
源码位于p_unix.cpp:302,
PackUnix::pack (this=0x5555557952c0, fo=0x7fffffffd1e0) at p_unix.cpp:302 302 if (pack2(fo, ft)) { (gdb) n 309 pack3(fo, ft); // append loader (gdb) s PackLinuxElf64::pack3 (this=0xc0000000d, fo=0x7fffffffcee0, ft=...) at p_lx_elf.cpp:492 492 { (gdb) n 493 off_t flen = super::pack3(fo, ft); // loader follows compressed PT_LOADs (gdb) s PackLinuxElf::pack3 (this=0x555555795b50, fo=0x10, ft=...) at p_lx_elf.cpp:324 324 { (gdb) n 326 unsigned const zero = 0;
unsigned const t = (4 & len) ^ ((!!xct_off)<<2); // 0 or 4 fo->write(&zero, t); // t=0 len += t; // force sz_pack2 (0 mod 8) [see below] len=0x1948 set_te32(&disp, sz_elf_hdrs + sizeof(p_info) + sizeof(l_info) + (!!xct_off & !!opt->o_unix.android_shlib)); // |1 iff android shlib,sz_elf_hdrs=232,sizeof(p_info) =12, fo->write(&disp, sizeof(disp)); // offset(b_info) sizeof(l_info)=12 ,disp=256 len += sizeof(disp); // len=len+4 ==len=0x1948+4=0x194C set_te32(&disp, len); // distance back to beginning (detect dynamic reloc)elf文件头部到目前位置的长度 fo->write(&disp, sizeof(disp)); //写入当前长度0x194C len += sizeof(disp); // len=0x194C+4=0x1950
00001930: 830C 1212 4FA8 840B EC08 6017 80A7 0000 ....O.....`..... 00001940: 0040 0020 0100 FF00 0001 0000 4C19 0000 .@. ........L...
到此,目前a.upx 长度为 0x1950=6480
-
添加loader: loader 内容为 为pack2()对可执行的pT_LOAD对进行压缩时 在函数comptesswithfilter(),根据stub/amd64_linux.elf-entry.h 及md64_linux.elf-fold.h 内容初始化的lincker及loader ,返回的linker->output大小。大小为2347
-
对PT_LOAD gap(相邻两个PT_LOAD 段直接的间隙,即 ptload2->offset -(ptload1->offset+ptload1->fsize)大小进行压缩,压缩时,写入一个binfo 结构,然后写入压缩数据 ; binfo 内容如下:{sz_unc = 400, sz_cpr = 14, b_method = 2 '\002', b_ftid = 0 '\000', b_cto8 = 0 '\000', b_unused = 0 '\000'} ,sz_unc 压缩前的长度,sz_cpr 压缩后的长度,method 压缩才采用的方法。
00002270: 4A00 0040 8447 2001 0000 FF00 9001 0000 J..@.G ......... 00002280: 0E00 0000 0200 0000 0084 0292 0000 0000 ................ 00002290: 0000 0000 12FF 0B03 0000 0E00 0000 0200 ................ 000022a0: 0000 002B 0092 0000 0000 0000 0080 04FF ...+............ 000022b0: 1006 0000 0E00 0000 0200 0000 4028 0092 ............@(.. 000022c0: 0000 0000 0000 0020 01FF
0000227C: 9001 0000 .... //前12位 0190 压缩前长度 400,压缩后长度000E 14 ,使用method: 02, 00002280: 0E00 0000 0200 0000 0084 0292 0000 0000 ................ 00002290: 0000 0000 12FF //后边14位为压缩后数据内容。
00002296: 0B03 0000 0E00 0000 0200 ................ //分析同上,压缩前长度030B,大小为779,压缩后为000E 为14 000022a0: 0000 002B 0092 0000 0000 0000 0080 04FF ...+............
000022b0: 1006 0000 0E00 0000 0200 0000 4028 0092 ............@(.. //同上,压缩前长度为0X0610,1552,压缩后为000E为14 000022c0: 0000 0000 0000 0020 01FF
000022ca: 481A 0000 7F08 ....... ..H..... // 压缩前长度0x1a48=6728,压缩后长度为087F 为:2175,binfo之后为压缩数据内容。 000022d0: 0000 0200 0000
到此PT_LOAD gap压缩完成,总长度为0x22d5+0x087F=0x2B54 。
-
写入UPX_MAGIC_LE32 内容。
-
对elfout 即 两个programheader 内容的赋值操作,并返回a.upx ,output 文件大小。elfout 为当前写回的文件头部内容。
{ehdr = {e_ident = {0x7f, 0x45, 0x4c, 0x46, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, e_type = {d = {0x3, 0x0}}, e_machine = {d = {0x3e,0x0}}, e_version = {d = {0x1, 0x0, 0x0, 0x0}}, e_entry = {d = {0x50, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_phoff = {d = {0x40, 0x0, 0x0, 0x0, 0x0, 0x0,0x0, 0x0}}, e_shoff = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_flags = {d = {0x0, 0x0, 0x0, 0x0}}, e_ehsize = {d = {0x40, 0x0}}, e_phentsize = {d = {0x38, 0x0}}, e_phnum = {d = {0x3, 0x0}}, e_shentsize = {d = {0x40, 0x0}}, e_shnum = {d = {0x0, 0x0}}, e_shstrndx = {d = {0x0, 0x0}}}, phdr = {{ p_type = {d = {0x1, 0x0, 0x0, 0x0}}, p_flags = {d = {0x5, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0,0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x7b, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x7b, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x1, 0x0, 0x0, 0x0}}, p_flags = {d = {0x6, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x30, 0x0, 0x0,0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = { d = {0x78, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x51, 0xe5, 0x74, 0x64}}, p_flags = {d = {0x6, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x0, 0x0, 0x0, 0x0}}, p_flags = {d = {0x0, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}}, linfo = {l_checksum = {d = {0x0, 0x0, 0x0, 0x0}}, l_magic = {d = {0x0, 0x0, 0x0, 0x0}}, l_lsize = {d = {0x0, 0x0}}, l_version = 0x0, l_format = 0x0}}
phdr[0] 长度为 6480+2347 大小==0x227B,对齐大小为4096=0x1000
phdr[1] vaddr= (0x227B+0xfff) & (0xf000)=0x3000
到此,a.upx 长度为11108=0x2B64,接下来分析 pack4 源码了。 p_unix.cpp:311
pack4():
ph = {version = 13, format = 22, method = 2, level = 8, u_len = 6728, c_len = 2175, u_adler = 3755430340, c_adler = 1167461060, u_file_size = 27304, filter = 73, filter_cto = 2, n_mru = 0, header_checksum = 0, saved_u_adler = 2139826101, saved_c_adler = 2878392669, buf_offset = 0, compress_result = {method = 2, level = 8, u_len = 6728, c_len = 2175, result_lzma = {pos_bits = 0, lit_pos_bits = 0, lit_context_bits = 0, dict_size = 0, fast_mode = 0, num_fast_bytes = 0, match_finder_cycles = 0, num_probs = 0}, result_ucl = {result = {4294967295, 5064, 4294967295, 69, 4294967295, 21, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, result_zlib = {dummy = 0}}, max_offset_found = 5064, max_match_found = 69, max_run_found = 21, first_offset_found = 1, overlap_overhead = 2048}
### 根据ph 内容生成 buf 内容,即packheader 内容,具体生成算法见packhead.cpp:92 size = 32; old_chksum = get_packheader_checksum(p, size - 1); //自定义计算方法,去除前4位,后续各位相加总和 % 251。 set_le32(p + 16, u_len);//0x1a68 set_le32(p + 20, c_len); //0x78f set_le32(p + 24, u_file_size); //0x6aa8 p[28] = (unsigned char) filter; //73=0x49 p[29] = (unsigned char) filter_cto;//2=0x2 assert(n_mru == 0 || (n_mru >= 2 && n_mru <= 256)); p[30] = (unsigned char) (n_mru ? n_mru - 1 : 0);//0 } set_le32(p + 8, u_adler);//0xdfd751c4 set_le32(p + 12, c_adler);//0x45960ac4 .......... p[4] = (unsigned char) version; //{version = 13, format = 22, method = 2,level = 8} p[5] = (unsigned char) format; p[6] = (unsigned char) method; p[7] = (unsigned char) level; p[size - 1] = get_packheader_checksum(p, size - 1);//自定义计算方法,去除前4位,后续各位相加总和 % 251。
buf 内容如下: {0x4d, 0x50, 0x50, 0x43, 0xd, 0x16, 0x2, 0x8, 0xc4, 0x51, 0xd7, 0xdf, 0xc4, 0xa, 0x96, 0x45, 0x48, 0x1a, 0x0, 0x0, 0x7f, 0x8, 0x0, 0x0, 0xa8, 0x6a, 0x0, 0x0, 0x49, 0x2, 0x0, 0xa}
p_lx_elf.cpp:4285: 定位到文件头部,对elfheader进行处理,根据elfout内容对a.upx
``` p/x elfout {ehdr = {e_ident = {0x7f, 0x45, 0x4c, 0x46, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, e_type = {d = {0x3, 0x0}}, e_machine = {d = {0x3e,0x0}}, e_version = {d = {0x1, 0x0, 0x0, 0x0}}, e_entry = {d = {0x50, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_phoff = {d = {0x40, 0x0, 0x0, 0x0, 0x0, 0x0,0x0, 0x0}}, e_shoff = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, e_flags = {d = {0x0, 0x0, 0x0, 0x0}}, e_ehsize = {d = {0x40, 0x0}}, e_phentsize = {d = {0x38, 0x0}}, e_phnum = {d = {0x3, 0x0}}, e_shentsize = {d = {0x40, 0x0}}, e_shnum = {d = {0x0, 0x0}}, e_shstrndx = {d = {0x0, 0x0}}}, phdr = {{ p_type = {d = {0x1, 0x0, 0x0, 0x0}}, p_flags = {d = {0x5, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x7b, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0,0x0}}, p_memsz = {d = {0x7b, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x1,0x0, 0x0, 0x0}}, p_flags = {d = {0x6, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x30, 0x0, 0x0,0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x78, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x51, 0xe5, 0x74, 0x64}},p_flags = {d = {0x6, 0x0, 0x0, 0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x0, 0x0, 0x0, 0x0,0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}, {p_type = {d = {0x0, 0x0, 0x0, 0x0}}, p_flags = {d = {0x0, 0x0, 0x0,0x0}}, p_offset = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_vaddr = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_paddr = {d = {0x0, 0x0,0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_filesz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_memsz = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, p_align = {d = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}}, linfo = {l_checksum = {d = {0x0, 0x0, 0x0, 0x0}}, l_magic = {d = {0x0, 0x0, 0x0, 0x0}}, l_lsize = {d = {0x0, 0x0}}, l_version = 0x0, l_format = 0x0}} ``` 同时写入linfo : 赋值在p_lx_elf.cpp:323 PackLinuxElf::pack3: linfo ``` {l_checksum = {d = {0x88, 0x31, 0x4a, 0x68}}, l_magic = {d = {0x4d, 0x50, 0x50, 0x43}}, l_lsize = {d = {0x34, 0x9}}, l_version = 0xd, l_format = 0x16} ``` 修改前a.upx 内容 ``` 00000000: 7F45 4C46 0201 0100 0000 0000 0000 0000 .ELF............ 00000010: 0300 3E00 0100 0000 BC00 1000 0000 0000 ..>............. 00000020: 4000 0000 0000 0000 0000 0000 0000 0000 @............... 00000030: 0000 0000 4000 3800 0300 4000 0000 0000 ....@.8...@..... 00000040: 0100 0000 0500 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: BC00 0000 0000 0000 BC00 0000 0000 0000 ................ 00000070: 0010 0000 0000 0000 0100 0000 0600 0000 ................ 00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000a0: 7860 0000 0000 0000 0010 0000 0000 0000 x`.............. 000000b0: 51E5 7464 0600 0000 0000 0000 0000 0000 Q.td............ 000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000e0: 1000 0000 0000 0000 0000 0000 0000 0000 ................ 000000f0: 0000 0000 0000 0000 ``` 修改后,a.upx 内容如下: ``` 00000000: 7F45 4C46 0201 0100 0000 0000 0000 0000 .ELF............ 00000010: 0300 3E00 0100 0000 5019 0000 0000 0000 ..>.....P....... 00000020: 4000 0000 0000 0000 0000 0000 0000 0000 @............... 00000030: 0000 0000 4000 3800 0300 4000 0000 0000 ....@.8...@..... 00000040: 0100 0000 0500 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: 7B22 0000 0000 0000 7B22 0000 0000 0000 {"......{"...... 00000070: 0010 0000 0000 0000 0100 0000 0600 0000 ................ 00000080: 0000 0000 0000 0000 0030 0000 0000 0000 .........0...... 00000090: 0030 0000 0000 0000 0000 0000 0000 0000 .0.............. 000000a0: 7830 0000 0000 0000 0010 0000 0000 0000 x0.............. 000000b0: 51E5 7464 0600 0000 0000 0000 0000 0000 Q.td............ 000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000000e0: 1000 0000 0000 0000 8831 4A68 4D50 5043 .........1JhMPPC 000000f0: 3409 0D16 ```