CrackMe-007

160CrackMe-07

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 = 0x37B
for 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

可以发现:

0x31 0x32 0x33 0x34 0x35
0x31 0x961 0x992 0x9C3 0x9F4 0xA25
0x32 0x992 0x9C4 0x9F6 0xA28 0xA5A
0x33 0x9C3 0x9F6 0xA29 0xA5C 0xA8F
0x34 0x9F4 0xA28 0xA5C 0xA90 0xAC4
0x35 0xA25 0xA5A 0xA8F 0xAC4 0xAF9

简化得到:

Nome = '12345'
sum = 0
EDI = 0x691
res = []
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

avatar