ximo 发表于 2010-8-6 18:36

庆祝A-new苏醒系列之二--加VMP外壳后的程序的爆破分析

本次分析的对象是加了VMP外壳的程序。当然如果你觉得脱VMP2.05的壳比直接爆破关键然后patch掉效验来的简单的话,你可以直接脱壳后进行破解。
试练品就是我发的那个crackme test2,地址:http://www.52pojie.net/thread-55207-1-1.html

首先还是写个简单的程序,做为试练品,来进行练习。
关键部分的源码如下:
int RegCode(char* szInput)
{
int len=strlen(szInput);
int sum=0;
if (len==0)
{
return 0;
}
for (int i=0;i<len;i++)
{
      sum+=szInput;
}
return sum;
}
void CCmDlg::OnButton1()
{
// TODO: Add your control notification handler code here
char szInput={0};
GetDlgItemText(IDC_EDIT1,szInput,255);
if (RegCode(szInput)==2012)
{
MessageBox("yes");
}
else
{
MessageBox("no");
}
}


然后,对编译后的程序,加上VMP的外壳,也就是默认选项,未对关键算法代码进行VM。
下面来进行破解。

一.寻找关键点:由于加了VMP外壳,我们也不打算对其进行脱壳,于是,直接在OD里F9运行,然后定位到代码段00401000,可以发现代码都已经解密了,接着直接查找字符串“yes”,就可以来到代码的关键部分:

004014B0    81EC 00010000      sub esp,100
004014B6    56                   push esi
004014B7    8BF1               mov esi,ecx
004014B9    57                   push edi
004014BA    B9 3F000000          mov ecx,3F
004014BF    33C0               xor eax,eax
004014C1    8D7C24 09            lea edi,dword ptr ss:
004014C5    C64424 08 00         mov byte ptr ss:,0
004014CA    68 FF000000          push 0FF
004014CF    F3:AB                rep stos dword ptr es:
004014D1    66:AB                stos word ptr es:
004014D3    AA                   stos byte ptr es:
004014D4    8D4424 0C            lea eax,dword ptr ss:
004014D8    8BCE               mov ecx,esi
004014DA    50                   push eax
004014DB    68 E8030000          push 3E8
004014E0    E8 69020000          call crackme2.0040174E
004014E5    8D4C24 08            lea ecx,dword ptr ss:
004014E9    51                   push ecx
004014EA    E8 91FFFFFF          call crackme2.00401480
004014EF    83C4 04            add esp,4
004014F2    3D DC070000          cmp eax,7DC
004014F7    6A 00                push 0
004014F9    6A 00                push 0
004014FB    75 15                jnz short crackme2.00401512
004014FD    68 24304000          push crackme2.00403024                               ; yes
00401502    8BCE               mov ecx,esi
00401504    E8 3F020000          call crackme2.00401748
00401509    5F                   pop edi
0040150A    5E                   pop esi
0040150B    81C4 00010000      add esp,100
00401511    C3                   retn
00401512    68 20304000          push crackme2.00403020                               ; no
00401517    8BCE               mov ecx,esi
00401519    E8 2A020000          call crackme2.00401748
0040151E    5F                   pop edi
0040151F    5E                   pop esi
00401520    81C4 00010000      add esp,100
00401526    C3                   retn


代码很清晰,爆破很容易。
004014FB    75 15                jnz short crackme2.00401512
把这句代码的jnz改成jz,也就是75改74,或者直接nop掉,即可完成爆破。
但是,由于加了外壳,不能直接修改后进行保存。不过我们找到了关键位置4014FB。

二.寻找解密过程,进行patch,实现爆破
重新加载程序,然后直接在4014FB下硬件写入断点,选择byte。F9运行,程序中断下来。
断下后看寄存器,注意EDI跟ESI的值:

ESI 0040E456 crackme2.0040E456   //待解密的数据
EDI 004014FC crackme2.004014FC //解密后存放的位置

然后单步跟,看解密算法:

0041B46E    AC                   lods byte ptr ds: //取待解密的数据
0041B46F    F5                   cmc
0041B470    C0C0 05            rol al,5
0041B473    C0FB 05            sar bl,5
0041B476    0F98C3               sets bl
0041B479    18F3               sbb bl,dh
0041B47B    04 47                add al,47
0041B47D    08EB               or bl,ch
0041B47F    F6D8               neg al
0041B481    28FB               sub bl,bh
0041B483    2C 1C                sub al,1C


0040FF44    F6D0               not al
0040FF46    F6D3               not bl


0041999E    AA                   stos byte ptr es: //写入解密后的数据
0041999F    9C                   pushfd


总结得到,解密算法为:
rol al,5
add al,47
neg al
sub al,1C
not al

下面我们来看下我们爆破的数据:
爆破点:4014FB 75---74
然后来计算下,74加密后的数据为多少。
写个解密函数的逆运算来进行解密吧:
mov al,74
not al
add al,1c
neg al
sub al,47
ror al,5

解密后可得,74加密后的数据为90.
于是,我们把4014fb这个地址的数据修改成90,保存一下,即可完成破解。


三.去除VMP效验
修改后,运行爆破后的程序,程序会提示错误,错误的提示为非法修改,于是下面的任务就是来去除效验。去效验,我使用的是最笨的方法,也就是开2个OD进行比较,分析代码的流程实在是个很蛋疼的体力活。
法1:
效验1:程序完整性的效验。
2个0D分别bp MapViewOfFile,然后运行。
断下后,根据上篇文章,直接找handle_nor32,
0041EA8D    8B55 04             mov edx,dword ptr ss:
这个这里下好断点,然后就是运行后进行比较。
可以发现,当esi==0x411b12时,被修改后的eflag值=0x202,而原程序的eflag=0x246

效验2:去掉第一个效验后,接着在上面我们修改的位置4014fb下硬件访问断点,byte就行,断下后,单步走几步看
0041E4D5    895C24 0C            mov dword ptr ss:,ebx
0041E4D9    881C24               mov byte ptr ss:,bl
0041E4DC    8D6424 34            lea esp,dword ptr ss:
0041E4E0    0F85 ED130000      jnz cm_vmp_c.0041F8D3 //解密循环
0041E4E6    68 7161C09F          push 9FC06171   //解密后的出口
0041E4EB    E8 D8080000          call cm_vmp_c.0041EDC8
0041E4F0    FF7424 04            push dword ptr ss:
0041E4F4    8F45 00            pop dword ptr ss:

这是个解密循环,直接在解密出口0041E4E6下好断点,F9运行。
解密的出口又是个VM,再次查找handle_nor32,进行比较
可以得到:当esi==0x41df21时,修改后的efalg=0x206,未修改后的为0x246,于是再次进行修改,这是patch点2

效验3:patch上面的2处后,程序还是出错。继续来进行patch。
继续在0041E4E6下好断点,F9运行,发现运行89后,程序出错,于是写个脚本定位到出错的前1次,

var time
mov time,0
loop:
run
inc time
cmp time,88
jne loop


同样,在原始的程序里,也运行脚本,中断下来。
接着,继续hook住handle_nor32,比较。
可得,当esi==0x41009时,修改后的eflag=0x202,原始的eflag=0x246
OK,修改完这3处后,程序可以正常运行了。
简单的patch代码如下:
原代码:

0041EA8D    8B55 04             mov edx,dword ptr ss:
0041EA90    F9                  stc
0041EA91    60                  pushad
0041EA92    80F9 04             cmp cl,4


patch后的:

0041EA8D   /E9 461F0000         jmp cm_vmp_c.004209D8
0041EA92   |80F9 04             cmp cl,4


004209D8    60                  pushad
004209D9    81FE 121B4100       cmp esi,cm_vmp_c.00411B12
004209DF    75 07               jnz short cm_vmp_c.004209E8
004209E1    C745 04 46020000    mov dword ptr ss:,246
004209E8    81FE 21DF4100       cmp esi,cm_vmp_c.0041DF21
004209EE    75 07               jnz short cm_vmp_c.004209F7
004209F0    C745 04 46020000    mov dword ptr ss:,246
004209F7    81FE 09004100       cmp esi,cm_vmp_c.00410009
004209FD    75 07               jnz short cm_vmp_c.00420A06
004209FF    C745 04 46020000    mov dword ptr ss:,246
00420A06    61                  popad
00420A07    8B55 04             mov edx,dword ptr ss:
00420A0A    F9                  stc
00420A0B    60                  pushad
00420A0C ^ E9 81E0FFFF         jmp cm_vmp_c.0041EA92


法2:
由法1可知效验数据的地方,于是直接在解密出口0041E4E6下后断点。
运行后,看寄存器,其中eax就为存放效验值的地方,这就是突破口。
比如:F9 4次后,修改后的为:
EAX 24652334
ECX 00000034
EDX 003DD3C4

而原来的程序为:
EAX 24652314
ECX 00000014
EDX 003DD3C4

发现效验值变了,那么到底用哪个寄存器做patch的参照寄存器呢?
比如用eax为参照,则patch后,效验值也就是eax就会改变,失败。
如果用ecx做参照,由于只是1个byte值,猜想会有重复,写个脚本跑一下可知,得确存在的重复,失败。而且根据不同的patch,ecx的值也会改变,失败。
于是,只能用edx作为参照寄存器。但是edx的值是否完全一致呢?发现,不同的机器,edx的值并不相同,比如本机,edx==0x003DD3C4,而虚拟机中则为:0x003CD3C4
但是也有发现,发现edx的低16位,也就是dx是一致的,于是选定edx的低16位作为参照。
于是,patch代码可以这样写:
cmp dx,XX //1个参照值
jne old
mov eax,yy //写入正确的效验值
jmp old

old:
//原始代码
jmp patch后的代码


具体的过程不再赘述了,方法就比较2边的效验值,然后进行写入。比如可以patch代码如下:
原代码:
0041E4E6    68 7161C09F         push 9FC06171
0041E4EB    E8 D8080000         call cm_vmp_c.0041EDC8
0041E4F0    FF7424 04         push dword ptr ss:
0041E4F4    8F45 00             pop dword ptr ss:

patch后的:

0041E4E6   /E9 D5220000         jmp cm_vmp_c.004207C0
0041E4EB   |E8 D8080000         call cm_vmp_c.0041EDC8
0041E4F0   |FF7424 04         push dword ptr ss:
0041E4F4   |8F45 00             pop dword ptr ss:
0041E4F7   |51                  push ecx


004207C0    66:81FA C4D3      cmp dx,0D3C4
004207C5    75 07               jnz short ximo6.004207CE
004207C7    B8 14236524         mov eax,24652314
004207CC    EB 36               jmp short ximo6.00420804
004207CE    66:81FA 0050      cmp dx,5000
004207D3    75 07               jnz short ximo6.004207DC
004207D5    B8 7A7DE99A         mov eax,9AE97D7A
004207DA    EB 28               jmp short ximo6.00420804
004207DC    66:81FA 6DE9      cmp dx,0E96D
004207E1    75 07               jnz short ximo6.004207EA
004207E3    B8 C23966A7         mov eax,A76639C2
004207E8    EB 1A               jmp short ximo6.00420804
004207EA    66:81FA F8FA      cmp dx,0FAF8
004207EF    75 07               jnz short ximo6.004207F8
004207F1    B8 D651DB65         mov eax,65DB51D6
004207F6    EB 0C               jmp short ximo6.00420804
004207F8    66:81FA 021A      cmp dx,1A02
004207FD    75 05               jnz short ximo6.00420804
004207FF    B8 C4A0613F         mov eax,3F61A0C4
00420804    68 7161C09F         push 9FC06171
00420809 ^ E9 DDDCFFFF         jmp ximo6.0041E4EB


保存修改后,同样正常运行。

总结下:2种方法都采用了最笨也是最直接的对比法,当然,可能还存在更为简洁的方法,能力有限,如有错误,请多多指点。

附件为试练品以及用2种方法爆破后的。

ximo

wuhanqi 发表于 2010-8-6 18:42

这么好的文章没人支持?

Zanker 发表于 2010-8-6 18:44

我的沙发...
又没了....

qq20048888 发表于 2010-8-6 18:45

来支持一下..

张相公 发表于 2010-8-6 18:45

怎么可能没人支持

sdnyzjzx 发表于 2010-8-6 19:08

现在还学不会这个,慢慢来。

hixiaosheng 发表于 2010-8-6 19:20

我来啦ximo哥~~

小Z 发表于 2010-8-6 19:32

恩,不错,支持

sunlili001 发表于 2010-8-6 19:32

ximo大大出品 都是精品么 哈哈!

飞刀 发表于 2010-8-6 19:36

再来支持这个,,,

最后再祝A哥早日康复
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 庆祝A-new苏醒系列之二--加VMP外壳后的程序的爆破分析