乐易论坛-乐易网易语言培训教程火山PC视窗中文编程交流论坛

 找回密码
 立即注册

手机号登录

用手机号号登陆

微信登录

微信扫码,快速开始

QQ登录

用QQ账号登陆

办理VIP,定制软件,报名培训联系QQ请牢记揰掵佲的QQ号1615457736 1615457734 其他都是骗子易语言0基础入门课程
易语言汇编快速入门课程《64位某信Hook技术实战基础教程》【投稿课程】百日Js加密分析实战课程(无密下载)
【强烈推荐】《火山视窗0基础入门系列课程》《64位某信Hook技术实战进阶教程》【投稿课程】《0基础x64位游戏内存辅助开发教程 》
《火山视窗POST基础入门课程》《64位某信数据库操作课程》【投稿课程】广告位招租联系QQ1615457736
查看: 7595|回复: 1

[图文教程] UPX脱壳全过程分析

[复制链接]

[图文教程] UPX脱壳全过程分析

[复制链接]
已绑定手机
已实名认证
揰掵佲
等级头衔

等級:乐易运营组

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

积分成就
易币
贡献
主题
精华
金钱
积分
24315
注册时间
2014-8-2
最后登录
1970-1-1

勋章墙

2014-10-15 00:05:17 | 显示全部楼层 |阅读模式

据统计90%查看本帖的人,都已经注册本站了哦

您需要 登录 才可以下载或查看,没有账号?立即注册

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

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

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


第二步:代码还原。

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



  1. 0049B4E8   > /8A06               mov     al, byte ptr [esi]                    ;  //依次取UPX1区段的数据进行还原
  2. 0049B4EA   . |46                 inc     esi                                   ;  //指向下一个要还原的数据 //ESI=ESI+1
  3. 0049B4EB   . |8807               mov     byte ptr [edi], al                    ;  //还原数据
  4. 0049B4ED   . |47                 inc     edi                                   ;  //EDI=EDI+1
  5. 0049B4EE   > |01DB               add     ebx, ebx                              ;  //用EBX做判断,是否继续还原
  6. 0049B4F0   . |75 07              jnz     short 0049B4F9
  7. 0049B4F2   > |8B1E               mov     ebx, dword ptr [esi]                  ;  //取循环变量KEY //存放在EBX=0xFF36DEEF
  8. 0049B4F4   . |83EE FC            sub     esi, -0x4                             ;  //ESI=ESI+4
  9. 0049B4F7   . |11DB               adc     ebx, ebx                              ;  //用EBX做判断,是否开始进行还原代码操作
  10. 0049B4F9   >^\72 ED              jb      short 0049B4E8
  11. 0049B4FB   .  B8 01000000        mov     eax, 0x1                              ;  //EAX=1
  12. 0049B500   >  01DB               add     ebx, ebx
  13. 0049B502   .  75 07              jnz     short 0049B50B
  14. 0049B504   .  8B1E               mov     ebx, dword ptr [esi]
  15. 0049B506   .  83EE FC            sub     esi, -0x4
  16. 0049B509   .  11DB               adc     ebx, ebx
  17. 0049B50B   >  11C0               adc     eax, eax                              ;  //使用EAX判断还原方式
  18. 0049B50D   .  01DB               add     ebx, ebx
  19. 0049B50F   .  73 0B              jnb     short 0049B51C
  20. 0049B511   .  75 28              jnz     short 0049B53B
  21. 0049B513   .  8B1E               mov     ebx, dword ptr [esi]
  22. 0049B515   .  83EE FC            sub     esi, -0x4
  23. 0049B518   .  11DB               adc     ebx, ebx
  24. 0049B51A   .  72 1F              jb      short 0049B53B
  25. 0049B51C   >  48                 dec     eax
  26. 0049B51D   .  01DB               add     ebx, ebx
  27. 0049B51F   .  75 07              jnz     short 0049B528
  28. 0049B521   .  8B1E               mov     ebx, dword ptr [esi]
  29. 0049B523   .  83EE FC            sub     esi, -0x4
  30. 0049B526   .  11DB               adc     ebx, ebx
  31. 0049B528   >  11C0               adc     eax, eax
  32. 0049B52A   .^ EB D4              jmp     short 0049B500
  33. 0049B52C   >  01DB               add     ebx, ebx
  34. 0049B52E   .  75 07              jnz     short 0049B537
  35. 0049B530   .  8B1E               mov     ebx, dword ptr [esi]
  36. 0049B532   .  83EE FC            sub     esi, -0x4
  37. 0049B535   .  11DB               adc     ebx, ebx
  38. 0049B537   >  11C9               adc     ecx, ecx
  39. 0049B539   .  EB 52              jmp     short 0049B58D
  40. 0049B53B   >  31C9               xor     ecx, ecx                              ;  //循环变量清零 //ECX=0
  41. 0049B53D   .  83E8 03            sub     eax, 0x3                              ;  //EAX=0xFFFFFFFF为单字节代码还原 //EAX!=0xFFFFFFFF为多字节还原代码
  42. 0049B540   .  72 11              jb      short 0049B553
  43. 0049B542   .  C1E0 08            shl     eax, 0x8                              ;  //EAX左移8位 //EAX清零
  44. 0049B545   .  8A06               mov     al, byte ptr [esi]                    ;  //取多字节循环还原地址KEY
  45. 0049B547   .  46                 inc     esi                                   ;  //ESI+1
  46. 0049B548   .  83F0 FF            xor     eax, 0xFFFFFFFF                       ;  //EAX和0xFFFFFFFF异或 //循环出口
  47. 0049B54B   .  74 75              je      short 0049B5C2
  48. 0049B54D   .  D1F8               sar     eax, 1                                ;  //EAX右移一位
  49. 0049B54F   .  89C5               mov     ebp, eax                              ;  //EBP=EAX //EBP的值为:~(KEY/2)
  50. 0049B551   .  EB 0B              jmp     short 0049B55E
  51. 0049B553   >  01DB               add     ebx, ebx
  52. 0049B555   .  75 07              jnz     short 0049B55E
  53. 0049B557   .  8B1E               mov     ebx, dword ptr [esi]
  54. 0049B559   .  83EE FC            sub     esi, -0x4
  55. 0049B55C   .  11DB               adc     ebx, ebx
  56. 0049B55E   >^ 72 CC              jb      short 0049B52C
  57. 0049B560   .  41                 inc     ecx
  58. 0049B561   .  01DB               add     ebx, ebx
  59. 0049B563   .  75 07              jnz     short 0049B56C
  60. 0049B565   .  8B1E               mov     ebx, dword ptr [esi]
  61. 0049B567   .  83EE FC            sub     esi, -0x4
  62. 0049B56A   .  11DB               adc     ebx, ebx
  63. 0049B56C   >^ 72 BE              jb      short 0049B52C
  64. 0049B56E   >  01DB               add     ebx, ebx
  65. 0049B570   .  75 07              jnz     short 0049B579
  66. 0049B572   .  8B1E               mov     ebx, dword ptr [esi]
  67. 0049B574   .  83EE FC            sub     esi, -0x4
  68. 0049B577   .  11DB               adc     ebx, ebx
  69. 0049B579   >  11C9               adc     ecx, ecx
  70. 0049B57B   .  01DB               add     ebx, ebx
  71. 0049B57D   .^ 73 EF              jnb     short 0049B56E
  72. 0049B57F   .  75 09              jnz     short 0049B58A
  73. 0049B581   .  8B1E               mov     ebx, dword ptr [esi]
  74. 0049B583   .  83EE FC            sub     esi, -0x4
  75. 0049B586   .  11DB               adc     ebx, ebx
  76. 0049B588   .^ 73 E4              jnb     short 0049B56E
  77. 0049B58A   >  83C1 02            add     ecx, 0x2                              ;  //循环变量递增
  78. 0049B58D   >  81FD 00FBFFFF      cmp     ebp, -0x500
  79. 0049B593   .  83D1 02            adc     ecx, 0x2                              ;  //循环变量递增
  80. 0049B596   .  8D142F             lea     edx, dword ptr [edi+ebp]              ;  //以EDI作为代码还原的基地址,用EBP指向已还原的代码,将相同代码进行还原
  81. 0049B599   .  83FD FC            cmp     ebp, -0x4                             ;  //再次判断循环还原的方式
  82. 0049B59C   .  76 0E              jbe     short 0049B5AC                        ;  //如果还原处和解码处相距距离小于4个字节则进行单字节还原
  83. 0049B59E   >  8A02               mov     al, byte ptr [edx]                    ;  //相同字节单字节还原循环
  84. 0049B5A0   .  42                 inc     edx                                   ;  //EDX+1
  85. 0049B5A1   .  8807               mov     byte ptr [edi], al                    ;  //循环还原
  86. 0049B5A3   .  47                 inc     edi                                   ;  //EDI+1
  87. 0049B5A4   .  49                 dec     ecx                                   ;  //判断循环是否结束 //ECX
  88. 0049B5A5   .^ 75 F7              jnz     short 0049B59E
  89. 0049B5A7   .^ E9 42FFFFFF        jmp     0049B4EE
  90. 0049B5AC   >  8B02               mov     eax, dword ptr [edx]                  ;  //多字节循环还原
  91. 0049B5AE   .  83C2 04            add     edx, 0x4
  92. 0049B5B1   .  8907               mov     dword ptr [edi], eax
  93. 0049B5B3   .  83C7 04            add     edi, 0x4
  94. 0049B5B6   .  83E9 04            sub     ecx, 0x4                              ;  //判断循环是否结束 //ECX
  95. 0049B5B9   .^ 77 F1              ja      short 0049B5AC
  96. 0049B5BB   .  01*F               add     edi, ecx
  97. 0049B5BD   .^ E9 2*FFFFFF        jmp     0049B4EE
  98. 0049B5C2   >  5E                 pop     esi                                   ;  //还原完毕 //首地址出栈入ESI
复制代码
整个过程中,ECX一般作为循环变量,EBX是绝对的王者,它掌控整个还原的节奏。
    关于EBX是怎样计算出来的,现在技术不够逆不出来。
    所以现在分析的都只不过是皮毛,UPX壳压缩的核心还是没能出来。
第三步:CALL修复


  1. 0049B5C3   .  89F7               mov     edi, esi
  2. 0049B5C5   .  B9 73250000        mov     ecx, 0x2573                           ;  //循环变量 //ECX=0x2573
  3. 0049B5CA   >  8A07               mov     al, byte ptr [edi]
  4. 0049B5CC   .  47                 inc     edi
  5. 0049B5CD   .  2C E8              sub     al, 0xE8
  6. 0049B5*F   >  3C 01              cmp     al, 0x1                               ;  //判断是否为0xE8
  7. 0049B5D1   .^ 77 F7              ja      short 0049B5CA
  8. 0049B5D3   .  803F 13            cmp     byte ptr [edi], 0x13                  ;  //判断0xE8后面是否为0x13 //判断是否为CALL
  9. 0049B5D6   .^ 75 F2              jnz     short 0049B5CA
  10. 0049B5D8   .  8B07               mov     eax, dword ptr [edi]
  11. 0049B5DA   .  8A5F 04            mov     bl, byte ptr [edi+0x4]                ;  //使用BL判断下一个指令是否是CALL
  12. 0049B5DD   .  66:C1E8 08         shr     ax, 0x8                               ;  //AX右移8位 //AL清零,AL=AH
  13. 0049B5E1   .  C1C0 10            rol     eax, 0x10                             ;  //EAX左移循环
  14. 0049B5E4   .  86C4               xchg    ah, al                                ;  //AH和AL交换
  15. 0049B5E6   .  29F8               sub     eax, edi
  16. 0049B5E8   .  80EB E8            sub     bl, 0xE8
  17. 0049B5EB   .  01F0               add     eax, esi
  18. 0049B5ED   .  8907               mov     dword ptr [edi], eax                  ;  //CALL地址还原
  19. 0049B5EF   .  83C7 05            add     edi, 0x5
  20. 0049B5F2   .  88D8               mov     al, bl
  21. 0049B5F4   .^ E2 D9              loopd   short 0049B5*F                        ;  //循环还原CALL地址
复制代码
关于CALL修复。在第二步代码还原时已经把CALL的特征码还原了,这里只是对CALL的地址做进一步的修正。
    它的算法我还是有点不明白。我看到在还原代码时,CALL后面的四个字节标志了CALL地址KEY,通过上面的CALL算法将KEY还原成正确地址。请问这个将KEY还原的代码用高级语言应该怎样表示呢?



第四步:函数表还原,没什么好解释的,看代码。
  1. 0049B5F6    8DBE 00800900        lea     edi, dword ptr [esi+0x98000]          ; //获取函数表还原基地址
  2. 0049B5FC    8B07                 mov     eax, dword ptr [edi]                  ; //获取函数表还原偏移
  3. 0049B5FE    09C0                 or      eax, eax
  4. 0049B600    74 45                je      short 0049B647                        ; //函数表还原完了进入下一个模块的跳转
  5. 0049B602    8B5F 04              mov     ebx, dword ptr [edi+0x4]              ; //获取函数存放位置偏移
  6. 0049B605    8D8430 B0C80900      lea     eax, dword ptr [eax+esi+0x9C8B0]      ; //获取函数所在DLL名称
  7. 0049B60C    01F3                 add     ebx, esi
  8. 0049B60E    50                   push    eax
  9. 0049B60F    83C7 08              add     edi, 0x8
  10. 0049B612    FF96 B4C90900        call    dword ptr [esi+0x9C9B4]               ; //载入DLL到内存
  11. 0049B618    95                   xchg    eax, ebp
  12. 0049B619    8A07                 mov     al, byte ptr [edi]
  13. 0049B61B    47                   inc     edi
  14. 0049B61C    08C0                 or      al, al
  15. 0049B61E  ^ 74 DC                je      short 0049B5FC                        ; //还原DLL后进入下一个DLL的还原跳转
  16. 0049B620    89F9                 mov     ecx, edi
  17. 0049B622    79 07                jns     short 0049B62B
  18. 0049B624    0FB707               movzx   eax, word ptr [edi]
  19. 0049B627    47                   inc     edi
  20. 0049B628    50                   push    eax
  21. 0049B629    47                   inc     edi
  22. 0049B62A    B9 5748F2AE          mov     ecx, 0xAEF24857
  23. 0049B62F    55                   push    ebp
  24. 0049B630    FF96 B8C90900        call    dword ptr [esi+0x9C9B8]               ; //获取函数所在地址
  25. 0049B636    09C0                 or      eax, eax
  26. 0049B638    74 07                je      short 0049B641
  27. 0049B63A    8903                 mov     dword ptr [ebx], eax                  ; //进行函数表地址还原
  28. 0049B63C    83C3 04              add     ebx, 0x4
  29. 0049B63F  ^ EB D8                jmp     short 0049B619                        ; //循环还原函数表
复制代码
第五步:节表初始化,看代码。
  1. 0049B641    FF96 C8C90900        call    dword ptr [esi+0x9C9C8]
  2. 0049B647    8BAE BCC90900        mov     ebp, dword ptr [esi+0x9C9BC]          ; //指向VirtualProtect函数地址
  3. 0049B64D    8DBE 00F0FFFF        lea     edi, dword ptr [esi-0x1000]           ; //指向文件头位置
  4. 0049B653    BB 00100000          mov     ebx, 0x1000                           ; //文件头大小
  5. 0049B658    50                   push    eax
  6. 0049B659    54                   push    esp
  7. 0049B65A    6A 04                push    0x4                                   ; //PAGE_EXECUTE_READWRITE
  8. 0049B65C    53                   push    ebx
  9. 0049B65D    57                   push    edi
  10. 0049B65E    FFD5                 call    ebp                                   ; 使用VirtualProtect把文件头设置为PAGE_EXECUTE_READWRITE,获得文件头的写权限
  11. 0049B660    8D87 0F020000        lea     eax, dword ptr [edi+0x20F]
  12. 0049B666    8020 7F              and     byte ptr [eax], 0x7F
  13. 0049B669    8060 28 7F           and     byte ptr [eax+0x28], 0x7F             ; //完成节表的初始化
  14. 0049B66D    58                   pop     eax                                 
  15. 0049B66E    50                   push    eax
  16. 0049B66F    54                   push    esp
  17. 0049B670    50                   push    eax
  18. 0049B671    53                   push    ebx
  19. 0049B672    57                   push    edi
  20. 0049B673    FFD5                 call    ebp                                   ; //用VirtualProtect恢复文件头属性
复制代码
最后:在0x0049B684飞向程序入口。
  1. 0049B675    58                   pop     eax
  2. 0049B676    61                   popad
  3. 0049B677    8D4424 80            lea     eax, dword ptr [esp-0x80]
  4. 0049B67B    6A 00                push    0x0
  5. 0049B67D    39C4                 cmp     esp, eax
  6. 0049B67F  ^ 75 FA                jnz     short 0049B67B
  7. 0049B681    83EC 80              sub     esp, -0x80
  8. 0049B684  - E9 C89AFAFF          jmp     00445151
复制代码


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

使用道具 举报

李寻欢
等级头衔

等級:编程学徒

Rank: 3Rank: 3

积分成就
易币
贡献
主题
精华
金钱
积分
350
注册时间
2014-11-13
最后登录
1970-1-1

勋章墙

2014-11-14 16:18:28 | 显示全部楼层

有楼主在,论坛的明天必将更好! 逐句地看完这个帖子以后,我的心久久不能平静,震撼啊!为什么会有如此好的帖子!我纵横网络论坛多年,自以为再也不会有任何帖子能打动我,没想到今天看到了如此精妙绝伦的这样一篇帖子。
回复

使用道具 举报

如果懒得打字,请选择右侧内容快捷回复 提醒:以任何方式进行『恶意灌水』的行为,进行封号处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

热点推荐上一条 /5 下一条

QQ|网站地图|手机版|小黑屋|乐易论坛-乐易网 | 湘ICP备19007035号

拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表乐易立场!

娄底市乐易网络科技有限公司声明:乐易原创培训课程版权均为我公司所有,未经许可,不得擅自翻录,盗版,破解本站课课程,我们将保留法律诉讼的权利

GMT+8, 2025-7-7 06:16 , Processed in 0.051466 second(s), 40 queries .

Powered by Discuz! X3.4

Copyright © Tencent Cloud.

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