UPX壳分析

面试的时候问到一些问题,之前一直没接触过,这几天在学校安排的培训空闲时间,用upx压缩一个exe来分析脱壳行为

UPX 3.94w+x64dbg

工具:Source Insight

目录文件夹结构如下
\UPX-3.08-SRC
├─doc
└─src
├─filter
└─stub
├─scripts
├─src
│ ├─arch
│ │ ├─amd64
│ │ ├─arm
│ │ │ ├─v4a
│ │ │ ├─v4t
│ │ │ └─v5a
│ │ ├─i086
│ │ ├─i386
│ │ ├─m68k
│ │ │ ├─m68000
│ │ │ └─m68020
│ │ ├─mips
│ │ │ └─r3000
│ │ └─powerpc
│ │ └─32
│ ├─c
│ └─include
└─tools
├─armpe
└─sstrip
从src内的main.cpp开始

这里写图片描述

main函数之前做了一些判断,看起来是关于upx版本和一些待压缩文件的判断,最后进入到do_files(),紧接着进入到do_one_files()

这里写图片描述

接着做基本的文件属性检查

    int r;
    struct stat st;
    memset(&st, 0, sizeof(st));
#if (HAVE_LSTAT)
    r = lstat(iname,&st);
#else
    r = stat(iname,&st);
#endif

    if (r != 0)
        throw FileNotFoundException(iname);
    if (!(S_ISREG(st.st_mode)))
        throwIOException("not a regular file -- skipped");
#if defined(__unix__)
    // no special bits may be set
    if ((st.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0)
        throwIOException("file has special permissions -- skipped");
#endif
    if (st.st_size <= 0)
        throwIOException("empty file -- skipped");
    if (st.st_size >= 1024*1024*1024)
        throwIOException("file is too large -- skipped");
    if ((st.st_mode & S_IWUSR) == 0)
    {
        bool skip = true;
        if (opt->output_name)
            skip = false;
        else if (opt->to_stdout)
            skip = false;
        else if (opt->backup)
            skip = false;
        if (skip)
            throwIOException("file is write protected -- skipped");
    }

最后打开打开IO文件流,准备进行压缩

先实例化一个类PackMaster

PackMaster pm(&fi, opt);
    if (opt->cmd == CMD_COMPRESS)
        pm.pack(&fo);

这里写图片描述

调用关系如上,调用顺序如上

其中getPacker()会寻找适合当前输入文件类型的一个packer方法,最终返回一个Packer实例,进入到doPack()

doPacke()是pack()的包装

pack内进行compress()调用,查看调用关系如下,进入到lzma算法的压缩
这里写图片描述

=============================================================
upx加载时的脱壳过程

01058E30  | pushal                                                    |
01058E31  | mov esi,up.1058000                                        | esi points at upx1
01058E36  | lea edi,dword ptr ds:[esi-7000]                           | since edi points at upx0
01058E3C  | push edi                                                  |
01058E3D  | jmp up.1058E4A                                            | start extracting data

这部分用ebx来做是否进行循环的判断,同时有两个部分,上半部分是单字节的恢复,下半部分是4字节的恢复

01058E4A  | mov ebx,dword ptr ds:[esi]                                | get loop key here?
01058E4C  | sub esi,FFFFFFFC                                          | esi+=4
01058E4F  | adc ebx,ebx                                               | check whether continue| seems like just one byte patch here
01058E51  | jb up.1058E40                                             |
01058E53  | mov eax,1                                                 |
01058E58  | add ebx,ebx                                               |
01058E5A  | jne up.1058E63                                            |
01058E5C  | mov ebx,dword ptr ds:[esi]                                |
01058E5E  | sub esi,FFFFFFFC                                          |
01058E61  | adc ebx,ebx                                               |
01058E63  | adc eax,eax                                               |
01058E65  | add ebx,ebx                                               |
01058E67  | jae up.1058E58                                            |
01058E69  | jne up.1058E74                                            |
01058E6B  | mov ebx,dword ptr ds:[esi]                                |
01058E6D  | sub esi,FFFFFFFC                                          |
01058E70  | adc ebx,ebx                                               |
01058E72  | jae up.1058E58                                            |
01058E74  | xor ecx,ecx                                               |
01058E76  | sub eax,3                                                 |
01058E79  | jb up.1058E88                                             |
01058E7B  | shl eax,8                                                 |
01058E7E  | mov al,byte ptr ds:[esi]                                  |
01058E80  | inc esi                                                   |
01058E81  | xor eax,FFFFFFFF                                          |
01058E84  | je up.1058EFA                                             |
01058E86  | mov ebp,eax                                               |
01058E88  | add ebx,ebx                                               |
01058E8A  | jne up.1058E93                                            |
01058E8C  | mov ebx,dword ptr ds:[esi]                                |
01058E8E  | sub esi,FFFFFFFC                                          |
01058E91  | adc ebx,ebx                                               |
01058E93  | adc ecx,ecx                                               |
01058E95  | add ebx,ebx                                               |
01058E97  | jne up.1058EA0                                            |
01058E99  | mov ebx,dword ptr ds:[esi]                                |
01058E9B  | sub esi,FFFFFFFC                                          |
01058E9E  | adc ebx,ebx                                               |
01058EA0  | adc ecx,ecx                                               |
01058EA2  | jne up.1058EC4                                            |
01058EA4  | inc ecx                                                   |
01058EA5  | add ebx,ebx                                               |
01058EA7  | jne up.1058EB0                                            |
01058EA9  | mov ebx,dword ptr ds:[esi]                                |
01058EAB  | sub esi,FFFFFFFC                                          |
01058EAE  | adc ebx,ebx                                               |
01058EB0  | adc ecx,ecx                                               |
01058EB2  | add ebx,ebx                                               |
01058EB4  | jae up.1058EA5                                            |
01058EB6  | jne up.1058EC1                                            |
01058EB8  | mov ebx,dword ptr ds:[esi]                                |
01058EBA  | sub esi,FFFFFFFC                                          |
01058EBD  | adc ebx,ebx                                               |
01058EBF  | jae up.1058EA5                                            |
01058EC1  | add ecx,2                                                 |
01058EC4  | cmp ebp,FFFFF300                                          |
01058ECA  | adc ecx,1                                                 |
01058ECD  | lea edx,dword ptr ds:[edi+ebp]                            |
01058ED0  | cmp ebp,FFFFFFFC                                          |
01058ED3  | jbe up.1058EE4                                            |
01058ED5  | mov al,byte ptr ds:[edx]                                  |
01058ED7  | inc edx                                                   |
01058ED8  | mov byte ptr ds:[edi],al                                  |
01058EDA  | inc edi                                                   |
01058EDB  | dec ecx                                                   |
01058EDC  | jne up.1058ED5                                            |
01058EDE  | jmp up.1058E46                                            |
01058EE3  | nop                                                       |
01058EE4  | mov eax,dword ptr ds:[edx]                                |
01058EE6  | add edx,4                                                 |
01058EE9  | mov dword ptr ds:[edi],eax                                | 4 bytes patch here
01058EEB  | add edi,4                                                 |
01058EEE  | sub ecx,4                                                 |
01058EF1  | ja up.1058EE4                                             |
01058EF3  | add edi,ecx                                               |
01058EF5  | jmp up.1058E46                                            |
01058EFA  | pop esi                                                   | finish extracting from UPX1 to UPX0

开始进行call 函数地址的修复
遍历在上一部分修复至upx0部分的字节,寻找E8/E9(call)指令,然后修改call 之后的偏移

01058EFB  | mov edi,esi                                               |
01058EFD  | mov ecx,58                                                |
01058F02  | mov al,byte ptr ds:[edi]                                  | start checking "call" func from upx0
01058F04  | inc edi                                                   |
01058F05  | sub al,E8                                                 |
01058F07  | cmp al,1                                                  |
01058F09  | ja up.1058F02                                             | loop to find E8/E9
01058F0B  | cmp byte ptr ds:[edi],0                                   |
01058F0E  | jne up.1058F02                                            |
01058F10  | mov eax,dword ptr ds:[edi]                                | get call fun offset(fake)
01058F12  | mov bl,byte ptr ds:[edi+4]                                |
01058F15  | shr ax,8                                                  |
01058F19  | rol eax,10                                                |
01058F1C  | xchg ah,al                                                |
01058F1E  | sub eax,edi                                               |
01058F20  | sub bl,E8                                                 |
01058F23  | add eax,esi                                               | eax-edi+esi=eax-edi's offset from upx0
01058F25  | mov dword ptr ds:[edi],eax                                | call func offset fix over
01058F27  | add edi,5                                                 |
01058F2A  | mov al,bl                                                 |
01058F2C  | loop up.1058F07                                           |
01058F2E  | lea edi,dword ptr ds:[esi+6000]                           |
01058F34  | mov eax,dword ptr ds:[edi]                                |
01058F36  | or eax,eax                                                |
01058F38  | je up.1058F76                                             | fix over

开始填充函数地址
使用了Loadlibrary() GetProcAddress(),进行指定函数的地址载入,并填充到指定地址

01058F4A  | call dword ptr ds:[esi+92B8]                              | LoadLibraryA
01058F50  | xchg eax,ebp                                              | ebp take kernel32.dll base addr & other dll base addr
01058F51  | mov al,byte ptr ds:[edi]                                  |
01058F53  | inc edi                                                   |
01058F54  | or al,al                                                  |
01058F56  | je up.1058F34                                             |
01058F58  | mov ecx,edi                                               |
01058F5A  | push edi                                                  |
01058F5B  | dec eax                                                   |
01058F5C  | repne scasb al,byte ptr es:[edi]                          | move edi to next string offset (func name string)
01058F5E  | push ebp                                                  |
01058F5F  | call dword ptr ds:[esi+92C0]                              |
01058F65  | or eax,eax                                                |
01058F67  | je up.1058F70                                             |
01058F69  | mov dword ptr ds:[ebx],eax                                | fix the func addr to upx0+2000
01058F6B  | add ebx,4                                                 |
01058F6E  | jmp up.1058F51                                            | fix address to upx0+1000
01058F70  | call dword ptr ds:[esi+92BC]                              |
01058F76  | add edi,4                                                 | func addr fix over

(还差最后一步,未完待补充)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值