看了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 EAX
00402AC8 . 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
ret
2、mov eax , large fs :0
此处的large是修饰符,直接去掉,“fs:0” MASM也认不出来,我们在.DATA区加入
assume fs :nothing
3、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 EBP
660E5F3B 8BEC MOV EBP ,ESP
660E5F3D 8D45 08 LEA EAX ,DWORD PTR SS :[EBP +8]
660E5F40 50 PUSH EAX
660E5F41 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内容,拷自IDA
loc_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
完整的代码及相关软件看附件。下篇想写个VC类的,如果有时间。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年12月14日
[招生]系统0day安全班,企业级设备固件漏洞挖掘,Linux平台漏洞挖掘!
上传的附件: