简单加密程序逆向

{题目exe下载, https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/202404050204566.exe, haofont hao-icon-book}

Flag:TAKEeasy

image-20240403230954383

解题逻辑

常规阅读

对不关键的代码采用静态分析与动态调试相结合的方式进行阅读,相应备注如下。

image-20240403231200959 image-20240403231237989

加密函数分析

image-20240403231355680

进入到关键的汇编代码阶段,通过一个for循环将输入的明文的每一位都进行了加密。

  • 初始变换:原始字符串中的每个字符都被处理以交换首尾位。

    • 每个字符与 01111110进行 AND运算,结果是字符的最高位和最低位被清零,中间6位保持不变,形成 0xxxxxx0的结构。
    • 每个字符与 10000000进行 AND运算,随后通过算术右移(SAR)7位,将最初的最高位移至最低位的位置,形成 0000000a的结构,其中 a是原始字符的最高位。
    • 每个字符与 00000001进行 AND运算,接着逻辑左移(SHL)7位,将最初的最低位移至最高位的位置,形成 b0000000的结构,其中 b是原始字符的最低位。
  • 组合和交换:上述得到的两个部分(0000000ab0000000)与中间6位(0xxxxxx0)进行 OR运算,生成一个新的字符 bxxxxxxa,即原字符的首尾位被交换。

  • 异或(XOR)加密:处理后的字符随后与一个密钥字符进行 XOR运算。密钥由索引对应的字节数组(byte_429A38)提供,索引基于字符在原字符串中的位置。XOR操作生成最终的加密字符。

  • 验证和输出:生成的密文字符与预定义的目标字符串进行逐位比较。目标字符串以十六进制格式提供,且考虑到小端字节序,其实际的字符顺序应为 52 C7 C2 CD EE E8 FE F5。若加密后的字符串与目标字符串匹配,程序输出“Congratulations”信息;否则输出错误信息。

    image-20240403232130812

得出解密程序

'''
    密钥
    .data:00429A38 dword_429A38    dd 09080706h
    .data:00429A3C                 dd 0D0C0B0Ah

    密文
    .data:00429A30 dword_429A30    dd CDC2C752h
    .data:00429A34                 dd F5FEEBEEh
'''

miyao = "0D0C0B0A09080706"
miwen = "F5FEEBEECDC2C752"

for i in range(8):
    print("------------第%d个字符-------------" % (i+1))
    # 截取密钥
    sub_miyao = miyao[-1*(i+1)*2+1:-1*(i+1)*2-1:-1]
    # 反转
    sub_miyao = sub_miyao[::-1]
    print("密钥为:{}".format(sub_miyao))
    # 截取密文
    sub_miwen = miwen[-1*(i+1)*2+1:-1*(i+1)*2-1:-1]
    # 反转
    sub_miwen = sub_miwen[::-1]
    print("密文为:{}".format(sub_miwen))
    # 将密文与密钥异或
    result = int(sub_miyao, 16) ^ int(sub_miwen, 16)
    # 格式化为8位二进制字符串
    result_str = format(result,'08b')
    # 首位交换
    result_str = result_str[-1]+result_str[1:7]+result_str[0]
    print("明文二进制为:{}".format(result_str))
    # 转换为十进制
    result = int(result_str, 2)
    print("ascill码为:{}".format(result))
    # 转换为字符
    print(chr(result))
"E:\Python 3.11.0\python.exe" D:\Python\reverse\Cpp3.py 
------------第1个字符-------------
密钥为:06
密文为:52
明文二进制为:01010100
ascill码为:84
T
------------第2个字符-------------
密钥为:07
密文为:C7
明文二进制为:01000001
ascill码为:65
A
------------第3个字符-------------
密钥为:08
密文为:C2
明文二进制为:01001011
ascill码为:75
K
------------第4个字符-------------
密钥为:09
密文为:CD
明文二进制为:01000101
ascill码为:69
E
------------第5个字符-------------
密钥为:0A
密文为:EE
明文二进制为:01100101
ascill码为:101
e
------------第6个字符-------------
密钥为:0B
密文为:EB
明文二进制为:01100001
ascill码为:97
a
------------第7个字符-------------
密钥为:0C
密文为:FE
明文二进制为:01110011
ascill码为:115
s
------------第8个字符-------------
密钥为:0D
密文为:F5
明文二进制为:01111001
ascill码为:121
y

进程已结束,退出代码0

栈溢出攻击初尝试

{题目exe下载, https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/StackOverflow.exe, haofont hao-icon-book}

Flag:任意八位字符

解题逻辑

常规IDA Pro看汇编

main函数

  1. 可以看出主程序通过 子函数sub_401005 对输入字符串进行比较,并将比较结果存放在eax中,若eax等于0,输出答案正确。
  2. 主体程序搞明白了以后,我们进入子函数。

再次跳转

进入真正的子函数

  1. 仔细阅读上述这个函数的汇编代码,他是解题的关键!**我们需要明确的目标是,在这个函数执行结束后,eax的值要为0。**上述图片中已给出了博主写好的注释,相信你可以轻易的读懂它,可以很明显的发现栈溢出攻击的实现是在指令 call _strcpy ,我们可以通过更改输入实现对Destination指向的位置进行更改,当输入的长度超出Destination指向的位置所能容纳的最大长度时,便会溢出到[ebp+var_4],然后通过后面的语句 mov eax, [ebp+var_4] 实现对eax的栈溢出攻击。

想要仅仅通过静态调试很难这么清晰的得到3的结论,所以我们需要在通过静态调试了解程序运行原理后采用动态调试的方式,来辅助我们进行分析。

OD动态调试

  1. 我们先通过静态调试的分析结果,在动态调试的一些关键函数上打上断点,以及做一些必要的注释。

  1. 测试输入abcde,观察eax的值是怎样变化的。

通过该图片我们也可以知道如果输入“1234567”,也可以输出正确。

比较过后由于输入字符串的第一个不匹配的字符acill码值大于预设的字符串,因此eax的值变为了1。

  1. 如上图,箭头标识的很明显了,要赋值给edx的位置就在存放eax的值的上方两个位置。共八个字节,也就是最多存放七个字符(因为字符串的最后一位是结束符/0,它的ascill码值为0),这时候我们只需要输入八位字符,那么/0的就溢出带了存放eax值的位置(小端存储),也就是如下图溢出到01的位置,将其变成了00!

  1. 只要将eax变为0,那么根据我们之前的分析,就可以正确输出,现在我们来测试一下。

说明分析正确!