看了deroko 的《Ripping VB code
and making keygen
out of it》,觉得写得非常好,给写算法注册机带来了不少方便之处。但是作者在最后部分特别是写注册机方面过于烦琐,不直观,尤其是其中引用大部分的宏在TASM中编译的时带来不少麻烦,所以我重新编辑了下,连翻带改写了这篇文章,里面的许多内容及一些处理方法会跟作者的的有所出入,并且添加了许多内容。附上在RADASM中编译通过的MASM32代码。
一、找到关键算法处
这是个VB写的CRACKME,先用VB的反编译利器P32Dasm(http://t4c.ic.cz/forum/showthread.php?t=67)反编译这个软件,得到以下信息:
文件: C:\Documents
and Settings\ crackme\crackme.exe
P32Dasm v2.3
VB6 编译类型: NCode
frm1
004028F4 1.1 cmdExit.Click()
004029C6 1.2 cmdRegister.Click() 注册按钮处理过程
00402BD3 1.3 Command1.Click()
00402D3D 1.4 Command2.Click()
0040315C 1.5 Form.Load()
00403332 1.6 txtReg.KeyPress(KeyAscii As Integer)
文件反编译结束。
从这里,我们得到信息,该CRACKME的按钮处理过程在004029C6处。
用OD载入,在004029C6处下断,运行程序后随便输入注册码,按注册后断下。单步跟踪,到下面代码:
00402AB2 . 68 24404000
PUSH crackme.00404024
; 00404024 001540A4 UNICODE "Serializer"00402AB7 . 50
PUSH EAX ; 0012F4EC 0015A04C UNICODE "6D6074621C09010205737401740207"00402AB8 . E8 00090000
CALL crackme.004033BD
; 关键算法00402ABD . 8BD0
MOV EDX,
EAX ; 得到注册码00402ABF . 8D4D DC
LEA ECX,
DWORD PTR SS:[
EBP-24]
00402AC2 . E8 99E7FFFF
CALL <JMP.&msvbvm60.__vbaStrMove>
00402AC7 . 50
PUSH EAX00402AC8 . E8 99E7FFFF
CALL <JMP.&msvbvm60.__vbaStrCmp>
; 对比注册码00402ACD . 8BF0
MOV ESI,
EAX 将OD中的内存数据窗口切换到“长型”-“地址”,我们在00402AB2和00402AB7处右击-在数据窗口中跟随,可以看到这两处分别是UNICODE字符串“Serializer”和机器码,00402AB8处是关键算法,00402ABD处得到真正的注册码,所以00402AB8处的CALL非常关键,但是我们现在不想了解其中的算法过程,只想拷贝CALL 004033BD里面的代码做注册机。
二、截取代码
启动IDA,反汇编该CRACKME,“跳转到指定地址”,填入004033BD,将004033BD到00403613处代码涂黑(即CALL 004033BD的整个内容),选择菜单“文件”-创建文件-创建ASM文件,我们保存为crackme.asm。
我们将这段代码随便找个汇编注册机模版放进去,这里将它放到我修改的ARTEAM的一个模版里。在RADASM中编译,让它列出错误。
三、分析代码
先讲一些基本的错误:
1、seg000:004033C3
push (
offset loc_401175+1)
从IDA中可以看出这是个压入异常句柄,置0即可,我们把它改为:
push offset dummy_ seh
…
dummy_seh:
xor eax,
eax ret2、mov
eax, large
fs:0
此处的large是修饰符,直接去掉,“fs:0” MASM也认不出来,我们在.DATA区加入
assume fs:
nothing3、seg000:004033DF
mov dword ptr [
ebp-8],
offset dword_401160
我们在OD中可以看到,dword_401160= 8000Eh
所以在.DATA处加入:
dword_401160
dd 8000Eh
4、seg000:00403572
push offset dword_4027BC
这里其实IDA反汇编错误(因为VB的字符串是以UNICODE形式存在的),dword_4027BC 是字符串
"0" 和 2 ,即:
dd 2 <-- --- 字符串长度
string_0
dd 30h <----- 字符串0
所以在.DATA处加入:
dd 2
dword_4027BC
dd 30h
5、jo loc_403616
push offset loc_403602
jmp short loc_4035E9
这几个可以在IDA中直接跟到代码处拷贝相应的内容:
loc_403616:
; CODE XREF: seg000:00403488 j ; seg000:004034F2 j ... call __vbaErrorOverflow
loc_403602:
; DATA XREF: seg000:004035B9 o mov ecx, [
ebp-14h]
mov eax, [
ebp-20h]
pop edi pop esi mov large
fs:0,
ecx pop ebx leave retn 8
loc_4035E9:
; CODE XREF: seg000:004035BE j lea ecx, [
ebp-24h]
call __vbaFreeStr
lea ecx, [
ebp-28h]
call __vbaFreeStr
lea ecx, [
ebp-2Ch]
call __vbaFreeStr
retn 现在来讲一个重点错误:
就是所有的VB内部函数,这个在MASM中当然不可能被识别。但是…
感谢Vortex为我们解决了这个问题,它的Dll2inc(http://www.vortex.masmcode.com/)轻松解决了这个烦恼。Vortex原本是想用来生成可以供MASM调用的C运行库的,可是我拿VB的运行库msvbvm60.dll测试了一下,居然成功了。
拿msvbvm60.dll经过Dll2inc转换后得到msvbvm60.def、msvbvm60.inc、msvbvm60.lib,这样就可以在MASM32中调用了。他专门定义了个调用这些函数的宏:cinvoke。我们只要在include和includelib中加入这些INC文件和LIB文件,然后将所有的调用VB内部函数的CALL改为cinvoke就可以“光明正大”的用这些VB内部函数了。
RADASM中编译,可是还是有错误。
我们用OD来跟一下到底问题出在哪里。RADASM中设置好调试器关联后,选择“构建”-“在调试器中运行”,OD自动反汇编了这个有问题的KEYGEN。我们逐步跟踪,到下面的代码时,出现异常,问题就出在__vbaStrCat中。
004012BC . E8 2B020000
CALL <JMP.&msvbvm60.__vbaStrCat>
我们跟入__vbaStrCat:
660E5F3A > 55
PUSH EBP660E5F3B 8BEC
MOV EBP,
ESP660E5F3D 8D45 08
LEA EAX,
DWORD PTR SS:[
EBP+8]
660E5F40 50
PUSH EAX660E5F41 FF75 08
PUSH DWORD PTR SS:[
EBP+8]
660E5F44 FF75 0C
PUSH DWORD PTR SS:[
EBP+C]
660E5F47 FF15 18EE1066
CALL DWORD PTR DS:[6610EE18]
; DS:[6610EE18]=00000000,问题出在这里 跟到660E5F47时出现异常,我们看到PTR
DS:[6610EE18]为0,我们拿原来的CRACKME或者直接拿msvbvm60.dll反汇编,跟入__vbaStrCat:
660E5F47 FF15 18EE1066
CALL DWORD PTR DS:[6610EE18]
; OLEAUT32.VarBstrCat 可以看出问题所在:PTR
DS:[6610EE18]在CRACKEME中有被初始化成VarBstrCat而在KEYGEN中没有。
我们用IDA反汇编msvbvm60.dl后,跟踪__vbaStrCat函数:
.text:660E5F40
push eax.text:660E5F41
push [
ebp+arg_0]
.text:660E5F44
push [
ebp+arg_4]
.text:660E5F47
call dword_6610EE18
跟进CALL:
.data:6610EE18 dword_6610EE18
dd 0
; DATA XREF: sub_66004F58+46C w.data:6610EE18
; __vbaStrCat+D r 继续跟踪:
.text:660053BA
push offset aVarbstrcat
; "VarBstrCat".text:660053BF
push edi.text:660053C0
call esi ; GetProcAddress.text:660053C2
test eax,
eax.text:660053C4
mov dword_6610EE18,
eax 我们来看看哪个输出函数能够导出该功能,继续跟踪下去:
.text:66004D81 sub_66004D81
proc near
; CODE XREF: sub_66004D59+20 p.text:66004D81
; CreateIExprSrvObj+3E p CreateIExprSrvObj觉得可疑,跟进看看:
.text:660EA734 public CreateIExprSrvObj
.text:660EA734 CreateIExprSrvObj
proc near
.text:660EA734
.text:660EA734 var_8 =
dword ptr -8
.text:660EA734 var_4 =
dword ptr -4
.text:660EA734 arg_0 =
dword ptr 8
.text:660EA734 arg_4 =
word ptr 0Ch
.text:660EA734 arg_8 =
word ptr 10h
.text:660EA734
.text:660EA734
push ebp.text:660EA735
mov ebp,
esp.text:660EA737
push ecx.text:660EA738
push ecx.text:660EA739
push ebx.text:660EA73A
xor ebx,
ebx.text:660EA73C
cmp [
ebp+arg_4], 4
.text:660EA741
push esi.text:660EA742
push edi.text:660EA743
mov [
ebp+var_8],
ebx.text:660EA746
mov [
ebp+var_4],
ebx.text:660EA749
jnz loc_660EA7F6
.text:660EA74F
cmp [
ebp+arg_8],
bx.text:660EA753
ja loc_660EA7F6
.text:660EA759
call sub_660CCD82
.text:660EA75E
test eax,
eax.text:660EA760
jnz short loc_660EA782
.text:660EA762
push 2
.text:660EA764
push ebx.text:660EA765
push ebx.text:660EA766
push 9
.text:660EA768
push ebx.text:660EA769
push ebx.text:660EA76A
push 2636h
.text:660EA76F
push ebx.text:660EA770
push 6
.text:660EA772
call sub_66004D81 <---- 就是这个CALL
我们需要这个CALL,deroko对CALL进行了修改并使之运行正常:
push 0
push 4
push 0
call CreateIExprSrvObj(记得改call为cinvoke)
我们复制到代码区,RADASM中编译已经通过了。
四、解决UNICODE字符串
我们注意到在调用这个算法CALL之前有压入机器码和字符串
"Serializer",这些字符串在VB中都是以UNICODE形式存在的,所以在定义字符串
"Serializer"时我们必须定义为UNICODE字符格式。
利用MASM32的宏ucMacros.asm可以轻易做到只要在定义的字符串变量前面加WSTR即可:
WSTR static_string,
"Serializer" 我们还需要在代码中加入压入机器码和字符串
"Serializer"的代码,然后才能调用CALL。
五,完善注册机
如果进一步分析,会发现软件机器码的由来。
0040322C . 50
PUSH EAX 0040322D . E8 EEF3FFFF
CALL crackme.00402620
; 此CALL跳到GetComputerNameW函数,返回计算机名 ...
00403286 . 57
PUSH EDI ; 00404024 0013F63C UNICODE "Serializer" 00403287 . 50
PUSH EAX ; 计算机名 UNICODE "OEM-MICRO" 00403288 . E8 30010000
CALL crackme.004033BD
; 算法CALL 0040328D . 8BD0
MOV EDX,
EAX ; 得到机器码 也就是说计算机名和字符串
"Serializer"经过这个算法CALL后得到机器码。
在注册机中我们就可以直接用改代码获取机器码。
好了,主要代码已经贴在下面了,其实关键CALL内的算法只是字符串间简单的异或操作。这里讨论的不是怎么去分析这个算法过程。我主要是想大家了解的是这种注册机写法的思路。若有差错,希望补充。
Generate
proc push 0
push 4
push 0
cinvoke CreateIExprSrvObj
; ----------------------------------------------------------- 计算机器码 mov computer_name_size,
sizeof computer_name
invoke GetComputerNameW,
addr computer_name,
addr computer_name_size
xor eax,
eax mov edi,
offset computer_name
stc sbb ecx,
ecx cld repnz scasw not ecx dec ecx shl ecx, 1
mov computer_name_size,
ecx mov eax,
offset static_string
mov ptr1,
eax mov eax,
offset computer_name
mov ptr2,
eax push offset ptr1
;压入UNICODE字符串"Serializer" push offset ptr2
;压入计算机名 call loc_4033BD
;算法CALL mov esi,
eax ;得到机器码 mov edi,
offset hardware_key
xor ecx,
ecx ; ----------------------------------------------------------- 将机器码放到hardware_key中供机器码框调用copy_hardwarekey:
lodsw test ax,
ax jz get_hardwarekey
add ecx, 2
stosw jmp copy_hardwarekey
; ----------------------------------------------------------- 计算注册码 get_hardwarekey:
mov hardware_key_size,
ecx mov eax,
offset static_string
mov ptr1,
eax mov eax,
offset hardware_key
mov ptr2,
eax push offset ptr1
;压入UNICODE字符串"Serializer" push offset ptr2
;压入机器码 call loc_4033BD
;算法CALL mov esi,
eax ;得到注册码 mov edi,
offset SerialBuffer
; ----------------------------------------------------------- 将注册码复制到SerialBuffer供注册码框调用 get_SerialBuffer:
lodsw test ax,
ax jz exit
stosw jmp get_SerialBuffer
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 以下是算法CALL内容,拷自IDAloc_4033BD:
push ebp mov ebp,
esp sub esp, 0Ch
push offset dummy_seh
mov eax,
fs:0
push eax mov fs:0,
esp sub esp, 64h
push ebx push esi push edi mov [
ebp-0Ch],
esp mov dword ptr [
ebp-8],
offset dword_401160
mov edi, [
ebp+0Ch]
push 1
pop esi xor eax,
eax push dword ptr [
edi]
mov [
ebp-20h],
eax mov [
ebp-24h],
eax mov [
ebp-28h],
eax mov [
ebp-2Ch],
eax mov [
ebp-38h],
eax mov [
ebp-3Ch],
eax mov [
ebp-40h],
eax mov [
ebp-50h],
eax mov [
ebp-60h],
eax mov [
ebp-1Ch],
esi cinvoke __vbaLenBstr
push 2
mov [
ebp-68h],
eax mov [
ebp-18h],
esi pop ebx; ---------------------------------------------------------------------------loc_40341C:
; CODE XREF: seg000:00403491j mov eax, [
ebp-18h]
cmp eax, [
ebp-68h]
jg short loc_403493
push dword ptr [
ebp-24h]
lea ecx, [
ebp-50h]
mov [
ebp-48h],
esi mov [
ebp-50h],
ebx push ecx push eax push dword ptr [
edi]
cinvoke rtcMidCharBstr
mov edx,
eax lea ecx, [
ebp-3Ch]
cinvoke __vbaStrMove
push eax cinvoke rtcAnsiValueBstr
push eax cinvoke __vbaStrI2
mov edx,
eax lea ecx, [
ebp-40h]
cinvoke __vbaStrMove
push eax cinvoke __vbaStrCat
mov edx,
eax lea ecx, [
ebp-24h]
cinvoke __vbaStrMove
lea eax, [
ebp-40h]
push eax lea eax, [
ebp-3Ch]
push eax push ebx cinvoke __vbaFreeStrList
add esp, 0Ch
lea ecx, [
ebp-50h]
cinvoke __vbaFreeVar
push 1
pop eax add eax, [
ebp-18h]
jo loc_403616
mov [
ebp-18h],
eax jmp short loc_40341C
; ---------------------------------------------------------------------------loc_403493:
; CODE XREF: seg000:00403422j mov eax, [
ebp+8]
push dword ptr [
eax]
cinvoke __vbaLenBstr
mov [
ebp-70h],
eax mov [
ebp-18h],
esi; ---------------------------------------------------------------------------loc_4034A3:
; CODE XREF: seg000:004035A9j mov edi, [
ebp-18h]
cmp edi, [
ebp-70h]
jg loc_4035AE
lea eax, [
ebp-50h]
mov [
ebp-48h],
esi push eax mov [
ebp-50h],
ebx push dword ptr [
ebp-1Ch]
push dword ptr [
ebp-24h]
cinvoke rtcMidCharBstr
mov edx,
eax lea ecx, [
ebp-3Ch]
cinvoke __vbaStrMove
push eax cinvoke rtcAnsiValueBstr
movsx eax,
ax lea ecx, [
ebp-3Ch]
mov [
ebp-30h],
eax cinvoke __vbaFreeStr
lea ecx, [
ebp-50h]
cinvoke __vbaFreeVar
mov eax, [
ebp-1Ch]
push dword ptr [
ebp-24h]
add eax,
esi jo loc_403616
mov [
ebp-1Ch],
eax cinvoke __vbaLenBstr
cmp [
ebp-1Ch],
eax jle short loc_403508
mov [
ebp-1Ch],
esi; ---------------------------------------------------------------------------loc_403508:
; CODE XREF: seg000:00403503j lea eax, [
ebp-50h]
mov [
ebp-48h],
esi push eax mov eax, [
ebp+8]
push edi mov [
ebp-50h],
ebx push dword ptr [
eax]
cinvoke rtcMidCharBstr
mov edx,
eax lea ecx, [
ebp-3Ch]
cinvoke __vbaStrMove
push eax cinvoke rtcAnsiValueBstr
lea ecx, [
ebp-3Ch]
movsx edi,
ax cinvoke __vbaFreeStr
lea ecx, [
ebp-50h]
cinvoke __vbaFreeVar
xor edi, [
ebp-30h]
lea eax, [
ebp-38h]
mov [
ebp-58h],
eax lea eax, [
ebp-60h]
push eax mov [
ebp-38h],
edi mov dword ptr [
ebp-60h], 4003h
cinvoke rtcHexBstrFromVar
mov edx,
eax lea ecx, [
ebp-28h]
cinvoke __vbaStrMove
push dword ptr [
ebp-28h]
cinvoke __vbaLenBstr
cmp eax,
ebx jge short loc_403589
push offset dword_4027BC
push dword ptr [
ebp-28h]
cinvoke __vbaStrCat
mov edx,
eax lea ecx, [
ebp-28h]
cinvoke __vbaStrMove
; ---------------------------------------------------------------------------loc_403589:
; CODE XREF: seg000:00403570j push dword ptr [
ebp-2Ch]
push dword ptr [
ebp-28h]
cinvoke __vbaStrCat
mov edx,
eax lea ecx, [
ebp-2Ch]
cinvoke __vbaStrMove
push 1
pop eax add eax, [
ebp-18h]
jo short loc_403616
mov [
ebp-18h],
eax jmp loc_4034A3
; ---------------------------------------------------------------------------loc_4035AE:
; CODE XREF: seg000:004034A9j mov edx, [
ebp-2Ch]
lea ecx, [
ebp-20h]
cinvoke __vbaStrCopy
push offset loc_403602
jmp short loc_4035E9
; --------------------------------------------------------------------------- test byte ptr [
ebp-4], 4
jz short loc_4035CE
lea ecx, [
ebp-20h]
cinvoke __vbaFreeStr
; ---------------------------------------------------------------------------loc_4035CE:
; CODE XREF: seg000:004035C4j lea eax, [
ebp-40h]
push eax lea eax, [
ebp-3Ch]
push eax push 2
cinvoke __vbaFreeStrList
add esp, 0Ch
lea ecx, [
ebp-50h]
cinvoke __vbaFreeVar
retn ; --------------------------------------------------------------------------- loc_403616:
; CODE XREF: seg000:00403488j ; seg000:004034F2j ... cinvoke __vbaErrorOverflow
; --------------------------------------------------------------------------- loc_4035E9:
; CODE XREF: seg000:004035BEj lea ecx, [
ebp-24h]
cinvoke __vbaFreeStr
lea ecx, [
ebp-28h]
cinvoke __vbaFreeStr
lea ecx, [
ebp-2Ch]
cinvoke __vbaFreeStr
retn ; ---------------------------------------------------------------------------loc_403602:
; DATA XREF: seg000:004035B9o mov ecx, [
ebp-14h]
mov eax, [
ebp-20h]
pop edi pop esi mov fs:0,
ecx pop ebx leave retn 8
; ---------------------------------------------------------------------------dummy_seh:
xor eax,
eax ret; ---------------------------------------------------------------------------exit:
ret Generate
endp