pediy2016 CM29

运行程序,传统32位窗口程序

要找到程序的输入点,考虑找MessageBoxA,GetWindowText等API调用点,由于没有进行api隐藏,可以轻易找到输入点。

反编译结果很清晰,可以看到我们的输入转变为ascii进行了求和,输入长度为8,然后调用sub_401B0A函数对其进行检查,进入该函数

反编译结果同样清晰,它看起来是个switch case结构,不过这本质上是个障眼法,因为8个可打印字符之和一定小于0x400,所以只有0x325时可行的,综上必须是0x325。不过这里有点像个蜜罐,程序在这里初始化了一个俄罗斯方块游戏,而这个switch case语句就是方向移动,若在此处进行逆向分析,就会发现其实什么也没干,浪费了许多时间。所以这里相当于只提供了2个线索,即输入长度为8,输入之和为0x325。

向上回溯到DialogFunc处

初始化过程本质上调用了一个函数,这个函数赋给了v5函数指针,动态调试一下,发现是CreateThread,即执行sub_40149c函数,进入该函数

函数内部也基本是这种模式,动态获取某个函数地址赋给指针,然后调用该函数,多是CreateThread和CreateEvent函数,依次查看CreateThread启动的函数,发现一个看起来比较关键的函数sub_4016F0,进入该函数内

在此函数中下断点,发现它会在窗口初始化完成后才会断下来,且每次继续运行都会再次断下,间隔时间很短,试试输入字符发现其中一个变量记录了我们的输入,发现其中动态获取了GetWindowText函数,主要功能如下

buf是一个全局变量,交叉引用到使用buf的地方,在sub_4017F9函数中,该函数也是被一个线程启动的,函数主要代码如下

由于题目提示,最终必定输出Good!,所以解码后的code中必定有该字符串的地址,于是整个程序验证流程满足3个条件:

  • 8个字符和为0x325
  • 0x66,然后分为两个32位整数作为密钥,和为0x32113442
  • Good!字符串的地址

显然这里的验证算法不可逆,所以只能通过穷举来获得序列号。这里的穷举空间很大,如果只进行简单的穷举,不可能跑得出来,所以要利用一些技巧。这里的关键点是条件2,因为可输入字符都小于128,所以最高位为0,两个字符之和不会发生进位,所以利用条件2可以进行逐字节爆破,来缩小穷举空间。脚本如下:

class add_num(object):
    def __init__(self, a = 0, b = 0):
        self.a = a
        self.b = b
    
    def __str__(self):
        return 'a = ' + hex(self.a) + ';b = ' + hex(self.b)

    def get_num(self):
        return self.a + self.b

dic = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
data = [0x69, 0x92, 0x14, 0x09, 0x58, 0x1A, 0x65, 0x79, 0x60, 0x13, 0xCC, 0xD1, 0x28, 0x34, 0x0A, 0x2C, 
        0x0F, 0x40, 0x0B, 0x5C, 0x83, 0x08, 0x4B, 0xF4, 0x30, 0x85, 0x39, 0x34, 0x18, 0x40, 0x0B, 0xCD, 
        0xE9, 0x6F, 0xCA, 0x8C, 0x1F, 0x0F, 0x4B, 0xF4]

def show_list(l):
    for c in l:
        print c

def test0(buf, vv):
    global dic
    for c1 in dic:
        for c2 in dic:
            x = add_num()
            x.a = ord(c1)
            x.b = ord(c2)
            if (x.a ^ 0x66) + (x.b ^ 0x66) == vv:
                buf.append(x)

def test1(s):
    global data
    plaint = data[:]
    decode(plaint, s)
    for i in range(0, len(plaint), 4):
        if plaint[i] != 0xb5:
            continue
        if plaint[i+1] != 0x32:
            continue
        if plaint[i+2] != 0x40:
            continue
        if plaint[i+3] != 0x00:
            continue
        print s
        break
    return;

def decode(data, key):
    key = key[::-1]
    key1 = key[:4].encode('hex')
    key1 = int(key1, 16) ^ 0x66666666
    key2 = key[4:8].encode('hex')
    key2 = int(key2, 16) ^ 0x66666666
    for i in range(0, len(data), 4):
        temp = bytes2int(data[i:i+4])
        temp += key1
        temp ^= key2
        data[i:i+4] = int2bytes(temp)

def bytes2int(bys):
    return (bys[3] << 24) + (bys[2] << 16) + (bys[1] << 8) + bys[0]

def int2bytes(n):
    blist = []
    blist.append(n & 0xff)
    blist.append((n >> 8) & 0xff)
    blist.append((n >> 16) & 0xff)
    blist.append((n >> 24) & 0xff)
    return blist

def main():
    buf1 = []; buf2 = []; buf3 = []; buf4 = []
    test0(buf1, 0x42); test0(buf2, 0x34)
    test0(buf3, 0x11); test0(buf4, 0x32)
    for a in buf1:
        for b in buf2:
            for c in buf3:
                for d in buf4:
                    if a.get_num() + b.get_num() + c.get_num() + d.get_num() == 0x325:
                        _str = [0]*8
                        _str[0] = a.a; _str[4] = a.b
                        _str[1] = b.a; _str[5] = b.b
                        _str[2] = c.a; _str[6] = c.b
                        _str[3] = d.a; _str[7] = d.b
                        s = ''
                        for i in range(8):
                            s += chr(_str[i])
                        test1(s)

if __name__ == '__main__':
    main()

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值