160CM-013
012的编程语言太古老,大概了解了一下,以我的能力肯定搞不定,直接跳过……
1.追码
这是一个二合一的题,有两个CrackMe,一个只需要输入序列号,另一个要输入名字和序列号。
2
PE先看看,无壳,VB,还是先用VB Decompiler看看(这里推荐V11以上的版本,反编译后可以直接预览Form控件,方便识别按钮)。
10
由于不同界面有些按钮使用了相同的名字,所以需要稍微看一下按钮对应的事件的代码,才能识别出各个按钮对应的事件。如下
11
查看按钮Command4对应的事件代码,发现事件函数入口地址为4052AC,可是代码起始地址是4051DA,比函数入口地址还小?OD打开Ctrl+G跳转到4052AC,呃……都不是汇编代码。。。
3
搜索一顿后才搞明白,原来这是VB的P-Code编译代码,这个一个解释型的代码,执行的时候是将代码数据加载到相应的函数中进行解释后再执行,因此在OD中对应的代码位置下断是断不下来的。
死马当活马医,既然是VB的,那试试OD中的VB断点吧,最常用的字符比较断点,先试了下“_vbaStrCmp”,发现断不下来;再试试“__vbaStrComp”,发现运气不错,断了下来。这个时候在堆栈区,可以发现有几个UniCode的字符串,一个是“try again”,一个数字字符串,输入到输入框中,注册成功。
切换到CrackMe 2.0,输入用户名,点击try按钮后,同样可以断下来,在堆栈区可以发现一个“xxxx-xxxxxx”的字符串,输入进去,注册成功。
这个程序里面因为没有字符串加密,直接明码比较的,而且比较函数也是VB标准函数,因此侥幸能追到注册码,如果字符串加个密再比较或者用自写的函数进行比较,就没法这样追到注册码了。
2.爆破
P-Code编译的程序,用OD基本没办法跟踪,因此不能用跟踪爆破法(P-code程序也有动态调试工具WKTVBDebugger,这个要win2000下才能运行,没有尝试过)。但是因为是P-Code,可以反编译得到VB代码,可以很方便的读懂程序逻辑,因此不需要动态分析,静态分析就能找到关键跳转,因此只要知道怎么修改跳转指令就可以了。
我们先看CrackMe 1.0中的事件函数,“loc_4051DA: If (Me.Text3.Text = Me.Label3.Caption) Then”这一行是关键判断,在VB Decompiler的反汇编代码中找到地址4051DA位置,发现是“BranchF loc_405295”,这个跳转刚好跳过“Congratulation!”字符串代码区,进入“Try Again!”字符串代码区,因此我们只需要想办法修改这个跳转就可以。
9
OD中打开程序,找到4051DA位置,发现内容是“1C F5”(由于这些代码是P-code代码,反汇编识别出来的都是错误信息),但是要怎么改呢?不用慌,前人已经给我们总结了P-code机器码的特点,如下:
p-code里面转向语句一共有三个:
Branch (1E) ---- 无条件跳转
BranchT(1D) ---- 栈顶数据为真则跳转
BranchF(1C) ---- 栈顶数据为假则跳转
因此,我们只需要二进制修改将“1C”改成“1D”就可以了。
1
同样,CrackMe 2.0中也可以找到关键跳转在405815位置,内容为“1C 09”,同样将“1C”改成“1D”就可以了。
7
3.算法
由于有VB Decompiler的帮助,算法逆向过程应该不难。不过自己水平比较菜,中间还是踩了不少坑。
先看CrackMe 1.0,前面已经找到事件函数对应Command4_Click,可以发现比较的内容为输入字符串和Label3的内容。
Label3里有啥?我们把所有的事件函数找一遍,发现只有Combo1和Command4事件中对这个控件有赋值,Combo1中的代码如下:
4
完整算法为
loc_4053DB: var_1D0 = CStr((((CLng((((Day(Now) * Day(23)) + (Month(Now) * Month(2))) + (Year(Now) * Year(3)))) + CLng((((Day(Now) * Day(23)) + (Month(Now) * Month(2))) + (Year(Now) * Year(3))))) + CLng((Day(14) * Year(2020)))) + CLng((Day(14) * Year(2020)))))
算法理解起来不难,就是获取当前时间的年月日和某个数值对应的年月日相乘相加等等。因为VB没学过,这几个函数一看EXCEL里面好像都有,于是在EXCEL中列了个公式计算了一下,其中day(23)在EXCEL中计算值为23……
算出一个值来填进去一试,发现不对(和之前追码得到的字符串对不上)。前后一阵查找,死活没找到问题在哪里,然后看了一下别人的破解教程,发现在VB中,day(23)=22。呃,,,都是微软同一家的东西为什么还搞出来不一样呢?
没办法,硬着头皮搞搞VB吧,VS 2012中创建一个VB控制台项目,把算法代码弄进去,一编译,发现day函数等都无法识别,难不成还要装个VB6.0才能愉快的玩耍?
最终,经过反复搜索,各种尝试,终于把算法代码撸出来了。
Dim day_now, day_23, month_now, month_2, year_now, year_3, day_14, year_2020, code As Integer
day_now = Now.Day
day_23 = Date.FromOADate(23).Day
month_now = Now.Month
month_2 = Date.FromOADate(2).Month
year_now = Now.Year
year_3 = Date.FromOADate(3).Year
day_14 = Date.FromOADate(14).Day
year_2020 = Date.FromOADate(2020).Year
code = (day_now * day_23 + month_now * month_2 + year_now * year_3) + (day_now * day_23 + month_now * month_2 + year_now * year_3) + (day_14 * year_2020) + (day_14 * year_2020)
MsgBox("注册码为:" & code)
再看CrackMe 2.0,反编译出来的代码如下:
Private Sub Command2_Click() '4058EC
'Data Table: 403D90
Dim var_90 As Long
Dim var_1CC As Variant
loc_4055EE: If (Len(Me.Text1.Text) < 5) Then
loc_4055FE: Me.Text2.Text = "At least 5 characters!"
loc_405606: Exit Sub
loc_405607: End If
loc_40560A: var_94 = "0110617121214051216101106141404110614140411091211100810101608040610121608100416"
loc_405622: var_98 = Me.Text1.Text
loc_40562D: var_A8 = 1 'Variant
loc_405641: For var_108 = 4 To CVar(Len(var_98)): var_C8 = var_108 'Variant
loc_405698: var_90 = CLng((CDbl(var_90) + (CDbl(Asc(Mid$(var_98, CLng(var_C8), 1))) * Val(Mid$(var_94, CLng((var_A8 * 3)), 3)))))
loc_4056C4: If ((var_A8 + 1) >= 39) Then
loc_4056CC: var_A8 = 0 'Variant
loc_4056D0: End If
loc_4056D3: Next var_108 'Variant
loc_4056DE: var_A8 = 1 'Variant
loc_4056F2: For var_168 = 4 To CVar(Len(var_98)): var_C8 = var_168 'Variant
loc_405764: var_1CC = CVar((CDbl((Asc(Mid$(var_98, CLng(var_C8), 1)) * Asc(Mid$(var_98, CLng((var_C8 - 1)), 1)))) * Val(Mid$(var_94, CLng((var_A8 * 2)), 2)))) 'Double
loc_40576C: var_178 = (var_178 + var_1CC) 'Variant
loc_40579D: If ((var_A8 + 1) >= 39) Then
loc_4057A5: var_A8 = 0 'Variant
loc_4057A9: End If
loc_4057AC: Next var_168 'Variant
loc_405815: If (Me.Text2.Text = LTrim$(Str$(var_90)) & "-" & LTrim$(Str$(var_178))) Then
loc_405824: Me.Command2.Visible = False
loc_405838: Me.Command1.Visible = False
loc_40584C: Me.Command5.Visible = True
loc_405860: Me.Command3.Visible = False
loc_405874: Me.Text2.Visible = False
loc_405888: Me.Frame3.Visible = True
loc_4058BD: Me.Label3.Caption = "Congratulation " & Me.Text1.Text & " !"
loc_4058D2: GoTo loc_4058EA
loc_4058D5: End If
loc_4058E2: Me.Text2.Text = "Try Again!"
loc_4058EA: ' Referenced from: 4058D2
loc_4058EA: Exit Sub
End Sub
上述算法不难理解,主要过程就是输入的字符串取字符经过某些运算得到新的字符串,然后进行拼接得到注册码。这里比较容易有问题的是,VB Decompiler的反编译代码不完整,其中漏掉了部分var_A8的计算代码,因此按上述代码计算得到的注册码是不正确的。这个时候就需要去掉“程序分析器和优化器”选项后重新发编译,可以看到在for循环中var_A8有个+1的计算过程。
最终的算法代码如下:
Module Module1
Sub Main()
Dim day_now, day_23, month_now, month_2, year_now, year_3, day_14, year_2020, code As Integer
day_now = Now.Day
day_23 = Date.FromOADate(23).Day
month_now = Now.Month
month_2 = Date.FromOADate(2).Month
year_now = Now.Year
year_3 = Date.FromOADate(3).Year
day_14 = Date.FromOADate(14).Day
year_2020 = Date.FromOADate(2020).Year
code = (day_now * day_23 + month_now * month_2 + year_now * year_3) + (day_now * day_23 + month_now * month_2 + year_now * year_3) + (day_14 * year_2020) + (day_14 * year_2020)
MsgBox("注册码为:" & code)
Dim Str, name, code3 As String
Dim i, j, m, temp, var_1CC, var_178, k, code2 As Integer
Str = "0110617121214051216101106141404110614140411091211100810101608040610121608100416"
name = InputBox("请输入用户名(长度≥5):")
m = 1
temp = 1
For i = 4 To Len(name)
code2 = CLng((CDbl(code2) + CDbl(Asc(Mid$(name, CLng(i), 1))) * Val(Mid$(Str, CLng((m * 3)), 3))))
temp = 1
m = m + temp
temp = 39
If ((m + 1) >= 39) Then
temp = 0
m = 0
End If
Next i
m = 1
temp = 1
For j = 4 To Len(name)
var_1CC = (CDbl((Asc(Mid$(name, CLng(j), 1)) * Asc(Mid$(name, CLng((j - 1)), 1)))) * Val(Mid$(Str, CLng((m * 2)), 2)))
var_178 = (var_178 + var_1CC)
temp = 1
m = m + temp
temp = 39
If ((m + 1) >= 39) Then
temp = 0
m = 0
End If
Next j
code3 = LTrim$(CInt(code2)) & "-" & LTrim$(CInt(var_178))
MsgBox("注册码为:" & code3)
End Sub
End Module
4.总结
- 第一次接触的P-Code编译的VB程序,大致了解了P-Code代码的工作方式和特点。破解过程中,VB断点有时候还是能派上用场的。爆破的话,需要掌握以下几个P-Code指令码:
P-Code里面跳转语句一共有三个:
Branch (1E) ---- 无条件跳转
BranchT(1D) ---- 栈顶数据为真则跳转
BranchF(1C) ---- 栈顶数据为假则跳转
注意:1C和1D可以互改,但是不能和1E互改,因为1C和1D操作时会执行数据出栈操作,而1E不会。
比较指令:
EqVarBool----比较变量相等: FB 33
NeVarBool----比较变量不相等: FB 40
EqStr ----比较字符串相等: FB 30
NeStr ----比较字符串相等: FB 3D
VB中的日期,数值范围为:-657435.0到2958465.99999999,0代表1899年12月30日00:00:00
奇怪之处:
0.5和-0.5均表示1899年12月30日12:00:00(不明白为什么要定义成这样)
EXCEL中的日期,1代表1900 年 1 月 1 日 00:00:00,只允许正值;