吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 66381|回复: 161
收起左侧

[原创] UPX脱壳全过程分析

    [复制链接]
HadgeROL 发表于 2014-10-2 14:21
本帖最后由 Blackk 于 2014-11-29 22:06 编辑

小菜也只能拿这样简单的壳来练手啦 ^ ^。
简单说下个人认为的UPX壳解码时的流程,然后我就按流程一点一点写吧。

     1.先初始化
     2.进行代码还原
     3.进行CALL修复
     4.进行函数表还原
     5.节表初始化
     6.解码完毕飞向程序入口

第一步:初始化,这个好像没什么好解释的呃,看代码。
[Asm] 纯文本查看 复制代码
0049B4D0 > $  60                 pushad                                        ;  //保存现场 //寄存器入栈 //ESP=12FFC4
0049B4D1   .  BE 00204600        mov     esi, 00462000                         ;  //ESI指向UPX1区段 //ESI=462000 //ESP=12FFA4
0049B4D6   .  8DBE 00F0F9FF      lea     edi, dword ptr [esi+0xFFF9F000]       ;  //指向要还原的首地址UPX0区段 //EDI=401000
0049B4DC   .  57                 push    edi                                   ;  //首地址入栈
0049B4DD   .  83CD FF            or      ebp, 0xFFFFFFFF                       ;  //初始化EBP //EBP用来判断第二次循环还原方式
0049B4E0   .  EB 10              jmp     short 0049B4F2                        ;  //初始化完毕


第二步:代码还原。

    接下来UPX壳进行代码还原,还原的方式我个人分为两种。
    一种是UPX壳无法压缩的代码,UPX老老实实的从UPX1区段将代码还原到UPX0区段;
    另一种就是UPX0中的重复指令,UPX壳根据UPX1中的KEY值找到UPX0中重复指令的位置,再进行的代码还原。具体过程如下:

[Asm] 纯文本查看 复制代码
0049B4E8   > /8A06               mov     al, byte ptr [esi]                    ;  //依次取UPX1区段的数据进行还原
0049B4EA   . |46                 inc     esi                                   ;  //指向下一个要还原的数据 //ESI=ESI+1
0049B4EB   . |8807               mov     byte ptr [edi], al                    ;  //还原数据
0049B4ED   . |47                 inc     edi                                   ;  //EDI=EDI+1
0049B4EE   > |01DB               add     ebx, ebx                              ;  //用EBX做判断,是否继续还原
0049B4F0   . |75 07              jnz     short 0049B4F9
0049B4F2   > |8B1E               mov     ebx, dword ptr [esi]                  ;  //取循环变量KEY //存放在EBX=0xFF36DEEF
0049B4F4   . |83EE FC            sub     esi, -0x4                             ;  //ESI=ESI+4
0049B4F7   . |11DB               adc     ebx, ebx                              ;  //用EBX做判断,是否开始进行还原代码操作
0049B4F9   >^\72 ED              jb      short 0049B4E8
0049B4FB   .  B8 01000000        mov     eax, 0x1                              ;  //EAX=1
0049B500   >  01DB               add     ebx, ebx
0049B502   .  75 07              jnz     short 0049B50B
0049B504   .  8B1E               mov     ebx, dword ptr [esi]
0049B506   .  83EE FC            sub     esi, -0x4
0049B509   .  11DB               adc     ebx, ebx
0049B50B   >  11C0               adc     eax, eax                              ;  //使用EAX判断还原方式
0049B50D   .  01DB               add     ebx, ebx
0049B50F   .  73 0B              jnb     short 0049B51C
0049B511   .  75 28              jnz     short 0049B53B
0049B513   .  8B1E               mov     ebx, dword ptr [esi]
0049B515   .  83EE FC            sub     esi, -0x4
0049B518   .  11DB               adc     ebx, ebx
0049B51A   .  72 1F              jb      short 0049B53B
0049B51C   >  48                 dec     eax
0049B51D   .  01DB               add     ebx, ebx
0049B51F   .  75 07              jnz     short 0049B528
0049B521   .  8B1E               mov     ebx, dword ptr [esi]
0049B523   .  83EE FC            sub     esi, -0x4
0049B526   .  11DB               adc     ebx, ebx
0049B528   >  11C0               adc     eax, eax
0049B52A   .^ EB D4              jmp     short 0049B500
0049B52C   >  01DB               add     ebx, ebx
0049B52E   .  75 07              jnz     short 0049B537
0049B530   .  8B1E               mov     ebx, dword ptr [esi]
0049B532   .  83EE FC            sub     esi, -0x4
0049B535   .  11DB               adc     ebx, ebx
0049B537   >  11C9               adc     ecx, ecx
0049B539   .  EB 52              jmp     short 0049B58D
0049B53B   >  31C9               xor     ecx, ecx                              ;  //循环变量清零 //ECX=0
0049B53D   .  83E8 03            sub     eax, 0x3                              ;  //EAX=0xFFFFFFFF为单字节代码还原 //EAX!=0xFFFFFFFF为多字节还原代码
0049B540   .  72 11              jb      short 0049B553
0049B542   .  C1E0 08            shl     eax, 0x8                              ;  //EAX左移8位 //EAX清零
0049B545   .  8A06               mov     al, byte ptr [esi]                    ;  //取多字节循环还原地址KEY
0049B547   .  46                 inc     esi                                   ;  //ESI+1
0049B548   .  83F0 FF            xor     eax, 0xFFFFFFFF                       ;  //EAX和0xFFFFFFFF异或 //循环出口
0049B54B   .  74 75              je      short 0049B5C2
0049B54D   .  D1F8               sar     eax, 1                                ;  //EAX右移一位
0049B54F   .  89C5               mov     ebp, eax                              ;  //EBP=EAX //EBP的值为:~(KEY/2)
0049B551   .  EB 0B              jmp     short 0049B55E
0049B553   >  01DB               add     ebx, ebx
0049B555   .  75 07              jnz     short 0049B55E
0049B557   .  8B1E               mov     ebx, dword ptr [esi]
0049B559   .  83EE FC            sub     esi, -0x4
0049B55C   .  11DB               adc     ebx, ebx
0049B55E   >^ 72 CC              jb      short 0049B52C
0049B560   .  41                 inc     ecx
0049B561   .  01DB               add     ebx, ebx
0049B563   .  75 07              jnz     short 0049B56C
0049B565   .  8B1E               mov     ebx, dword ptr [esi]
0049B567   .  83EE FC            sub     esi, -0x4
0049B56A   .  11DB               adc     ebx, ebx
0049B56C   >^ 72 BE              jb      short 0049B52C
0049B56E   >  01DB               add     ebx, ebx
0049B570   .  75 07              jnz     short 0049B579
0049B572   .  8B1E               mov     ebx, dword ptr [esi]
0049B574   .  83EE FC            sub     esi, -0x4
0049B577   .  11DB               adc     ebx, ebx
0049B579   >  11C9               adc     ecx, ecx
0049B57B   .  01DB               add     ebx, ebx
0049B57D   .^ 73 EF              jnb     short 0049B56E
0049B57F   .  75 09              jnz     short 0049B58A
0049B581   .  8B1E               mov     ebx, dword ptr [esi]
0049B583   .  83EE FC            sub     esi, -0x4
0049B586   .  11DB               adc     ebx, ebx
0049B588   .^ 73 E4              jnb     short 0049B56E
0049B58A   >  83C1 02            add     ecx, 0x2                              ;  //循环变量递增
0049B58D   >  81FD 00FBFFFF      cmp     ebp, -0x500
0049B593   .  83D1 02            adc     ecx, 0x2                              ;  //循环变量递增
0049B596   .  8D142F             lea     edx, dword ptr [edi+ebp]              ;  //以EDI作为代码还原的基地址,用EBP指向已还原的代码,将相同代码进行还原
0049B599   .  83FD FC            cmp     ebp, -0x4                             ;  //再次判断循环还原的方式
0049B59C   .  76 0E              jbe     short 0049B5AC                        ;  //如果还原处和解码处相距距离小于4个字节则进行单字节还原
0049B59E   >  8A02               mov     al, byte ptr [edx]                    ;  //相同字节单字节还原循环
0049B5A0   .  42                 inc     edx                                   ;  //EDX+1
0049B5A1   .  8807               mov     byte ptr [edi], al                    ;  //循环还原
0049B5A3   .  47                 inc     edi                                   ;  //EDI+1
0049B5A4   .  49                 dec     ecx                                   ;  //判断循环是否结束 //ECX
0049B5A5   .^ 75 F7              jnz     short 0049B59E
0049B5A7   .^ E9 42FFFFFF        jmp     0049B4EE
0049B5AC   >  8B02               mov     eax, dword ptr [edx]                  ;  //多字节循环还原
0049B5AE   .  83C2 04            add     edx, 0x4
0049B5B1   .  8907               mov     dword ptr [edi], eax
0049B5B3   .  83C7 04            add     edi, 0x4
0049B5B6   .  83E9 04            sub     ecx, 0x4                              ;  //判断循环是否结束 //ECX
0049B5B9   .^ 77 F1              ja      short 0049B5AC
0049B5BB   .  01CF               add     edi, ecx
0049B5BD   .^ E9 2CFFFFFF        jmp     0049B4EE
0049B5C2   >  5E                 pop     esi                                   ;  //还原完毕 //首地址出栈入ESI


    整个过程中,ECX一般作为循环变量,EBX是绝对的王者,它掌控整个还原的节奏。
    关于EBX是怎样计算出来的,现在技术不够逆不出来。
    所以现在分析的都只不过是皮毛,UPX壳压缩的核心还是没能出来。

第三步:CALL修复
[Asm] 纯文本查看 复制代码
0049B5C3   .  89F7               mov     edi, esi
0049B5C5   .  B9 73250000        mov     ecx, 0x2573                           ;  //循环变量 //ECX=0x2573
0049B5CA   >  8A07               mov     al, byte ptr [edi]
0049B5CC   .  47                 inc     edi
0049B5CD   .  2C E8              sub     al, 0xE8
0049B5CF   >  3C 01              cmp     al, 0x1                               ;  //判断是否为0xE8
0049B5D1   .^ 77 F7              ja      short 0049B5CA
0049B5D3   .  803F 13            cmp     byte ptr [edi], 0x13                  ;  //判断0xE8后面是否为0x13 //判断是否为CALL
0049B5D6   .^ 75 F2              jnz     short 0049B5CA
0049B5D8   .  8B07               mov     eax, dword ptr [edi]
0049B5DA   .  8A5F 04            mov     bl, byte ptr [edi+0x4]                ;  //使用BL判断下一个指令是否是CALL
0049B5DD   .  66:C1E8 08         shr     ax, 0x8                               ;  //AX右移8位 //AL清零,AL=AH
0049B5E1   .  C1C0 10            rol     eax, 0x10                             ;  //EAX左移循环
0049B5E4   .  86C4               xchg    ah, al                                ;  //AH和AL交换
0049B5E6   .  29F8               sub     eax, edi
0049B5E8   .  80EB E8            sub     bl, 0xE8
0049B5EB   .  01F0               add     eax, esi
0049B5ED   .  8907               mov     dword ptr [edi], eax                  ;  //CALL地址还原
0049B5EF   .  83C7 05            add     edi, 0x5
0049B5F2   .  88D8               mov     al, bl
0049B5F4   .^ E2 D9              loopd   short 0049B5CF                        ;  //循环还原CALL地址


    关于CALL修复。在第二步代码还原时已经把CALL的特征码还原了,这里只是对CALL的地址做进一步的修正。
    它的算法我还是有点不明白。我看到在还原代码时,CALL后面的四个字节标志了CALL地址KEY,通过上面的CALL算法将KEY还原成正确地址。请问这个将KEY还原的代码用高级语言应该怎样表示呢?



第四步:函数表还原,没什么好解释的,看代码。
[Asm] 纯文本查看 复制代码
0049B5F6    8DBE 00800900        lea     edi, dword ptr [esi+0x98000]          ; //获取函数表还原基地址
0049B5FC    8B07                 mov     eax, dword ptr [edi]                  ; //获取函数表还原偏移
0049B5FE    09C0                 or      eax, eax
0049B600    74 45                je      short 0049B647                        ; //函数表还原完了进入下一个模块的跳转
0049B602    8B5F 04              mov     ebx, dword ptr [edi+0x4]              ; //获取函数存放位置偏移
0049B605    8D8430 B0C80900      lea     eax, dword ptr [eax+esi+0x9C8B0]      ; //获取函数所在DLL名称
0049B60C    01F3                 add     ebx, esi
0049B60E    50                   push    eax
0049B60F    83C7 08              add     edi, 0x8
0049B612    FF96 B4C90900        call    dword ptr [esi+0x9C9B4]               ; //载入DLL到内存
0049B618    95                   xchg    eax, ebp
0049B619    8A07                 mov     al, byte ptr [edi]
0049B61B    47                   inc     edi
0049B61C    08C0                 or      al, al
0049B61E  ^ 74 DC                je      short 0049B5FC                        ; //还原DLL后进入下一个DLL的还原跳转
0049B620    89F9                 mov     ecx, edi
0049B622    79 07                jns     short 0049B62B
0049B624    0FB707               movzx   eax, word ptr [edi]
0049B627    47                   inc     edi
0049B628    50                   push    eax
0049B629    47                   inc     edi
0049B62A    B9 5748F2AE          mov     ecx, 0xAEF24857
0049B62F    55                   push    ebp
0049B630    FF96 B8C90900        call    dword ptr [esi+0x9C9B8]               ; //获取函数所在地址
0049B636    09C0                 or      eax, eax
0049B638    74 07                je      short 0049B641
0049B63A    8903                 mov     dword ptr [ebx], eax                  ; //进行函数表地址还原
0049B63C    83C3 04              add     ebx, 0x4
0049B63F  ^ EB D8                jmp     short 0049B619                        ; //循环还原函数表


第五步:节表初始化,看代码。
[Asm] 纯文本查看 复制代码
0049B641    FF96 C8C90900        call    dword ptr [esi+0x9C9C8]
0049B647    8BAE BCC90900        mov     ebp, dword ptr [esi+0x9C9BC]          ; //指向VirtualProtect函数地址
0049B64D    8DBE 00F0FFFF        lea     edi, dword ptr [esi-0x1000]           ; //指向文件头位置
0049B653    BB 00100000          mov     ebx, 0x1000                           ; //文件头大小
0049B658    50                   push    eax
0049B659    54                   push    esp
0049B65A    6A 04                push    0x4                                   ; //PAGE_EXECUTE_READWRITE
0049B65C    53                   push    ebx
0049B65D    57                   push    edi
0049B65E    FFD5                 call    ebp                                   ; 使用VirtualProtect把文件头设置为PAGE_EXECUTE_READWRITE,获得文件头的写权限
0049B660    8D87 0F020000        lea     eax, dword ptr [edi+0x20F]
0049B666    8020 7F              and     byte ptr [eax], 0x7F
0049B669    8060 28 7F           and     byte ptr [eax+0x28], 0x7F             ; //完成节表的初始化
0049B66D    58                   pop     eax                                   
0049B66E    50                   push    eax
0049B66F    54                   push    esp
0049B670    50                   push    eax
0049B671    53                   push    ebx
0049B672    57                   push    edi
0049B673    FFD5                 call    ebp                                   ; //用VirtualProtect恢复文件头属性


最后:在0x0049B684飞向程序入口。
[Asm] 纯文本查看 复制代码
0049B675    58                   pop     eax
0049B676    61                   popad
0049B677    8D4424 80            lea     eax, dword ptr [esp-0x80]
0049B67B    6A 00                push    0x0
0049B67D    39C4                 cmp     esp, eax
0049B67F  ^ 75 FA                jnz     short 0049B67B
0049B681    83EC 80              sub     esp, -0x80
0049B684  - E9 C89AFAFF          jmp     00445151


至此UPX壳解码分析完毕了。第一次逆向壳,写下文章留个记录。
以上文章只是个人见解,难免有误,还希望各路友人帮忙指正。


免费评分

参与人数 22吾爱币 +4 热心值 +22 收起 理由
isgoodtime + 1 + 1 我很赞同!
冻住不许走 + 1 + 1 我很赞同!
开始的复活节 + 1 我很赞同!
net234 + 1 学习中啊~~
flexlm_crk + 1 + 1 谢谢@Thanks!
海天一色001 + 1 + 1 谢谢@Thanks!
祈祷灬烦恼 + 1 已经处理,感谢您对吾爱破解论坛的支持!
vigers + 1 我很赞同!
大器晚成0125 + 1 我很赞同!
燃香小狼 + 1 大牛,顶你一个!!!!!
Adeline + 1 谢谢@Thanks!
囚徒灬 + 1 已答复!
a10168 + 1 热心回复!
Passerby + 1 不得不说是精品逆文
wi5101 + 1 谢谢@Thanks!
梦游枪手 + 1 热心回复!
独孤~至尊 + 1 我很赞同!
吾爱扣扣 + 1 我很赞同!
brack + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
小淫仙 + 1 我很赞同!
Kido + 1 我很赞同!
lwj一辈子 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Bad丶Boy 发表于 2014-10-2 15:41
UPX分析都这么多  好麻烦 看不懂
游戏而已 发表于 2014-10-2 15:05
lwj一辈子 发表于 2014-10-2 15:03
hao516677 发表于 2014-10-2 14:45
谢谢你的分析,我来看看
 楼主| HadgeROL 发表于 2014-10-2 14:22
本帖最后由 Blackk 于 2014-10-2 14:29 编辑

按照国际惯列,沙发自己坐。试炼程序:
upx.zip (230.85 KB, 下载次数: 219)
520Kelly 发表于 2014-10-2 22:15
大神求VMP脱壳全分析 坐等人肉还原
manbajie 发表于 2014-10-3 08:08
来学习一下的说
Nisy 发表于 2014-10-7 17:15
不错哦~
旋冰 发表于 2014-10-7 19:10
大牛的作品,小白暂时看不透!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 08:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表