upx入门学习笔记

一、开始

         Upx是一款开源免费的跨平台的压缩壳,因此作为学习目的还是很不错的,具体upx的介绍可以查看其官网(链接见参考),通过学习upx更好的了解elf文件的加壳思路和方式,另外本文也只是对学习过程做一个记录,如有疏漏或错误之处,请多指教,学习交流。

二、分析点

         主要分析32位linux下可执行文件的加壳方式,目的是分析清楚upx的加壳流程和加壳思路

三、源码分析

         1.首先可以clone源码自行编译,或者下载已经编译好的可执行文件

                

          2.upx编译

                   编译需要lzma-sdk,下载上一步的代码已经包含,接下来需要安装ucl库,下载链接如下,http://cpgs.fdcservers.net/ubuntu/ubuntu/pool/universe/u/ucl/

                  

                  解压后执行./configure,生成makefile文件,然后执行make进行编译,最后执行make install进行安装即可,再将ucl-1.0.3下的include/ucl文件夹复制到upx/src中即可,最后再将upx/stub/scripts/check_whitespace.sh文件中的代码注释掉

                 

                 接下来在upx源码目录下使用make all 命令进行upx编译,最后生成src/upx.out如下所示。

                

                3.测试使用,观察文件执行前后的变化,首先准备一个简单的demo程序,编译生成elf可执行程序。

#include<stdio.h>

#include<time.h>

void getNowTime()

{

         time_t now;

         time(&now);

         printf("now time is %d\n", now);

}

int main()

{

    getNowTime();

    return 0;

}

                  再使用upx -3 demo选择快速方式加壳,提示错误如下

                  

                   由于压缩的demo文件太小,小于40kb,不能被压缩,采用静态方式编译即可。

                  

                  使用upx -1 demo 成功压缩

                  

                   使用readelf –hlS demo分别查看对应的文件信息

                   

                   压缩前文件信息如下所示:

ELF Header:

  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00

  Class:                             ELF32

  Data:                              2's complement, little endian

  Version:                           1 (current)

  OS/ABI:                            UNIX - GNU

  ABI Version:                       0

  Type:                              EXEC (Executable file)

  Machine:                           Intel 80386

  Version:                           0x1

  Entry point address:               0x8048736

  Start of program headers:          52 (bytes into file)

  Start of section headers:          727032 (bytes into file)

  Flags:                             0x0

  Size of this header:               52 (bytes)

  Size of program headers:           32 (bytes)

  Number of program headers:         6

  Size of section headers:           40 (bytes)

  Number of section headers:         33

  Section header string table index: 30

 

Section Headers:

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .note.ABI-tag     NOTE            080480f4 0000f4 000020 00   A  0   0  4

  [ 2] .note.gnu.build-i NOTE            08048114 000114 000024 00   A  0   0  4

  [ 3] .rel.plt          REL             08048138 000138 000070 08  AI  0  24  4

  [ 4] .init             PROGBITS        080481a8 0001a8 000023 00  AX  0   0  4

  [ 5] .plt              PROGBITS        080481d0 0001d0 0000e0 00  AX  0   0 16

  [ 6] .text             PROGBITS        080482b0 0002b0 072a4c 00  AX  0   0 16

  [ 7] __libc_freeres_fn PROGBITS        080bad00 072d00 000abd 00  AX  0   0 16

  [ 8] __libc_thread_fre PROGBITS        080bb7c0 0737c0 0000a5 00  AX  0   0 16

  [ 9] .fini             PROGBITS        080bb868 073868 000014 00  AX  0   0  4

  [10] .rodata           PROGBITS        080bb880 073880 01a90c 00   A  0   0 32

  [11] __libc_subfreeres PROGBITS        080d618c 08e18c 000028 00   A  0   0  4

  [12] .stapsdt.base     PROGBITS        080d61b4 08e1b4 000001 00   A  0   0  1

  [13] __libc_atexit     PROGBITS        080d61b8 08e1b8 000004 00   A  0   0  4

  [14] __libc_thread_sub PROGBITS        080d61bc 08e1bc 000004 00   A  0   0  4

  [15] .eh_frame         PROGBITS        080d61c0 08e1c0 012ac4 00   A  0   0  4

  [16] .gcc_except_table PROGBITS        080e8c84 0a0c84 0000a1 00   A  0   0  1

  [17] .tdata            PROGBITS        080e9f5c 0a0f5c 000010 00 WAT  0   0  4

  [18] .tbss             NOBITS          080e9f6c 0a0f6c 000018 00 WAT  0   0  4

  [19] .init_array       INIT_ARRAY      080e9f6c 0a0f6c 000008 00  WA  0   0  4

  [20] .fini_array       FINI_ARRAY      080e9f74 0a0f74 000008 00  WA  0   0  4

  [21] .jcr              PROGBITS        080e9f7c 0a0f7c 000004 00  WA  0   0  4

  [22] .data.rel.ro      PROGBITS        080e9f80 0a0f80 000070 00  WA  0   0 32

  [23] .got              PROGBITS        080e9ff0 0a0ff0 000008 04  WA  0   0  4

  [24] .got.plt          PROGBITS        080ea000 0a1000 000044 04  WA  0   0  4

  [25] .data             PROGBITS        080ea060 0a1060 000f20 00  WA  0   0 32

  [26] .bss              NOBITS          080eaf80 0a1f80 000e0c 00  WA  0   0 32

  [27] __libc_freeres_pt NOBITS          080ebd8c 0a1f80 000018 00  WA  0   0  4

  [28] .comment          PROGBITS        00000000 0a1f80 000034 01  MS  0   0  1

  [29] .note.stapsdt     NOTE            00000000 0a1fb4 000b54 00      0   0  4

  [30] .shstrtab         STRTAB          00000000 0b168f 000168 00      0   0  1

  [31] .symtab           SYMTAB          00000000 0a2b08 007ea0 10     32 849  4

  [32] .strtab           STRTAB          00000000 0aa9a8 006ce7 00      0   0  1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings)

  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)

  O (extra OS processing required) o (OS specific), p (processor specific)

 

Program Headers:

  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align

  LOAD           0x000000 0x08048000 0x08048000 0xa0d25 0xa0d25 R E 0x1000

  LOAD           0x0a0f5c 0x080e9f5c 0x080e9f5c 0x01024 0x01e48 RW  0x1000

  NOTE           0x0000f4 0x080480f4 0x080480f4 0x00044 0x00044 R   0x4

  TLS            0x0a0f5c 0x080e9f5c 0x080e9f5c 0x00010 0x00028 R   0x4

  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

  GNU_RELRO      0x0a0f5c 0x080e9f5c 0x080e9f5c 0x000a4 0x000a4 R   0x1

 

 Section to Segment mapping:

  Segment Sections...

   00     .note.ABI-tag .note.gnu.build-id .rel.plt .init .plt .text __libc_freeres_fn __libc_thread_freeres_fn .fini .rodata __libc_subfreeres .stapsdt.base __libc_atexit __libc_thread_subfreeres .eh_frame .gcc_except_table

   01     .tdata .init_array .fini_array .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs

   02     .note.ABI-tag .note.gnu.build-id

   03     .tdata .tbss

   04    

   05     .tdata .init_array .fini_array .jcr .data.rel.ro .got

 

          压缩后文件信息如下所示:

ELF Header:

  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00

  Class:                             ELF32

  Data:                              2's complement, little endian

  Version:                           1 (current)

  OS/ABI:                            UNIX - GNU

  ABI Version:                       0

  Type:                              EXEC (Executable file)

  Machine:                           Intel 80386

  Version:                           0x1

  Entry point address:               0xc4fd20

  Start of program headers:          52 (bytes into file)

  Start of section headers:          0 (bytes into file)

  Flags:                             0x0

  Size of this header:               52 (bytes)

  Size of program headers:           32 (bytes)

  Number of program headers:         3

  Size of section headers:           40 (bytes)

  Number of section headers:         0

  Section header string table index: 0

 

There are no sections in this file.

 

Program Headers:

  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align

  LOAD           0x000000 0x00c01000 0x00c01000 0x4f529 0x4f529 R E 0x1000

  LOAD           0x000da4 0x080ebda4 0x080ebda4 0x00000 0x00000 RW  0x1000

  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

            通过对压缩前后文件信息的对比发现,upx压缩之后会对头部信息Start of section headersNumber of section headersSection header string table index进行擦除,对section进行擦除,程序头的NOTETLSGNU_RELRO进行擦除,LOAD段的基地址被修改。头部的相关信息Entry point addressNumber of program headers被修改。

 

四、流程

        首先从main函数出发,跟踪函数调用流程,定位调用的压缩函数。

        

           在main函数中开始调用do_files()函数,继续跟进,

           

            其中do_files()会调用do_one_file(),继续跟进

       

           当命令参数为压缩命令时调用pm.pack(),继续跟进,

           

             其中通过getPacker()函数确定加壳文件类型,再调用doPack(),

            

            该函数为加壳函数,在Packer类中定义为纯虚函数,因此需要定位其具体的实现类,主要关注elf文件的加壳,如下所示

            

            因此接下来需要关注该加壳函数在PackUnix类中的实现。继续跟进,如下所示,

           

           具体的实现过程为对应的如下虚函数pack1(),pack2(),pack3(),pack4()

        

           同样定位实现类,对应的继承关系如下所示,

          

        此处主要针对32位elf可执行文件进行分析,实现类为PackLinuxElf32和PackLinuxElf32x86,PackLinuxElf32对函数pack1~4()进行了重写,针对不同的架构PackLinuxElf32x86对pack1()进行了重写。

        PackLinuxElf32x86::Pack1函数如下。

        

          调用generateElfHdr()函数生成elf头

       

        其中将elf Header中的e_shoff,e_shnum,e_shstrndx置0。接下来分析PackLinuxElf32::pack2()追加压缩数据

        

        其中主要是对文件大小做判断,小于0x40的不进行压缩,抛出NotCompressible异常。对数据进行压缩调用packExtent()函数。

         接下来分析PackLinuxElf32::pack3()追加loader

        

        加载压缩PT_LOADs,压缩调试信息,调用packExtent()。

     

      对PT_LOAD进行修改,设置各个字段的值。

        

        计算新的偏移的值。修改DT_INIT,修改inputfile中的e_shoff,e_shnum,e_shstrndx为0。

        接下来分析PackLinuxElf32::pack4()添加壳的头部

          

            将shstrtab等信息写入输出文件

     

     如果是非共享库则打开文件校验section头部和程序头部,复制旧的section头部,最后更新.e_shstrndx。

         

          调用父类的pack4(),写入packHeader和overlay_offset。

五、总结

        通过对源码的分析了解到upx对一个32位的可执行文件进行了哪些修改和压缩,在遇到类似upx的加壳时能通过相关特征进行判断,也可以对upx进行定制来对抗脱壳。

        其中pack()函数为加壳函数,每一步分别调用pack1()进行头部的处理,对头部的section信息进行擦出,pack2()对数据段进行压缩,pack3()修改LOAD段,pack4()则添加最后的壳的头部,并修改壳的section段。

六、参考

         https://github.com/upx/upx

         https://upx.github.io/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值