【文章标题】: VMCraCk的双重爆破&&VM简要分析
【文章作者】: eASYVMBOOm
【作者主页】: http://blog.sina.com.cn/77muyulong
【软件名称】: RM090228_by_ShellWolf.exe
【下载地址】: http://bbs.pediy.com/showthread.php?t=82948
【加壳方式】: 无
【保护方式】: NoProtect
【使用工具】: OD winHEX
【软件介绍】: ShellWolf老大制作的一个CM
【作者声明】: 并没能按照老大的要求弄出算法 仅作学习笔记...
--------------------------------------------------------------------------------
【详细过程】
最近工作比较忙 好久都顾不上写点东西了 今天偶得ShellWolf老大制作的一个VMCraCk 爱不释手 奈何水平一般 仅做了部分分析 于是成文 仅为一学习笔记耳...
载入程序 搜索字符参考 找到
文本字符串参考位于 RM090228:.text,项目 11
地址=0040188C
反汇编=
push RM090228.00419680
文本字符=ASCII
"Success!"
跟过去看到了许多字符 这就是验证的核心了
代码
00401760 . 55
push ebp ; msgbox
00401761 . 8BEC
mov ebp,
esp
00401763 . 6A FF
push -1
00401765 . 68 A0964100
push RM090228.004196A0
0040176A . 68 A0424000
push RM090228.004042A0
; SE handler installation
0040176F . 64:A1 00000000
mov eax,
dword ptr fs:[0]
00401775 . 50
push eax
00401776 . 64:8925 00000000
mov dword ptr fs:[0],
esp
0040177D . 81EC 24030000
sub esp,324
00401783 . A1 F8FC4100
mov eax,
dword ptr ds:[41FCF8]
00401788 . 8945 E4
mov dword ptr ss:[
ebp-1C],
eax
0040178B . 53
push ebx
0040178C . 56
push esi
0040178D . 57
push edi
0040178E . 8965 E8
mov dword ptr ss:[
ebp-18],
esp
00401791 . 8BF1
mov esi,
ecx
00401793 . 89B5 D0FCFFFF
mov dword ptr ss:[
ebp-330],
esi
该函数用来读取注册名和验证吗
00401799 . 6A 0F
push 0F
; /Arg3 = 0000000F
0040179B . 8D45 D4
lea eax,
dword ptr ss:[
ebp-2C]
; |
0040179E . 50
push eax ; |Arg2
0040179F . 68 EA030000
push 3EA
; |Arg1 = 000003EA
004017A4 . E8 22250100
call <RM090228.GetText>
; \RM090228.00413CCB
00413CCB <>/$ 55
push ebp ; GetText
00413CCC |. 8BEC
mov ebp,
esp
00413CCE |. 8379 48 00
cmp dword ptr ds:[
ecx+48],0
00413CD2 |. 75 16
jnz short RM090228.00413CEA
00413CD4 |. FF75 10
push dword ptr ss:[
ebp+10]
; /Count
00413CD7 |. FF75 0C
push dword ptr ss:[
ebp+C]
; |Buffer
00413CDA |. FF75 08
push dword ptr ss:[
ebp+8]
; |ControlID
00413CDD |. FF71 1C
push dword ptr ds:[
ecx+1C]
; |hWnd
00413CE0 |. FF15 A0924100
call dword ptr ds:[<&USER32.GetDlgI>
; \GetDlgItemTextA
00413CE6 |. 5D
pop ebp
00413CE7 |. C2 0C00
retn 0C
作用同上
004017A9 . 8AD8
mov bl,
al
004017AB . 889D CCFCFFFF
mov byte ptr ss:[
ebp-334],
bl
004017B1 . 6A 40
push 40
; /Arg3 = 00000040
004017B3 . 8D8D D4FDFFFF
lea ecx,
dword ptr ss:[
ebp-22C]
; |
004017B9 . 51
push ecx ; |Arg2
004017BA . 68 EB030000
push 3EB
; |Arg1 = 000003EB
004017BF . 8BCE
mov ecx,
esi ; |
004017C1 . E8 05250100
call <RM090228.GetText>
; \RM090228.00413CCB
00413CCB <>/$ 55
push ebp ; GetText
00413CCC |. 8BEC
mov ebp,
esp
00413CCE |. 8379 48 00
cmp dword ptr ds:[
ecx+48],0
00413CD2 |. 75 16
jnz short RM090228.00413CEA
00413CD4 |. FF75 10
push dword ptr ss:[
ebp+10]
; /Count
00413CD7 |. FF75 0C
push dword ptr ss:[
ebp+C]
; |Buffer
00413CDA |. FF75 08
push dword ptr ss:[
ebp+8]
; |ControlID
00413CDD |. FF71 1C
push dword ptr ds:[
ecx+1C]
; |hWnd
00413CE0 |. FF15 A0924100
call dword ptr ds:[<&USER32.GetDlgI>
; \GetDlgItemTextA
00413CE6 |. 5D
pop ebp
00413CE7 |. C2 0C00
retn 0C
以下为判断验证码合法性的代码 长度必须要为0X10...
004017C6 . 80FB 04
cmp bl,4
; bl<0x4跳
004017C9 . 0F82 14010000
jb RM090228.004018E3
004017CF . 80FB 0F
cmp bl,0F
; bl>0xf跳
004017D2 . 0F87 0B010000
ja RM090228.004018E3
004017D8 . 3C 10
cmp al,10
; 长度为0x10
004017DA . 0F85 03010000
jnz RM090228.004018E3
先说主程序爆破 看代码
0040187A . E8 91FDFFFF
call <RM090228.VMFile>
0040187F . 83C4 10
add esp,10
00401882 . 6A 00
push 0
00401884 . 84C0
test al,
al
00401886 74 0B
je short RM090228.00401893
00401888 . 8D4D D4
lea ecx,
dword ptr ss:[
ebp-2C]
0040188B . 51
push ecx
0040188C . 68 80964100
push RM090228.00419680
; ASCII "Success!"
00401891 . EB 18
jmp short RM090228.004018AB
00401893 > 8D55 D4
lea edx,
dword ptr ss:[
ebp-2C]
00401896 . 52
push edx
00401897 . EB 0D
jmp short RM090228.004018A6
00401899 . B8 01000000
mov eax,1
0040189E . C3
retn
0040189F . 8B65 E8
mov esp,
dword ptr ss:[
ebp-18]
004018A2 . 6A 00
push 0
004018A4 . 6A 00
push 0
004018A6 > 68 78964100
push RM090228.00419678
; ASCII "Fail!"
004018AB > 8B8D D0FCFFFF
mov ecx,
dword ptr ss:[
ebp-330]
004018B1 . E8 78FD0000
call RM090228.0041162E
004018B6 . C745 FC FFFFFFFF
mov dword ptr ss:[
ebp-4],-1
004018BD . 68 6C964100
push RM090228.0041966C
; ASCII "Register"
004018C2 . 6A 01
push 1
004018C4 . 8BB5 D0FCFFFF
mov esi,
dword ptr ss:[
ebp-330]
004018CA . 8BCE
mov ecx,
esi
004018CC . E8 22240100
call RM090228.00413CF3
004018D1 . 6A 01
push 1
004018D3 . 6A 01
push 1
004018D5 . 8BCE
mov ecx,
esi
004018D7 . E8 CB230100
call RM090228.00413CA7
004018DC . 8BC8
mov ecx,
eax
004018DE . E8 D8240100
call RM090228.00413DBB
004018E3 > 8B4D F0
mov ecx,
dword ptr ss:[
ebp-10]
004018E6 . 64:890D 00000000
mov dword ptr fs:[0],
ecx
004018ED . 8B4D E4
mov ecx,
dword ptr ss:[
ebp-1C]
004018F0 . E8 60290000
call RM090228.00404255
004018F5 . 5F
pop edi
004018F6 . 5E
pop esi
004018F7 . 5B
pop ebx
004018F8 . 8BE5
mov esp,
ebp
004018FA . 5D
pop ebp
004018FB . C3
retn
主程序爆破十分简单 只要把
00401886 74 0B
je short RM090228.00401893
nop掉就可以了 输入任何0x10位的验证码 都可以通过...
接着深入 在判断之前看一下那个call 跟进
该函数用来load vmdata.dat走啊走 就到了
0040170C |. 52
push edx
0040170D |. 68 60964100
push RM090228.00419660
; ASCII "vmdata.dat"
00401712 |. E8 E9F9FFFF
call RM090228.00401100
00401717 |. E8 F4FAFFFF
call <RM090228.VMStart>
这个call跟进就到了VMengine
接下来是完整的VMengine 后面的注释是我的简要分析 由于时间因素 之分析了其中一个proc
VM的初始化 这个引擎没有初始化虚拟机环境 直接就是读取字节码执行了...
00401210 <> $ 55
push ebp ; VMstart
00401211 . 8BEC
mov ebp,
esp
00401213 . 83EC 2C
sub esp,2C
; enter 分配局部变量
00401216 . A1 F8FC4100
mov eax,
dword ptr ds:[41FCF8]
0040121B . 8945 F8
mov dword ptr ss:[
ebp-8],
eax
0040121E . 56
push esi
0040121F . A1 580C4200
mov eax,
dword ptr ds:[420C58]
00401224 . 8B0D 480C4200
mov ecx,
dword ptr ds:[420C48]
0040122A . 0308
add ecx,
dword ptr ds:[
eax]
0040122C . 890D 440C4200
mov dword ptr ds:[420C44],
ecx
00401232 > 8B15 440C4200
mov edx,
dword ptr ds:[420C44]
; 内存指针所指变量置寄存器edx
00401238 . 0FB602
movzx eax,
byte ptr ds:[
edx]
; 拓展复制edx指向的字节码
0040123B . 3D FF000000
cmp eax,0FF
; 比较字节码是否==0xFF
00401240 . 0F84 77030000
je RM090228.004015BD
; 是则跳=>VM_RETN
00401246 . 8B0D 440C4200
mov ecx,
dword ptr ds:[420C44]
; 内存指针所指变量置寄存器ecx
0040124C . 8A11
mov dl,
byte ptr ds:[
ecx]
; 字节码置dl
0040124E . 8855 E7
mov byte ptr ss:[
ebp-19],
dl ; 字节码置VM_CODE
00401251 . 0FB645 E7
movzx eax,
byte ptr ss:[
ebp-19]
; VM_CODE拓展置eax
00401255 . 8945 D4
mov dword ptr ss:[
ebp-2C],
eax ; VM_CODE置局部变量 VM_CODE2??
00401258 . 837D D4 05
cmp dword ptr ss:[
ebp-2C],5
; VM_CODE>0x5??
0040125C . 0F87 37030000
ja RM090228.00401599
; Y==>jmp
00401262 . 8B4D D4
mov ecx,
dword ptr ss:[
ebp-2C]
; VM_CODE置ecx
00401265 . FF248D CA154000
jmp dword ptr ds:[
ecx*4+4015CA]
; VMJMPTOPROC 基址4015ca共6个
读取字节码 参与运算 计算出procJMPaddress
可以看出 procJMPbase是4015CA
内存里看一下
004015CA 0040126C RM090228.0040126C
004015CE 00401310 RM090228.00401310
004015D2 00401351 RM090228.00401351
004015D6 004013F8 RM090228.004013F8
004015DA 0040146E RM090228.0040146E
004015DE 004014E4 RM090228.004014E4
总共6个过程 加上在这个之外的0XFF VM_RETN一共是7个
第一次读取的字节码是0X01 对应的过程为 RM090228.00401310
跳过去...
00401310 > \A1 440C4200
mov eax,
dword ptr ds:[420C44]
; proc2 取VMEIP
00401315 . 66:8B48 02
mov cx,
word ptr ds:[
eax+2]
; WORD[VMEIP+2]=>cx
00401319 . 66:894D EC
mov word ptr ss:[
ebp-14],
cx ; 保存cx该WORD
0040131D . 8B15 580C4200
mov edx,
dword ptr ds:[420C58]
; \
00401323 . 8B02
mov eax,
dword ptr ds:[
edx]
; |读取DWORD
00401325 . 83C0 08
add eax,8
; |==>DWODR[[00420c58]]+8
00401328 . 8B0D 580C4200
mov ecx,
dword ptr ds:[420C58]
; |存回DWORD
0040132E . 8901
mov dword ptr ds:[
ecx],
eax ; /
00401330 . 8B15 440C4200
mov edx,
dword ptr ds:[420C44]
; 取VMEIP
00401336 . 8B42 04
mov eax,
dword ptr ds:[
edx+4]
; DWORD[VMEIP+4]=>eax
00401339 . 8945 D8
mov dword ptr ss:[
ebp-28],
eax ; 保存eax该DWORD
0040133C . 0FB74D EC
movzx ecx,
word ptr ss:[
ebp-14]
; 拓展取WORD到ecx
00401340 . 8B15 5C0C4200
mov edx,
dword ptr ds:[420C5C]
; XXXXXXXX
00401346 . 8B45 D8
mov eax,
dword ptr ss:[
ebp-28]
; 取DWORD到eax
00401349 . 89048A
mov dword ptr ds:[
edx+
ecx*4],
eax ; mov [[420c5c]+WORD*4],DWORD
0040134C . E9 53020000
jmp RM090228.004015A4
004015A4 > \8B0D 580C4200
mov ecx,
dword ptr ds:[420C58]
; \
004015AA . 8B15 480C4200
mov edx,
dword ptr ds:[420C48]
; |
004015B0 . 0311
add edx,
dword ptr ds:[
ecx]
; |
004015B2 . 8915 440C4200
mov dword ptr ds:[420C44],
edx ; /修改VMEIP
004015B8 .^ E9 75FCFFFF
jmp RM090228.00401232
以上就是一个完整的VM过程 在每条指令后面都有我家的详细注释
以上这条指令就相当于写常数到内存地址
原字节码为
00F30040 01 00 61 03 DE 84 D7 08 .a?.
还原后为
mov [[420c5c]+361*4],08D784DE
也就是
01 00 YY YY XX XX XX XX
;******************************
mov [[420c5c]+YYYY*4],XXXXXXXX
这样一步步分析 方可得到类似于源码的算法 由于本人过菜 加之时间有限 所以无法完成算法分析 故追随某牛 另辟蹊径...
还是看验证上面的判断
00401884 . 84C0
test al,
al
00401886 74 0B
je short RM090228.00401893
此判断验证al是否为0 如果为0 则跳转 也就会出错 于是我们只要让VM返回个al!=0就可以了 如果我们把所有VM_code都搞清楚的话 这个应该不太难 这里偷了个懒 因为发现只要让VMengine直接返回 就可以通过验证 而返回的字节码我们已经知道是0XFF了 所以 就可以修改虚拟机的字节码 达到爆破的效果...
但是用WINHEX修改后却失败了 调试发现 如果只变这一个字节的话不能读出字节码 这个虚拟机的字解码是把vmdata.dat文件里数据抽出来并且经过本身数据的参与计算以后放到了内存里 修改这个字节会影响到后面运算的进行 所以会出现这样的错误 看来还是比较麻烦的...
校验运算代码
00401FFC |> /3306 /
xor eax,
dword ptr ds:[
esi]
00401FFE |. |83C6 04 |
add esi,4
00402001 |. |BB FF000000 |
mov ebx,0FF
00402006 |. |21C3 |
and ebx,
eax
00402008 |. |C1E8 08 |
shr eax,8
0040200B |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
0040200E |. |BB FF000000 |
mov ebx,0FF
00402013 |. |21C3 |
and ebx,
eax
00402015 |. |C1E8 08 |
shr eax,8
00402018 |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
0040201B |. |BB FF000000 |
mov ebx,0FF
00402020 |. |21C3 |
and ebx,
eax
00402022 |. |C1E8 08 |
shr eax,8
00402025 |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
00402028 |. |BB FF000000 |
mov ebx,0FF
0040202D |. |21C3 |
and ebx,
eax
0040202F |. |C1E8 08 |
shr eax,8
00402032 |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
00402035 |. |3306 |
xor eax,
dword ptr ds:[
esi]
00402037 |. |83C6 04 |
add esi,4
0040203A |. |BB FF000000 |
mov ebx,0FF
0040203F |. |21C3 |
and ebx,
eax
00402041 |. |C1E8 08 |
shr eax,8
00402044 |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
00402047 |. |BB FF000000 |
mov ebx,0FF
0040204C |. |21C3 |
and ebx,
eax
0040204E |. |C1E8 08 |
shr eax,8
00402051 |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
00402054 |. |BB FF000000 |
mov ebx,0FF
00402059 |. |21C3 |
and ebx,
eax
0040205B |. |C1E8 08 |
shr eax,8
0040205E |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
00402061 |. |BB FF000000 |
mov ebx,0FF
00402066 |. |21C3 |
and ebx,
eax
00402068 |. |C1E8 08 |
shr eax,8
0040206B |. |33049F |
xor eax,
dword ptr ds:[
edi+
ebx*4]
0040206E |. |49 |
dec ecx
0040206F |.^\0F85 87FFFFFF \jnz RM090228.00401FFC
应该说现在已经可以内存补丁了
至于是怎么做到DATAFile爆破的 等有时间在研究 或者青海风老师指点下.....
附上 DATAFile爆破修改的字节
Offsets: 16 进制
破 原
C: A0 EA
D: 55 50
E: 3C 48
F: EC C5
3B: FF 01
--------------------------------------------------------------------------------
【经验总结】
1 找准VM关键点最重要 VM中亦可爆破
2 注重VM外防爆破...
--------------------------------------------------------------------------------
【版权声明】: 本文首发于PEDIY(hIMcrACk) 一蓑烟雨(i++) 无版权 欢迎转载 欢迎大侠指点~
2009年03月01日 2:40:54
本代码由xTiNt自动着色
http://kbadboy.yeah.net