本帖最后由 pk8900 于 2017-12-31 18:56 编辑
【引言】最近一直进行[反汇编练习] 160个CrackMe的破解练习,发现其中有一大部分的VB5&6程序,在对这些程序练习的基础上总结了一个方法,分享给大家,有不对的地方还请大神指出。此方法前提是借助VB反编译软件VB Decompiler的分析结果,在此基础上对反编译的伪VB代码进行整理还原。选的这个CrackMe程序比较简单,方便我对此方法的讲解,使用此方法可能要求你懂一点VB或VBscript编程基础知识。
【适用对象】VB5&6程序,Native Code编码方式(动态调试方便),P-CODE编码方式(调试麻烦)
【工具软件】VB Decompiler、NotePad++、X64DBG(OD也可)、 Visual Basic 6.0
【例程】下载地址:http://pan.baidu.com/share/link?shareid=541269&uk=4146939145
【步骤一】获取程序反编译VB伪代码,找到关键代码部分, 例程:160个CrackMe之064(execution),用VB Decompiler反编译,界面如下:
代码部分共三部分:
Code -> Form1 -> Command1_Click_40D8F0 -->关键代码,Register按钮事件。
Code -> Form1 -> About_Click_40D780 -->About对话框事件。
Code -> Form1 -> Exit_Click_40DCE0 -->关闭窗体事件。
这里的Command1_Click事件就是我们要寻找的关键代码,也有很多程序会在Text_OnChange事件里写代码,我们也必须关注。
【步骤二】代码还原:下面我们就对这段Command1_Click事件代码进行整理还原。
将Command1_Click代码复制到NotePad++中,在NotePad++语言菜单中选择VB,之所以用NotePad++整理,是因为NotePad++中,你选择的部分NotePad++会自动匹配并高亮显示,这个功能很关键。如下图:
我双击选择了var_18,代码中引用的var_18都会自动高亮突出显示,你可以看到这些变量的引用情况。接下来对代码进行整理,反编译的VB伪代码为:
[Visual Basic] 纯文本查看 复制代码 Private Sub Command1_Click() '40D8F0
loc_0040D930: var_eax = arg_8.AddRef 'Ignore this
loc_0040D957: var_eax = Form1.Text1 'Ignore this
loc_0040D965: var_3C = Form1.Text1 'Ignore this
loc_0040D96F: var_54 = Ucase(Form1.Text1 'Ignore this)
loc_0040D98A: var_28 = var_54
loc_0040D996: call undef 'Ignore this '__vbaFreeVarList(00000002, 9, var_54, arg_8, edi, ebx, %ecx = %S_edx_S)
loc_0040D9B1: var_B8 = Len(var_28)
loc_0040D9C3: If 00000001h > 0 Then GoTo loc_0040DB03
loc_0040D9D2: var_7C = var_28
loc_0040D9FA: var_54 = Mid(var_28, si, 1)
loc_0040DA0F: var_18 = var_54
loc_0040DA1B: call undef 'Ignore this '__vbaFreeVarList(00000002, 2, var_54)
loc_0040DA2E: Asc(var_18) = Asc(var_18) - 0040h
loc_0040DA32: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA38: Asc(var_18) = Asc(var_18) * 0082h
loc_0040DA3D: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA43: Asc(var_18) = Asc(var_18) + di
loc_0040DA46: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA4C: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA50: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA56: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA5A: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA60: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA64: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA6A: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA6E: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA74: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA78: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA7E: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA82: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA88: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA8C: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA92: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DA96: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA9C: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DAA0: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAA6: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DAAA: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAB0: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DAB4: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DABA: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DABE: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAC4: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DAC8: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DACE: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DAD2: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAD8: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DADC: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAE2: Asc(var_18) = Asc(var_18) + 0050h
loc_0040DAE6: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAF3: 00000001h = 00000001h + si
loc_0040DAF6: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAFE: GoTo loc_0040D9BC
loc_0040DB03: 'Referenced from: 0040D9C3
loc_0040DB09: var_eax = Form1.Text2 'Ignore this
loc_0040DB23: var_2C = Text2.Text
loc_0040DB44: call var_40F0E8(Asc(var_18), var_2C, arg_8)
loc_0040DB4F: var_30 = var_40F0E8(Asc(var_18), var_2C, arg_8)
loc_0040DB65: esi = ( = var_30) + 1
loc_0040DB9C: If ( = var_30) + 1 = 0 Then GoTo loc_0040DBFC
loc_0040DBBF: var_3C = "Nice Going!!! you Cracked the CrackMe!" & "vbCrLf" & "Contact HackerG or DEATH to get your Present..."
loc_0040DBFA: GoTo loc_0040DC58
loc_0040DBFC: 'Referenced from: 0040DB9C
loc_0040DC20: var_3C = "You Lamer!!! Cant Crack This?!" & "vbCrLf" & "Try Again..."
loc_0040DC58: 'Referenced from: 0040DBFA
loc_0040DC5A: call undef 'Ignore this '__vbaFreeVarList(00000004, var_44, var_54, var_64, var_74, var_44, var_54, var_64, var_74)
loc_0040DC6F: GoTo loc_0040DCA9
loc_0040DC9F: call undef 'Ignore this '__vbaFreeVarList(00000004, var_44, var_54, var_64, var_74)
loc_0040DCA8: Exit Sub
loc_0040DCA9: 'Referenced from: 0040DC6F
loc_0040DCB7: GoTo loc_00esi
End Sub
下面进行整理,具体的过程,参照下图:
【步骤三】VB调试:
上面两个整理过程,可能看起来有些乱,我想只要专注,应该可以看得懂。经过上面步骤,代码已经被整理的差不多了,接下来,打开 VB,新建一个EXE工程,添加两个文本框,一个按钮,在按钮事件中将代码贴入,对代码进行调试修改,发现计算器si初值未确定,加入si = 1,最终修改如下:
[Visual Basic] 纯文本查看 复制代码 Private Sub Command1_Click() '40D8F0
loc_0040D98A: var_28 = UCase(Form1.Text1.Text)
loc_0040D9B1: var_B8 = Len(var_28)
loc_0040D9B2: si = 1 '加入循环变量的初始值:1
loc_0040D9BC:
loc_0040D9C3: If si > var_B8 Then GoTo loc_0040DB03
loc_0040D9D2: var_7C = var_28
loc_0040D9FA: var_54 = Mid(var_28, si, 1)
loc_0040DA0F: var_18 = Asc(var_54)
loc_0040DA2E: var_18 = var_18 - &H40
loc_0040DA32: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA38: var_18 = var_18 * &H82
loc_0040DA3D: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA43: var_18 = var_18 + di
loc_0040DA46: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA4C: var_18 = var_18 + &H50
loc_0040DA50: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA56: var_18 = var_18 + &H50
loc_0040DA5A: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA60: var_18 = var_18 + &H50
loc_0040DA64: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA6A: var_18 = var_18 + &H50
loc_0040DA6E: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA74: var_18 = var_18 + &H50
loc_0040DA78: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA7E: var_18 = var_18 + &H50
loc_0040DA82: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA88: var_18 = var_18 + &H50
loc_0040DA8C: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA92: var_18 = var_18 + &H50
loc_0040DA96: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DA9C: var_18 = var_18 + &H50
loc_0040DAA0: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAA6: var_18 = var_18 + &H50
loc_0040DAAA: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAB0: var_18 = var_18 + &H50
loc_0040DAB4: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DABA: var_18 = var_18 + &H50
loc_0040DABE: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAC4: var_18 = var_18 + &H50
loc_0040DAC8: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DACE: var_18 = var_18 + &H50
loc_0040DAD2: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAD8: var_18 = var_18 + &H50
loc_0040DADC: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAE2: var_18 = var_18 + &H50
loc_0040DAE6: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAF3: si = si + 1
di = var_18 '伪代码中漏掉的,补上
loc_0040DAF6: If Err.Number <> 0 Then GoTo loc_0040DCD9
loc_0040DAFE: GoTo loc_0040D9BC
loc_0040DB03: 'Referenced from: 0040D9C3
loc_0040DB09: 'Ignore this
loc_0040DB23: var_2C = Text2.Text
loc_0040DB44: Text2.Text = CStr(di) '加入这一句代码实现正确注册码的显示
loc_0040DB9C: If var_2C <> CStr(di) Then GoTo loc_0040DBFC
loc_0040DBBF: MsgBox "Nice Going!!! you Cracked the CrackMe!" & "vbCrLf" & "Contact HackerG or DEATH to get your Present..."
loc_0040DBFA: GoTo loc_0040DC58
loc_0040DBFC: 'Referenced from: 0040DB9C
loc_0040DC20: MsgBox "You Lamer!!! Cant Crack This?!" & "vbCrLf" & "Try Again..."
loc_0040DC58: 'Referenced from: 0040DBFA
loc_0040DCD9:
loc_0040DCA8: Exit Sub
loc_0040DCA9: 'Referenced from: 0040DC6F
End Sub
经过在VB中的调试结果和原程序在X64DBG中的调试,代码中加入了两句:
[Visual Basic] 纯文本查看 复制代码 loc_0040D9B2: si = 1 '加入循环变量的初始值:1
loc_0040DB44: Text2.Text = CStr(di) '加入这一句代码实现真正注册码的显示
实现了注册机的功能。VB中调试可以打开VB的监控窗口,将变量拖入,在代码上下断点,就可以观察变量值的变化了。
代码在VB6.0中运行并中断截图:
160个CrackMe之064(execution):用户名:52pojie.cn 注册码:16570,成功破解。
接下来说一下:for 循环代码的整理过程,程序为:160个CrackMe之059 (Eternal Bliss .02 .exe),伪代码为:
[Visual Basic] 纯文本查看 复制代码 402E34: For var_2C = 1 To 5 Step 1
loc_00402E39: var_220 = var_2C
loc_00402E3F: GoTo loc_00402FB9
loc_00402E44:
loc_00402E4C: var_eax = Form1.Text1 'Ignore this
loc_00402E7A: var_E0 = Text1.Text
loc_00402E82: var_168 = var_E0
loc_00402ED5: var_224 = var_E0
loc_00402EE8: var_F0 = var_224
loc_00402F17: var_118 = Mid(var_224, CLng(var_2C), 1)
loc_00402F2A: var_E4 = CStr(var_118)
loc_00402F35: var_140 = Asc(var_E4)
loc_00402F62: var_7C = var_7C & Asc(var_E4)
loc_00402F78: var_eax = %fobj
loc_00402F94: call undef 'Ignore this '__vbaFreeVarList(00000003, 8, 2, var_118, arg_8, arg_8, arg_8, 00000001h, edi, esi, ebx)
loc_00402FAE: Next var_2C
loc_00402FB3: var_220 = Next var_2C
loc_00402FB9: 'Referenced from: 00402E3F
loc_00402FC0: If var_220 <> 0 Then GoTo loc_00402E44
loc_00402FEF: var_E4 = CStr(var_4C & var_6C)
loc_00403026: If Err.Number <> 0 Then GoTo loc_004043E9
loc_00403042: var_DC = (Val(CStr(var_7C)) - var_160)
整理方法见下图:
经过整理:代码如下:
[Visual Basic] 纯文本查看 复制代码 402E34: For var_2C = 1 To 5 Step 1
var_224=Text1.Text
loc_00402F17: var_118 = Mid(var_224, CLng(var_2C), 1)
loc_00402F2A: var_E4 = CStr(var_118)
loc_00402F62: var_7C = var_7C & Asc(var_E4)
loc_00402FAE: Next var_2C '循环最终的结果值为:var_7C
【总结】:
0x01:VB Decompiler反编译代码中有些常量值操作会被漏掉,需要查看汇编代码。
0x02:VB 的变量值经常通过中间变量来转化和赋值,造成反编译中变量被换了名字,可联系上下文,将换了名字的变量对上号。
0x03:伪代码中的十六进制常量:如 0050h,应整理更正为: &H50
0x04:伪代码中的var_7C,汇编中一般为EBP-7C,因VB数据结构的特殊性,真正var_7C的值大部分时候是EBP-70堆栈地址的值,代码中的:arg_8即汇编中的ESP+8。而EAX。ESI等就不用说了,也是汇编中的寄存器。
0x05:VB程序中有大多数据类型可自动进行转化,所以本例中伪代码: Asc(var_18) = Asc(var_18) + 0050h 应整理为:var_18 = var_18 + &H50
0x06:对于复杂的程序,可将跳转的行号用字符串替换,使代码流程更清晰。
0x07:动态分析时,可以将VB Decompiler的代码用我以前写的一个小工具转为MAP文件导入调试器中,帖子地址:https://www.52pojie.cn/thread-668710-1-1.html,方便对照。
以上就是我最近一段时间对VB程序进行伪代码整理分析的一点心得,供坛友借鉴,明天就是2018年了,写这篇帖子算是总结一下自己的心得吧,祝坛友2018年Crack水平日进。No failed. Only Success!
|