首页
社区
课程
招聘
[原创]UPX脱壳详细分析
2009-8-30 16:03 25704

[原创]UPX脱壳详细分析

2009-8-30 16:03
25704
【文章标题】: UPX脱壳详细分析
【文章作者】: index09
【作者主页】: http://hi.baidu.com/index09
【使用工具】: UPX + OD + Stud_PE + Import REC
--------------------------------------------------------------------------------
【详细过程】
  又被R公司鄙视了,每次都被相同的理由鄙视。哭……
  于是决定好好学一下逆向了。
  首先做个幼儿级的脱壳练习,当做开始吧。
  网上有很多类似文章,基本只写了找OEP的过程,这里稍加分析,高手莫笑。
  
  
  用UPX加密记事本,简单用Stud_PE查看一下节表信息。
  No  | Name      | VSize      | VOffset    | RSize      | ROffset    | Charact.   |
  01  | UPX0      | 0000F000   | 00001000   | 00000000   | 00000400   | E0000080   |
  02  | UPX1      | 00005000   | 00010000   | 00004600   | 00000400   | E0000040   |
  03  | .rsrc     | 00008000   | 00015000   | 00007200   | 00004A00   | C0000040   |
  看样子没有加密资源
  
  OD载入后如下
  01014241   .  BE 00000101   MOV ESI,NOTEPAD.01010000                 ;  esi = sec upx1
  01014246   .  8DBE 0010FFFF LEA EDI,DWORD PTR DS:[ESI+FFFF1000]      ;  edi = sec upx0
  0101424C   .  57            PUSH EDI
  0101424D   .  83CD FF       OR EBP,FFFFFFFF
  01014250   .  EB 10         JMP SHORT NOTEPAD.01014262
  分别把 UPX1和UPX0节的首地址放入了esi和edi
  上面看到UPX0段的RSize为0,猜想是释放解压数据的空间。而UPX1段应该就是加密的程序代码了。
  
  继续向下看
  01014258   > /8A06          MOV AL,BYTE PTR DS:[ESI]                 ;  //////////////////////////////////////////////
  0101425A   . |46            INC ESI
  0101425B   . |8807          MOV BYTE PTR DS:[EDI],AL
  0101425D   . |47            INC EDI
  0101425E   > |01DB          ADD EBX,EBX
  01014260   . |75 07         JNZ SHORT NOTEPAD.01014269               ;  express data in sec upx1 to sec upx0
  01014262   > |8B1E          MOV EBX,DWORD PTR DS:[ESI]
  01014264   . |83EE FC       SUB ESI,-4
  01014267   . |11DB          ADC EBX,EBX
  01014269   >^\72 ED         JB SHORT NOTEPAD.01014258
  0101426B   .  B8 01000000   MOV EAX,1
  01014270   >  01DB          ADD EBX,EBX
  01014272   .  75 07         JNZ SHORT NOTEPAD.0101427B
  01014274   .  8B1E          MOV EBX,DWORD PTR DS:[ESI]
  01014276   .  83EE FC       SUB ESI,-4
  01014279   .  11DB          ADC EBX,EBX
  
  .......
  
  0101431A   > /8A07          MOV AL,BYTE PTR DS:[EDI]                 ;  /////////////////////////
  0101431C   . |47            INC EDI
  0101431D   . |2C E8         SUB AL,0E8                               ;  find [edi] <= 0xE9 && [edi+1] == 1
  0101431F   > |3C 01         CMP AL,1
  01014321   .^ 77 F7         JA SHORT NOTEPAD.0101431A
  01014323   . |803F 01       CMP BYTE PTR DS:[EDI],1
  01014326   .^\75 F2         JNZ SHORT NOTEPAD.0101431A               ;  .........................
  01014328   .  8B07          MOV EAX,DWORD PTR DS:[EDI]
  0101432A   .  8A5F 04       MOV BL,BYTE PTR DS:[EDI+4]
  0101432D   .  66:C1E8 08    SHR AX,8
  01014331   .  C1C0 10       ROL EAX,10                               ;  edi = A B C D
  01014334   .  86C4          XCHG AH,AL                               ;  eax = 0 C B A
  01014336   .  29F8          SUB EAX,EDI
  01014338   .  80EB E8       SUB BL,0E8
  0101433B   .  01F0          ADD EAX,ESI                              ;  eax = edi offset to sec upx0 + eax
  0101433D   .  8907          MOV DWORD PTR DS:[EDI],EAX
  0101433F   .  83C7 05       ADD EDI,5
  01014342   .  88D8          MOV AL,BL
  01014344   .^ E2 D9         LOOPD SHORT NOTEPAD.0101431F             ;  ...........................................
  一大堆都是从UPX1中读取数据,做一些处理,并且放入UPX0中。
  应该是UPX的解压算法。具体算法比较复杂没有详细的分析。
  里面的EBX控制了每一步解压应该做的操作,十分好奇这个数是怎么出来的。改天看看UPX的源代码,看看它神奇的压缩算法。
  看雪上有一篇对算法的分析,有兴趣请自行搜索。
  
  然后来到了这里
  01014346   .  8DBE 00200100 LEA EDI,DWORD PTR DS:[ESI+12000]         ;  //////////////IAT////////////////////
  0101434C   >  8B07          MOV EAX,DWORD PTR DS:[EDI]               ;  edi = upx import table??
  0101434E   .  09C0          OR EAX,EAX
  01014350   .  74 3C         JE SHORT NOTEPAD.0101438E                ;  jmp out
  01014352   .  8B5F 04       MOV EBX,DWORD PTR DS:[EDI+4]
  01014355   .  8D8430 24AE01>LEA EAX,DWORD PTR DS:[EAX+ESI+1AE24]     ;  eax = lib name
  0101435C   .  01F3          ADD EBX,ESI                              ;  ebx = esi + 4-7 (ori IAT??)
  0101435E   .  50            PUSH EAX
  0101435F   .  83C7 08       ADD EDI,8
  01014362   .  FF96 ECAE0100 CALL DWORD PTR DS:[ESI+1AEEC]            ;  loadlibrary
  01014368   .  95            XCHG EAX,EBP                             ;  ebp = lib handle
  01014369   >  8A07          MOV AL,BYTE PTR DS:[EDI]
  0101436B   .  47            INC EDI
  0101436C   .  08C0          OR AL,AL
  0101436E   .^ 74 DC         JE SHORT NOTEPAD.0101434C
  01014370   .  89F9          MOV ECX,EDI
  01014372   .  57            PUSH EDI                                 ;  proc name
  01014373   .  48            DEC EAX
  01014374   .  F2:AE         REPNE SCAS BYTE PTR ES:[EDI]
  01014376   .  55            PUSH EBP                                 ;  lib handle
  01014377   .  FF96 F0AE0100 CALL DWORD PTR DS:[ESI+1AEF0]            ;  getprocaddress
  0101437D   .  09C0          OR EAX,EAX
  0101437F   .  74 07         JE SHORT NOTEPAD.01014388
  01014381   .  8903          MOV DWORD PTR DS:[EBX],EAX
  01014383   .  83C3 04       ADD EBX,4
  01014386   .^ EB E1         JMP SHORT NOTEPAD.01014369               ;  ............................................
  这里有两重循环,分别从UPX1中读取dll名称,使用LoadLibrary加载入内存。
  获得句柄后,再从UPX1中读取相应函数名,使用GetProcAddress获得函数地址。
  01014381   .  8903          MOV DWORD PTR DS:[EBX],EAX
  这一句将函数地址填入了源程序的IAT,完成了IAT的填充。
  从这段代码中可以获得IAT的RVA,为0x10000。记下来留着以后修复IAT时使用。
  
  继续往下
  0101438E   > \8BAE F4AE0100 MOV EBP,DWORD PTR DS:[ESI+1AEF4]
  01014394   .  8DBE 00F0FFFF LEA EDI,DWORD PTR DS:[ESI-1000]
  0101439A   .  BB 00100000   MOV EBX,1000
  0101439F   .  50            PUSH EAX
  010143A0   .  54            PUSH ESP
  010143A1   .  6A 04         PUSH 4                                   ;  PAGE_EXECUTE_READWRITE
  010143A3   .  53            PUSH EBX
  010143A4   .  57            PUSH EDI                                 ;  set file header to PAGE_EXECUTE_READWRITE
  010143A5   .  FFD5          CALL EBP                                 ;  virtualprotect
  010143A7   .  8D87 FF010000 LEA EAX,DWORD PTR DS:[EDI+1FF]
  010143AD   .  8020 7F       AND BYTE PTR DS:[EAX],7F                 ;  remove sec UPX0 UNINITIALIZED_DATA character
  010143B0   .  8060 28 7F    AND BYTE PTR DS:[EAX+28],7F              ;  remove sec UPX1 UNINITIALIZED_DATA character
  010143B4   .  58            POP EAX
  010143B5   .  50            PUSH EAX
  010143B6   .  54            PUSH ESP
  010143B7   .  50            PUSH EAX
  010143B8   .  53            PUSH EBX                                 ;  set to old protect
  010143B9   .  57            PUSH EDI
  010143BA   .  FFD5          CALL EBP                                 ;  virtualprotect
  这里首先使用VirtualProtect把文件头设置为PAGE_EXECUTE_READWRITE,获得文件头的写权限。
  然后
  010143AD   .  8020 7F       AND BYTE PTR DS:[EAX],7F                 ;  remove sec UPX0 UNINITIALIZED_DATA character
  010143B0   .  8060 28 7F    AND BYTE PTR DS:[EAX+28],7F              ;  remove sec UPX1 UNINITIALIZED_DATA character
  去除了节表中UPX0和UPX1段的UNINITIALIZED_DATA属性,完成了节表的初始化。有些版本UPX并没有这段代码。
  最后又用VirtualProtect恢复文件头属性。
  
  
  再往下看是一个大大的JMP
  010143CB   .- E9 CD2FFFFF   JMP NOTEPAD.0100739D                     ;  jmp to OEP
  跳转到OEP
  
  记下OEP的RAV为739D。
  使用LoadPE在OEP处Dump出镜像文件。
  
  使用ImportREC修复一下IAT
  IAT的起始地址为刚才记下的0x10000,通过观察那段内存得到IAT大小为0x344
  在ImportREC的IAT Infos Needed中填入我们获得的信息,便可以成功修复。
  
  测试一下脱出来的镜像,可以正确运行。
  
  当然你可以在找到OEP时直接用OllyDump插件直接脱壳和修复IAT。这里只是为了练手小小的绕了个弯。
  有兴趣的同学欢迎交流。

  附件为OD的调试文件,加壳后的记事本和原始的记事本程序。

--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年08月30日 15:59:39

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

上传的附件:
收藏
点赞7
打赏
分享
最新回复 (19)
雪    币: 7802
活跃值: (148)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
guobing 2009-8-30 18:36
2
0
深入学习一下。老是见到压缩壳就把ESP定律用上太单调了。
呵呵,找个时候也鄙视一下R公司。
雪    币: 102
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
jingru 2009-8-30 18:58
3
0
顶啊..upx脱壳不是问题,iat也不用考虑直接用od那个插件dump出来就行了..过程还得再看看
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cqyxyxyx 2009-8-30 22:55
4
0
Powerful!Surpport!
雪    币: 431
活跃值: (1875)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
riusksk 41 2009-8-30 23:14
5
0
support
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
guafeng 2009-9-7 13:06
6
0
好文,谢谢作者
雪    币: 324
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
CYBER涛 1 2009-9-7 16:43
7
0
不错,学习一下了!
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小黑冰 2009-9-16 13:34
8
0
支持下````
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zexp 2009-9-16 15:49
9
0
od 直接dump 出来的 更直接一下,还是支持
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
CncyTi 2009-9-17 11:08
10
0
授人以鱼,不如授人以渔。
雪    币: 159
活跃值: (339)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
Lenus 3 2010-7-9 18:14
11
0
1.nrv_ucl decompress
2.unfilter code section
3.fill iat
4.jmp oep
雪    币: 51
活跃值: (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yangand 2010-8-5 17:26
12
0
这个用ESP就直接脱了。
雪    币: 264
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
向xiang 2010-8-5 23:59
13
0
感谢提供~~~
雪    币: 128
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
狩望幸福 2010-8-6 08:38
14
0
受教了哦··能学到点东西··谢谢··
雪    币: 531
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zxlin 2010-8-6 15:08
15
0
来学下谢谢楼主分享
雪    币: 166
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
孤叶飘零 1 2010-10-18 23:25
16
0
先拷贝下来,再弄个算法的文章 一起看  谢谢楼主
雪    币: 58
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Modifix 2010-10-25 21:14
17
0
0101BEF0 >74E35E25  apphelp.74E35E25

这句是apphelp.74E35E25而非getprocaddress,请问这怎么解释???
雪    币: 58
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Modifix 2010-10-26 23:23
18
0
不好意思,我的系统可能被整了,重装系统后这里的确是GetProcAddress.

GetProcAddress是被HOOK了吗?
雪    币: 370
活跃值: (256)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
0aW5n 2021-1-13 13:19
19
0
写的很详细,学习了
雪    币: 1918
活跃值: (313)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wangez8 2021-3-18 14:18
20
0
谢谢楼主分享
游客
登录 | 注册 方可回帖
返回