CrackMe-007

发布于 2023-04-14  100 次阅读


Start

0x01 信息收集

查壳,跟CrackMe06是同个作者

image20210302105906768.png

同样拉进DarkDe4反编译,看看做了哪些修改:

image20210302110154774.png

事件为:

  • CancellaClick
  • AboutClick
  • RegisterzClick
  • AgainClick

默认的程序中是没有AgainClick这个控件的,随便输入点Nome和Codicei点击注册也并未出现,说明这里应该是注册成功才会出现该控件

0x02 爆破与算法破解

拖进IDA,先看到RegisterzClick的控件做了什么操作:

image20210302110535131.png

同样的,导入Delphi相关签名表,然后导出MAP

image20210302110859202.png

拖进OD,先将MAP文件导入,由于没有开启PIE,所以可以直接定位到该回调函数的地址:

image20210302111023637.png

随便输入点东西:

image20210302111049666.png

第一步GetText为Codice的值,第二步调用了VarLong,然后提示You MUST insert a valid long Integer,说明Codice必须是整数型字符串。

image20210302111247935.png

重新输入:

image20210302111459644.png

第二个jle指令用来判断Codice是否小于等于0,如果小于等于0的话会弹出,Please Code > 0

image20210302112323606.png
image20210302112427215.png

往下第二次GetText获取的是Nome的值,并且调用了004429A8的函数,调用完之后往下有个je指令,该指令刚好跳过了SetVisible的函数,说明4429A8应该就是做注册码验证的。

image20210302111658912.png

跟进0x004429A8:

存在两个局部变量:

一个是Nome的值为test123

一个是Codice的值转成hex -> hex(int(12345,10)),这个位置的值往上跟可以看到是通过ValToLong函数生成的

image20210302112652021.png

往下判断Nome的长度是否大于4,那么就可以得到Nome的长度大于等于5就行了

image20210302112859565.png

往下进入循环,这里有两层循环

image20210302113035638.png

先看到第一次循环的内层循环,第一次循环,取第一个和最后一个字符串的ASCII码相乘,再乘上edi的值,此时edi的值为0,ebx的值也为0,这里ebx应该是存放最终的结果的,第一次的内层循环由于edi的值一直为0,所以此时所有的结果皆为0。

但是在外层的循环,edi的值也未发生改变,那么此时不管内层循环做任何的操作,edi的值一直为0,最终得到的ebx的结果也是一直为0。

回到IDA中:

可以看到v3应该就是ebx,它是该函数传入的第一个参数:

image20210302120157535.png

回到函数调用处,可以看到传入为int类型的变量,dword_445830,但是这个变量的唯一赋值的地方是在提示输入insert Integer Codice的位置,那么这里就需要让其执行。

image20210302120322473.png

回到OD中,重新执行该回调函数:

nop掉jcc指令,往下看到dword_445830的值还是0:

image20210302131347425.png

回到IDA,跟进SelectAll函数,观察会对Codice的长度进行判断,判断他的长度是否小于等于5,如果条件成立,可以看到返回值还是0。这里应该就是问题所在,重新输入Codice:

image20210302131446123.png
image20210302131820063.png
image20210302131912670.png

再回到44298A,此时edi中已经有值了。

image20210302132052140.png

那么基本上就得出该注册算法的成立条件:

首先EDI中的值是受Codice控制的:

输入1234567 -> 0x9D2

输入234567 -> 0x691

然后最终与Nome绑定的Codice又是通过EDI与Nome各种算法后得到的

通过调试可以整理整个计算过程:

Codice = '234567'EDI = 0x37Bfor i in range(1, len(Codice)):    EDI += (ord(Codice[i]) % 0x11 + 1) * ord(Codice[i-1])
image20210302161236065.png

再次回到4429A8这个函数,重新整理两层循环:

整个计算过程为:

Nome = '12345'res = []for i in Nome:    index = len(Nome) - 1     while index >= 0:        res.append(ord(i) * ord(Nome[index]))        index -= 1

每一次相乘得到的结果:

image20210302172548850.png

可以发现:

0x310x320x330x340x35
0x310x9610x9920x9C30x9F40xA25
0x320x9920x9C40x9F60xA280xA5A
0x330x9C30x9F60xA290xA5C0xA8F
0x340x9F40xA280xA5C0xA900xAC4
0x350xA250xA5A0xA8F0xAC40xAF9

简化得到:

Nome = '12345'sum = 0EDI = 0x691res = []for i in Nome:    for j in Nome:        sum += ord(i)*ord(j)*EDI

往下看到IDA:

v8 = abs(v4) % 666666;v15 = v15 % 0x50 + v15 / 0x59 + 1;if ( v8 == v15 )    LOBYTE(v8) = 1;else    v8 = 0;

最终处理与比较,那么根据这个就可以写出脚本,这里是无法直接通过Nome算出Codice的,所以需要去爆破,其次,默认的程序无论Nome和Codice是否匹配都是无法注册成功的。

0x03 注册机编写与程序修正

先根据之前的分析写出注册码的生成算法,这里我们知道Codice是纯数字的,而Nome是数字或字母都可以,所以这里选择爆破Codice,然后Codice是大于等于6位数的正整数,所以初始化的num100000,然后+1进行碰撞就行了,这里未追究速度的问题,所以采用单线程进行爆破,速度会比较慢

Nome = ''Codice = ''​def generate_name(self):    self.Nome = ''.join(random.sample(string.ascii_letters + string.digits, random.randint(6,10)))​def generate_serial(self):    num = 100000    while True:        self.Codice = str(num)        sum = 0x37b        for i in range(1, len(self.Codice)):            sum += (ord(self.Codice[i]) % 0x11 + 1) * ord(self.Codice[i-1])        tmp = 0        for i in self.Nome:            for j in self.Nome:                tmp += ord(i)*ord(j)*sum​        res1 = int(self.Codice, 10) % 0x50 + int(self.Codice, 10) // 0x59 + 1        res2 = abs(tmp) % 0xA2C2A        if (res1 == res2):            break        else:            num += 1            continue

程序修正的目地是让EBX中的值不为0,所以,需要将这两处的jcc指令修改:

image20210304105930118.png

第一处的指令可以直接nop掉,不过因为是输入正确的Nome和Codice,所以这个位置最终是想要调用到WindowDesigner这个函数,并且不想弹出Dialog,所以需要跳转到0x442F70这个位置,这里需要了解jcc指令硬编码相关的知识:

可以看到所有的JCC指令的硬编码都是两个字节,第一字节代表指令类型,第二个字节代表的是跳转的位置,这里的74 37,后面的0x37指的是从当前指令的下一条指令的EIP开始加上0x37就是要跳转的位置,此时下一条指令的EIP为0x442F66,加上0x37,就是0x442F9D的位置,那么我们现在需要跳转到0x442F70的位置,就是用后面的0x70 - 0x66就行了,得到0x0A,那么修改第二个字节为0A就行了:

image20210304110905982.png

后面的jmp指令直接nop就可以了

最终可以得到:

image20210304113606111.png
image20210304113613494.png

ps:其实双击指令位置改就行hhhh,没必要自己计算硬编码

End