xiaoyu2032 发表于 2022-5-10 23:04

练习笔记之160Crackme-013

本帖最后由 xiaoyu2032 于 2022-5-10 23:15 编辑

# 160CM-013

  012的编程语言太古老,大概了解了一下,以我的能力肯定搞不定,直接跳过……

## 1.追码

  这是一个二合一的题,有两个CrackMe,一个只需要输入序列号,另一个要输入名字和序列号。

  PE先看看,无壳,VB,还是先用VB Decompiler看看(这里推荐V11以上的版本,反编译后可以直接预览Form控件,方便识别按钮)。

  由于不同界面有些按钮使用了相同的名字,所以需要稍微看一下按钮对应的事件的代码,才能识别出各个按钮对应的事件。如下

  查看按钮Command4对应的事件代码,发现事件函数入口地址为4052AC,可是代码起始地址是4051DA,比函数入口地址还小?OD打开Ctrl+G跳转到4052AC,呃……都不是汇编代码。。。

  搜索一顿后才搞明白,原来这是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!”字符串代码区,因此我们只需要想办法修改这个跳转就可以。

  OD中打开程序,找到4051DA位置,发现内容是“1C F5”(由于这些代码是P-code代码,反汇编识别出来的都是错误信息),但是要怎么改呢?不用慌,前人已经给我们总结了P-code机器码的特点,如下:
`p-code里面转向语句一共有三个:`
Branch (1E) ---- 无条件跳转
BranchT(1D) ---- 栈顶数据为真则跳转
BranchF(1C) ---- 栈顶数据为假则跳转
  因此,我们只需要二进制修改将“1C”改成“1D”就可以了。

  同样,CrackMe 2.0中也可以找到关键跳转在405815位置,内容为“1C 09”,同样将“1C”改成“1D”就可以了。

## 3.算法

  由于有VB Decompiler的帮助,算法逆向过程应该不难。不过自己水平比较菜,中间还是踩了不少坑。
  先看CrackMe 1.0,前面已经找到事件函数对应Command4_Click,可以发现比较的内容为输入字符串和Label3的内容。

  Label3里有啥?我们把所有的事件函数找一遍,发现只有Combo1和Command4事件中对这个控件有赋值,Combo1中的代码如下:

  完整算法为

``` VB
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才能愉快的玩耍?
  最终,经过反复搜索,各种尝试,终于把算法代码撸出来了。

``` VB
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,反编译出来的代码如下:

``` VB
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的计算过程。
  最终的算法代码如下:

``` VB
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来编写VB程序的注册机,还是要省事一些,可以不用将函数翻译到其他语言,基本直接复制反编译出来的算法代码就可以。

- 关于VB中day(23)和EXCEl中查1天的问题,查阅了官方文档,最终原因如下:

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,只允许正值;

xiaoyu2032 发表于 2022-5-14 08:09

wulei1873 发表于 2022-5-14 02:24
注意:1C和1D可以互改,但是不能和1E互改,因为1C和1D操作时会执行数据出栈操作,而1E不会

这个没看懂

1C和1D指令以栈顶数据进行判断并跳转,同时执行一次数据出栈操作,而1E只进行跳转,不进行出栈操作。如果1C或1D指令换成了1E,后续操作的堆栈数据就错位了,将导致后续代码执行出现异常。

martin313 发表于 2022-5-14 22:46

抽空请练习一下 Similarity ,我一直找不到关键的全局修改点,如果找到的话,请指点一二

官网地址:https://www.similarityapp.com/

moshuiNW 发表于 2022-5-11 06:58

牛蛙牛蛙牛蛙!

aiyikegu 发表于 2022-5-11 07:59

牛牛牛,感谢分享

yuyi0 发表于 2022-5-11 09:11

感谢分享

0xchuxiuyun 发表于 2022-5-12 08:53

阔以,谢谢分享

测试学学 发表于 2022-5-12 11:02

真的可以学到很多东西

戰龍在野 发表于 2022-5-12 20:43

不错坚持就是胜利项起来

killsu 发表于 2022-5-12 23:25

感谢分享

wulei1873 发表于 2022-5-14 02:24

注意:1C和1D可以互改,但是不能和1E互改,因为1C和1D操作时会执行数据出栈操作,而1E不会

这个没看懂
页: [1] 2
查看完整版本: 练习笔记之160Crackme-013