TA的每日心情 | 开心 2011-8-9 18:15 |
---|
签到天数: 8 天 [LV.3]偶尔看看II
|
楼主 |
发表于 2010-1-23 19:36:29
|
显示全部楼层
Enigma主程序脱壳系列之二-----主程序脱壳
本文来自: UPK软件安全社区 作者: hyperchem 日期: 2009-4-21 16:45 阅读: 1753人 收藏
脱壳, Enigma, 主程序
Enigma主程序脱壳系列之二-----主程序脱壳
写在前面:在研究IAT解密的过程中,ZeNiX大哥帮了不少忙,在此表示感谢!
上篇文章讨论了提取Enigma里面捆绑的DLL文件,请确认你现在已经把Dll文件都提取并修复好了。因为这次脱壳的时候需要用到这些dll。一共三个dllll_Loader.dll,informer.dll,disasm.dll,dll的名称可以从dll的输出表里面看到。
还记得这段代码么?是壳释放Dll_Loader.dll并加载以后的代码:
00000: 00A61C18 54 push esp
00001: 00A61C19 64:FF35 0000000>push dword ptr fs:[0]
00002: 00A61C20 68 601D6600 push Enigma.00661D60
00003: 00A61C25 56 push esi---------------------------这里esi放的是dll的基址
00004: 00A61C26 55 push ebp---------------------------这里是exe的基址
00005: 00A61C27 8B46 3C mov eax,dword ptr ds:[esi+3C]------获取PE标志的偏移
00006: 00A61C2A 8B8430 78000000 mov eax,dword ptr ds:[eax+esi+78]--获取输出表的RVA
00007: 00A61C31 03C6 add eax,esi------------------------加上基址得到输出表的VA
00008: 00A61C33 81C0 28000000 add eax,28-------------------------获取输出表函数表的VA
00009: 00A61C39 60 pushad
00010: 00A61C3A 8B40 04 mov eax,dword ptr ds:[eax+4]-------获取输出表中的第二个函数Init的RVA
00011: 00A61C3D 56 push esi
00012: 00A61C3E 03C6 add eax,esi------------------------加上基址
00013: 00A61C40 FFD0 call eax---------------------------调用Init这个函数初始化dll运行环境
00014: 00A61C42 61 popad
00015: 00A61C43 90 nop
00016: 00A61C44 8B40 00 mov eax,dword ptr ds:[eax]---------这里是获取输出表中的第一个函数Start的RVA
00017: 00A61C47 90 nop
00018: 00A61C48 03C6 add eax,esi------------------------加上基址
00019: 00A61C4A 56 push esi---------------------------dll基址入栈
00020: 00A61C4B 6A 00 push 0-----------------------------另一个参数入栈
00021: 00A61C4D 6A 01 push 1-----------------------------再入栈一个参数
00022: 00A61C4F 55 push ebp---------------------------exe基址入栈
00023: 00A61C50 50 push eax---------------------------start函数地址入栈
00024: 00A61C51 8B46 3C mov eax,dword ptr ds:[esi+3C]------还是PE标志
00025: 00A61C54 8B8430 28000000 mov eax,dword ptr ds:[eax+esi+28]--获取dll的入口点RVA
00026: 00A61C5B 03C6 add eax,esi------------------------加上基址
00027: 00A61C5D 50 push eax---------------------------入口点VA入栈
00028: 00A61C5E C3 retn-------------------------------跳到dll入口点
从上面的分析看出,在跳到dll入口点进行执行之前,因为没有调用标准的LoadLibraryA,所以这些需要自己初始化dll的运行环境,这里初始化的过程是用init完成的。接下来,壳在堆栈中压入了4个数和1个函数地址。这个干什么的呢?往下看~
再打开一个OD。随便找一个dll 用OD载入。停在入口点以后 我们看下堆栈顶的值:
00000: 0013F898 7C92118A 返回到 ntdll.7C92118A-----------这个堆栈顶
00001: 0013F89C 10000000 ASCII "MZP"
00002: 0013F8A0 00000001
00003: 0013F8A4 00000000
00004: 0013F8A8 1009FB74 DLL_Load.<ModuleEntryPoint>
这个值是干什么的呢?
dll初始化完成以后,会返回到这个地址,等待消息循环和函数调用。
同样的道理,Enigma中的dll在入口点时,堆栈如下:
00000: 0013FF7C 00B02740 Enigma.00B02740------------ESP
00001: 0013FF80 00400000 ASCII "MZP"
00002: 0013FF84 00000001
00003: 0013FF88 00000000
00004: 0013FF8C 00A63000 ASCII "MZP"
所以上面那段代码的意思就是,先把Start这个函数的地址和需要的参数全部入栈,然后在dll初始化完成以后马上跳到Start函数开始执行。
这样就巧妙的完成了
LoadLibraryA
GetProcAddress
Call eax
的功能
相信很多人在单步跟踪的时候 跟进这dll入口点以后就会被搞晕了。这样的分析以后,大家应该就知道该怎么办了吧?
he 00B02740,然后运行。我们就到了程序解码的关键位置。
00000: 00B02740 55 push ebp ---停在这里
00001: 00B02741 8BEC mov ebp,esp
00002: 00B02743 53 push ebx
00003: 00B02744 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
00004: 00B02747 A1 B0EFB000 mov eax,dword ptr ds:[B0EFB0]
00005: 00B0274C 8918 mov dword ptr ds:[eax],ebx
00006: 00B0274E A1 98EEB000 mov eax,dword ptr ds:[B0EE98]
00007: 00B02753 8B55 0C mov edx,dword ptr ss:[ebp+C]
00008: 00B02756 8910 mov dword ptr ds:[eax],edx
00009: 00B02758 E8 5FDEF9FF call Enigma.00AA05BC
00010: 00B0275D 8B45 10 mov eax,dword ptr ss:[ebp+10]
00011: 00B02760 03C3 add eax,ebx
00012: 00B02762 E8 A1FFFFFF call Enigma.00B02708
00013: 00B02767 E8 90E7FEFF call Enigma.00AF0EFC
00014: 00B0276C 8B55 18 mov edx,dword ptr ss:[ebp+18]
00015: 00B0276F 8B45 14 mov eax,dword ptr ss:[ebp+14]
00016: 00B02772 E8 DDF4FFFF call Enigma.00B01C54
00017: 00B02777 5B pop ebx
00018: 00B02778 5D pop ebp
00019: 00B02779 C2 1400 retn 14
下面是脱壳的方法加上少量的分析,详尽的分析将在以后发布。
一,先去掉代码的校验,方便我们patch代码和下断点。
这里就是一个典型的校验部分,而Enigma的代码校验也只有这一种形式。分析一下:
00000: 00B01DA6 E8 258AFEFF call Enigma.00AEA7D0------------- 生成随机数
00001: 00B01DAB 50 push eax--------------------------随机数入栈,这里很重要的说
00002: 00B01DAC 89C1 mov ecx,eax-----------------------把随机数赋值给ecx作为下面call的参数
00003: 00B01DAE B8 EC1DB000 mov eax,Enigma.00B01DEC-----------这个赋值很重要,给eax的值就是要跳转到的目标地址
00004: 00B01DB3 E8 C489FEFF call Enigma.00AEA77C--------------这个是代码校验的关键call
00005: 00B01DB8 010424 add dword ptr ss:[esp],eax--------把校验call算出的值加上esp的值 如果校验不成功 则返回地址就会被破坏
00006: 00B01DBB C3 retn
00AEA77C这个call的功能是:计算代码的CRC,并同随机数进行计算,返回值=跳转目标地址-随机数(ecx)-正确的CRC+计算出的CRC
从上面的公式可以看出,只有当CRC校验正确的时候,返回的地址才是对的。我们需要patch这个call的代码。
从分析中可以看出 ecx中试随机数,eax是返回地址。所以这样修改:
00000: sub eax,ecx
00001: retn
这样修改三个字节就可以过掉壳对代码的校验了。
代码校验一共五处,四处是作为代码转移,一处是跳到OEP之前。前四处是一样,最后一处少有差别。但是校验方式本质上都是同一种。下面列出了剩下的三处校验。第五处请见后面。
00000: 2,
00001: 00B01FC2 E8 0988FEFF call Enigma.00AEA7D0
00002: 00B01FC7 50 push eax
00003: 00B01FC8 89C1 mov ecx,eax
00004: 00B01FCA B8 0820B000 mov eax,Enigma.00B02008
00005: 00B01FCF E8 A887FEFF call Enigma.00AEA77C
00006: 00B01FD4 010424 add dword ptr ss:[esp],eax
00007: 00B01FD7 C3 retn
00008: 3,
00009: 00B022C5 E8 0685FEFF call Enigma.00AEA7D0
00010: 00B022CA 50 push eax
00011: 00B022CB 89C1 mov ecx,eax
00012: 00B022CD B8 0B23B000 mov eax,Enigma.00B0230B
00013: 00B022D2 E8 A584FEFF call Enigma.00AEA77C
00014: 00B022D7 010424 add dword ptr ss:[esp],eax
00015: 00B022DA C3 retn
00016: 4,
00017: 00B024F1 E8 DA82FEFF call Enigma.00AEA7D0
00018: 00B024F6 50 push eax
00019: 00B024F7 89C1 mov ecx,eax
00020: 00B024F9 B8 3725B000 mov eax,Enigma.00B02537
00021: 00B024FE E8 7982FEFF call Enigma.00AEA77C
00022: 00B02503 010424 add dword ptr ss:[esp],eax
00023: 00B02506 C3 retn
二,patch代码跳过IAT加密。
1,解除API模拟。壳会IAT中的API地址替换为模拟的API地址,处理下面两处来解除API模拟。
00000: 00AF9DBD 894C82 04 mov dword ptr ds:[edx+eax*4+4],ecx------------这条指令nop
00001:
00002: 00AF985E A1 B4EFB000 mov eax,dword ptr ds:[B0EFB4]
00003: 00AF9863 80B8 725E0000 0>cmp byte ptr ds:[eax+5E72],0
00004: 00AF986A 74 76 je short Enigma.00AF98E2----------------------这个改成jmp
2,解除API加密。壳函数对API进行加密。也需要跳出去。
00000: 00AFA9EA 894C82 04 mov dword ptr ds:[edx+eax*4+4],ecx------------这里nop掉
处理掉上面三处后就完全解密IAT了。
需要补充的是,上面的四处patch对于所有用Enigma 1.65加密的程序都适用。只要转换一下VA就行了。
三,到OEP去。
在dll的领空中 搜索指令jmp eax
找到类似这样的代码:
00000: 00AF5671 8B45 FC mov eax,dword ptr ss:[ebp-4]
00001: 00AF5674 FFE0 jmp eax------------这里就是马上就要到OEP去了。
00002: 00AF5676 5F pop edi
00003: 00AF5677 5E pop esi
00004: 00AF5678 5B pop ebx
00005: 00AF5679 8BE5 mov esp,ebp
00006: 00AF567B 5D pop ebp
00007: 00AF567C C2 0C00 retn 0C
下好断点。运行。暂停一下,跟进去以后,就到了下面的代码。
这段代码是到OEP前的最后一段校验。
00000: 012376F4 E8 D7308BFF call Enigma.00AEA7D0----------------生成随机数
00001: 01237702 8905 8C02B200 mov dword ptr ds:[B2028C],eax-------保存随机数
00002: 01237711 B8 10802C01 mov eax,12C8010---------------------这个真正的OEP(或被抽走的)
00003: 0123771F 8B0D 8C02B200 mov ecx,dword ptr ds:[B2028C]-------随机数给ECX作为下面call的参数
00004: 0123772E E8 49308BFF call Enigma.00AEA77C----------------计算代码的CRC,并同随机数进行计算,返回值为OEP-随机数(ecx)-正确的CRC+计算出的CRC
00005: 0123773C 8905 9002B200 mov dword ptr ds:[B20290],eax-------保存返回值
00006: 0123774B B8 E0FF1300 mov eax,13FFE0
00007: 01237759 64:8905 0000000>mov dword ptr fs:[0],eax
00008: 01237769 BC 64FF1300 mov esp,13FF64
00009: 01237777 B8 10802C01 mov eax,12C8010
00010: 01237785 8B05 9002B200 mov eax,dword ptr ds:[B20290]-------返回值入栈
00011: 01237794 50 push eax
00012: 0123779E 8B05 8C02B200 mov eax,dword ptr ds:[B2028C]-------取最开始的随机数
00013: 012377AD 010424 add dword ptr ss:[esp],eax----------加上返回值。只有当CRC正确的时候这个值才对。
00014: 012377B9 8BC4 mov eax,esp
00015: 012377C4 81E8 7C000000 sub eax,7C
00016: 012377DC 68 00000000 push 0
00017: 012377EA 3BE0 cmp esp,eax
00018: 012377F5 ^\0F85 D8FFFFFF jnz 012377DC
00019: 01237804 81C4 7C000000 add esp,7C
00020: 01237813 58 pop eax
00021: 0123781D C78424 04000000>mov dword ptr ss:[esp+4],0
00022: 01237843 FFE0 jmp eax-----------------------------这里就跳到了OEP(或被抽走的)
四,修复Stolen Code
跟进上面的jmp eax就到了下面代码:
00000: 012D801C 55 push ebp
00001: 012D801D 8BEC mov ebp,esp
00002: 012D801F 83C4 F0 add esp,-10
00003: 012D8022 53 push ebx
00004: 012D8023 B8 8CFB7F00 mov eax,7FFB8C
00005: 012D8028 E8 8BEA12FF call Enigma.00406AB8
00006: 012D802D 8B1D A4688100 mov ebx,dword ptr ds:[8168A4] ; Enigma.00817C1C
00007: 012D8033 8B43 00 mov eax,dword ptr ds:[ebx]
00008: 012D8036 E8 69581CFF call Enigma.0049D8A4
00009: 012D803B 8B43 00 mov eax,dword ptr ds:[ebx]
00010: 012D803E BA 04098000 mov edx,800904 ; ASCII "The Enigma Protector"
00011: 012D8043 E8 54541CFF call Enigma.0049D49C
00012: 012D8048 B2 01 mov dl,1
00013: 012D804A 8B05 88425300 mov eax,dword ptr ds:[534288] ; Enigma.005342D4
00014: 012D8050 E8 CFC325FF call Enigma.00534424
00015: 012D8055 8B15 406B8100 mov edx,dword ptr ds:[816B40] ; Enigma.0081EE10
00016: 012D805B 8902 mov dword ptr ds:[edx],eax
00017: 012D805D 8B0D 80638100 mov ecx,dword ptr ds:[816380] ; Enigma.0093184C
00018: 012D8063 8B43 00 mov eax,dword ptr ds:[ebx]
00019: 012D8066 8B15 EC0A5B00 mov edx,dword ptr ds:[5B0AEC] ; Enigma.005B0B38
00020: 012D806C E8 4B581CFF call Enigma.0049D8BC
00021: 012D8071 8B0D 58678100 mov ecx,dword ptr ds:[816758] ; Enigma.00931828
00022: 012D8077 8B43 00 mov eax,dword ptr ds:[ebx]
00023: 012D807A 8B15 38147E00 mov edx,dword ptr ds:[7E1438] ; Enigma.007E1484
00024: 012D8080 E8 37581CFF call Enigma.0049D8BC
00025: 012D8085 8B0D F0678100 mov ecx,dword ptr ds:[8167F0] ; Enigma.0081F270
00026: 012D808B 8B43 00 mov eax,dword ptr ds:[ebx]
00027: 012D808E 8B15 C0055B00 mov edx,dword ptr ds:[5B05C0] ; Enigma.005B060C
00028: 012D8094 E8 23581CFF call Enigma.0049D8BC
00029: 012D8099 8B0D C86A8100 mov ecx,dword ptr ds:[816AC8] ; Enigma.00931834
00030: 012D809F 8B43 00 mov eax,dword ptr ds:[ebx]
00031: 012D80A2 8B15 80177E00 mov edx,dword ptr ds:[7E1780] ; Enigma.007E17CC
00032: 012D80A8 E8 0F581CFF call Enigma.0049D8BC
00033: 012D80AD B2 01 mov dl,1
00034: 012D80AF 8B05 607D7E00 mov eax,dword ptr ds:[7E7D60] ; Enigma.007E7DAC
00035: 012D80B5 E8 D2FF50FF call Enigma.007E808C
00036: 012D80BA 8B15 8C698100 mov edx,dword ptr ds:[81698C] ; Enigma.00931850
00037: 012D80C0 8902 mov dword ptr ds:[edx],eax
00038: 012D80C2 8B05 8C698100 mov eax,dword ptr ds:[81698C] ; Enigma.00931850
00039: 012D80C8 8B40 00 mov eax,dword ptr ds:[eax]
00040: 012D80CB E8 E8FC50FF call Enigma.007E7DB8
00041: 012D80D0 8B0D D4658100 mov ecx,dword ptr ds:[8165D4] ; Enigma.009313E4
00042: 012D80D6 8B43 00 mov eax,dword ptr ds:[ebx]
00043: 012D80D9 8B15 880F7C00 mov edx,dword ptr ds:[7C0F88] ; Enigma.007C0FD4
00044: 012D80DF E8 D8571CFF call Enigma.0049D8BC
00045: 012D80E4 8B0D 08648100 mov ecx,dword ptr ds:[816408] ; Enigma.00931818
00046: 012D80EA 8B43 00 mov eax,dword ptr ds:[ebx]
00047: 012D80ED 8B15 E4C47D00 mov edx,dword ptr ds:[7DC4E4] ; Enigma.007DC530
00048: 012D80F3 E8 C4571CFF call Enigma.0049D8BC
00049: 012D80F8 8B0D DC668100 mov ecx,dword ptr ds:[8166DC] ; Enigma.0093180C
00050: 012D80FE 8B43 00 mov eax,dword ptr ds:[ebx]
00051: 012D8101 8B15 40B87D00 mov edx,dword ptr ds:[7DB840] ; Enigma.007DB88C
00052: 012D8107 E8 B0571CFF call Enigma.0049D8BC
00053: 012D810C 8B0D 346C8100 mov ecx,dword ptr ds:[816C34] ; Enigma.0081F250
00054: 012D8112 8B43 00 mov eax,dword ptr ds:[ebx]
00055: 012D8115 8B15 24A75A00 mov edx,dword ptr ds:[5AA724] ; Enigma.005AA770
00056: 012D811B E8 9C571CFF call Enigma.0049D8BC
00057: 012D8120 8B0D DC6B8100 mov ecx,dword ptr ds:[816BDC] ; Enigma.0081F260
00058: 012D8126 8B43 00 mov eax,dword ptr ds:[ebx]
00059: 012D8129 8B15 3CE45A00 mov edx,dword ptr ds:[5AE43C] ; Enigma.005AE488
00060: 012D812F E8 88571CFF call Enigma.0049D8BC
00061: 012D8134 8B0D 48678100 mov ecx,dword ptr ds:[816748] ; Enigma.0081F258
00062: 012D813A 8B43 00 mov eax,dword ptr ds:[ebx]
00063: 012D813D 8B15 B4AD5A00 mov edx,dword ptr ds:[5AADB4] ; Enigma.005AAE00
00064: 012D8143 E8 74571CFF call Enigma.0049D8BC
00065: 012D8148 8B05 80638100 mov eax,dword ptr ds:[816380] ; Enigma.0093184C
00066: 012D814E 8B40 00 mov eax,dword ptr ds:[eax]
00067: 012D8151 E8 66B41AFF call Enigma.004835BC
00068: 012D8156 50 push eax
00069: 012D8157 E8 A4F812FF call Enigma.00407A00
00070: 012D815C 8B43 00 mov eax,dword ptr ds:[ebx]
00071: 012D815F E8 D8571CFF call Enigma.0049D93C
00072: 012D8164 8B05 8C698100 mov eax,dword ptr ds:[81698C] ; Enigma.00931850
00073: 012D816A 8B40 00 mov eax,dword ptr ds:[eax]
00074: 012D816D E8 1EB512FF call Enigma.00403690
00075: 012D8172 5B pop ebx
00076: 012D8173 E8 D4C112FF call Enigma.0040434C
00077: 012D8178 - E9 7E8752FF jmp Enigma.008008FB-------------1
怎么看都别扭,这虽然是OEP的特征 但是所在的位置不对,不在原程序的代码段。这段就是被偷走的OEP处的代码。我们需要把这些代码贴回去。那么它原始的位置在哪里呢?看上面的1处。这个跳转的目标地址的上面就是原始的OEP。
00000: 008007B4 2E db 2E ; CHAR '.'---这里就是原始OEP的位置。
00001: 008007B5 A1 db A1
00002: 008007B6 EF db EF
00003: 008007B7 49 db 49 ; CHAR 'I'
00004: 008007B8 28 db 28 ; CHAR '('
00005: 008007B9 1B db 1B
把代码贴回到这里。就完成了Stolen Code的修复.
五,后期修复。
现在不dump么?别慌,还有一些没有修复呢。
还记得被捆绑的三个dll么? 这三个dll同样是要被Enigma的主程序调用的。那么也就说输入表中会引用这些dll的。虽然这时程序的IAT中这些dll中的函数地址,但是因为这三dll不是正常载入的。内存中没有这个模块 OD中无法识别这些函数,ImportREC也无法识别。看下面的IAT调用就知道了,这明明是调用Dll_Loader.dll中的函数 但是无法正确显示。所以这就是我们要修复的内容。
00000: 005492BC $- FF25 6CB52101 jmp dword ptr ds:[121B56C] ; Enigma.00AE0E20
00001: 005492C2 8BC0 mov eax,eax
00002: 005492C4 $- FF25 60B52101 jmp dword ptr ds:[121B560] ; Enigma.00AE1434
00003: 005492CA 8BC0 mov eax,eax
00004: 005492CC $- FF25 54B52101 jmp dword ptr ds:[121B554] ; Enigma.00AE17C0
00005: 005492D2 8BC0 mov eax,eax
00006: 005492D4 $- FF25 48B52101 jmp dword ptr ds:[121B548] ; Enigma.00AE17FC
00007: 005492DA 8BC0 mov eax,eax
00008: 005492DC $- FF25 3CB52101 jmp dword ptr ds:[121B53C] ; Enigma.00AE065C
我们需要把这三个dll都用正常的方式载入内存中,然后根据载入的dll基址修正IAT。
找个空白的地方写一段代码
00000: push xxxxxx
00001: Call LoadLibraryA
00002: xxxxxx是指dll名称所在的首地址
载入三个dll以后 记住三个dll载入后的基址。
DLL名称 当前基址 载入后的基址
DLL_Loader.dll 00A63000 AAAAAAA
informer.dll 00B4B000 BBBBBBB
disasm.dll 00B88000 CCCCCCC
计算载入后基址和当前基址的差值 然后手动加到这些dll的IAT中。
全部修复完成以后。使用UIF把IAT表移动回程序中。自己找个空白的地方。
剩下的工作就简单了。Dump,ImportREC重建输入表。
脱壳完成。 |
|