几个逆向刷题的记录,带点小技巧

admin 2024年2月29日14:15:50评论9 views字数 5873阅读19分34秒阅读模式

Problem: [RE1]P4

[

  • 解题大致思路 拖入IDA,在主函数F5直接阅读代码,代码逻辑比较简单,就是对‘NRQ@PC}?m9kni;k7v&%rq-t!zz{+}|xzA@@@@G@Z’这串字符串进行异或,直接exp即可。 但是本题考察的基本的调试能力,所以观察代码,可知在下列代码执行完后,程序会将最终结果存入内存,所以可以动调追踪直接获取
  for ( i = 0; i < strlen(Str); ++i )
    Str[i] ^= i;

但是在main函数的开头有反调试

  if ( (unsigned int)CheckDebug() )
    return 0;

此处直接用的if判断,如果有调试器则退出,因此汇编代码中一定有一个jz,tab回到反汇编窗口,在jz      short loc_401595处F2下断点,然后调试器选择Local windows debugger,运行 断下后,在寄存器窗口旁边的标识位寄存器窗口,对ZF置1,查看跳转分支发生改变即代表绕过反调试成功 随后回到伪代码,在for ( i = 0; i < strlen(Str); ++i )处下断点,然后运行程序 此时程序没有断下,因为需要我们输入后才能执行到断点处,由于程序对输入长度有限制,所以直接复制strcpy处的字符串 然后程序断下,此时异或循环已经完成,直接双击Str到内存区域可以看到运算结果,选中生成的字符串区域。按A将其转为字符串,得到flag 此处还可以直接对寄存器进行读值,已知异或循环执行后会将结果存入内存,所以直接在异或循环tab来到反汇编窗口 看到异或代码xor     edx, eax,在该段代码下方的mov下断点,并右键编辑断点,将动作改为追踪,因为此处是循环,如果用中断的话,则每一次循环都需要手动运行,然后点击条件右边的三个点,即可输入脚本,此时可以使用IDC脚本或者Python输出寄存器的值,观察异或代码可知异或的结果是存入到edx寄存器中,所以输出edx中的值即可

auto edx = GetRegValue("EDX");
Message("%c",edx&0xff);

这里因为在C语言中,%c用于输出一个字符,所以这里使用&0xff来确保传递给Message函数的参数是一个字节大小的值,对应于ASCII码表中的一个字符 然后重新运行程序,并重复绕过反调试操作,然后重复输入操作,再运行,即可在输出窗口得到flag

  • 具体攻击代码
s = 'NRQ@PC}?m9kni;k7v&%rq-t!zz{+}|xzA@@@@G@Z'
for i in range(len(s)):
    print(chr(ord(s[i])^i),end='')

  • 对该题的考点总结
  • 动态调试

Problem: [RE1]P3

[

  • 解题大致思路 进入IDA,在主函数F5,首先是qmemcpy(v8, &unk_404240, sizeof(v8));,这里很明显将一串字符数据存入了V8变量,双击&unk_404240,将数据提取出来
F3000000490000008A000000ED00000082000000CE00000044000000E2000000350000007C00000023000000F5000000DC000000F800000097000000A40000003B0000006A0000007B000000FE000000E0000000460000007C000000590000001E000000300000001C000000BC00000038000000F90000009900000013000000

将这段文本放入文本编辑器,替换,将所有的0替换为空字符,得到最终的密文原文

F3498AED82CE44E2357C23F5DCF897A43B6A7BFEE467C591E31CBC38F99913

向下看,找到scanf("%s", Str); 此处可知,Str是要输入的flag,选中Str,按N将其改为flag方便阅读 下面的if ( strlen(flag) == 32 )可知,flag长度为32 strcpy((char *)key, "1234567890123456")此处可知加密的key,并知道其长度为16位,根据AES加密的特征可以计算循环加密轮次,16X8=128比特,即10轮 然后向下找到aes加密的函数aesEncrypt(key, 0x10u, v10, (uint8_t *)ct, 0x20u);,双击跟进,在for ( j = 1; j <= 9; ++j )处印证猜想 然后就可以根据这个思路写exp

  • 具体攻击代码
from Crypto.Cipher import AES

def aes_decrypt(key_str, ciphertext_hex):
    # 将16进制字符串转换为字节序列
    ciphertext_bytes = bytes.fromhex(ciphertext_hex)
    # 将字符串密钥转换为字节序列
    key = key_str.encode('utf-8')
    # 创建AES解密器
    cipher = AES.new(key, AES.MODE_ECB)
    # 解密
    decrypted_data = cipher.decrypt(ciphertext_bytes)
    print(decrypted_data)

# 示例

key_str = '1234567890123456'
ciphertext_hex = 'F3498AED82CE44E2357C23F5DCF897A43B6A7BFEE0467C591E301CBC38F99913'
plaintext_hex_result = aes_decrypt(key_str, ciphertext_hex)

  • 对该题的考点总结
  • AES加密算法

Problem: [RE1]P2

[

  • 解题大致思路 进入IDA,首先F5查看伪代码,首先需要输入一串长度为25的字符串 随后会进行for循环
    for ( i = 0; i <= 24; ++i )
    {
      v4[i] = key[i % 6] ^ Str[i];
      v4[i] += 12;
    }

此处运算用到了常量key,双击key跟进,得到key的值为‘NSSCTF’ 根据代码逻辑,首先for循环的次数为25次,与输入的字符串长度一致,然后将key字符串中的第i%6位与Str类型转换后的i进行异或,随后对运算的结果进行+12 然后是第二个for循环

    for ( i = 0; i <= 34; ++i )
    {
      if ( enc[i] != v4[i] )
      {
        printf("No");
        return 0;
      }
    }
    printf("Yes");
  }

此处循环次数为35次,根据下方代码逻辑可知,enc存储的内容为加密后的密文,此处是将上一个for循环的结果与程序预置的结果进行比较,如果一致则输出yes,反之则输出no,所以密文长度应该为35位 双击enc,选中数据,shift+E提取数据

unsigned char enc[] =
{
  0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x41, 0x25, 0x32, 0x3C, 
  0x2C, 0x25, 0x3B, 0x18, 0x2C, 0x36, 0x45, 0x42, 0x2E, 0x42, 
  0x18, 0x27, 0x27, 0x20, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

然后根据代码逻辑编写逆运算EXP

  • 具体攻击代码
s = [0xc,0xc,0xc,0xc,0xc,0xc,0x41,0x25,0x32,0x3c,0x2c,0x25,0x3b,0x18,0x2c,0x36,0x45,0x42,0x2e,0x42,0x18,0x27,0x27,0x20,0x3f]
key = 'NSSCTF'
for i in range(len(s)):
    print(chr((s[i] - 12) ^ ord(key[i%6])),end='')
  • 对该题的考点总结
  • 异或运算

Problem: [RE1]P1

[

  • 解题大致思路

  • 从压栈往下,分别看到puts和scanf,此处代码无用,但是该段代码中有一处比较代码,cmp     rax, 23h,转换为10进制为35,观察hint,发现长度一致,所以明文长度应该为35

  • 向下走,有一个jmp跳转,jmp     loc_40162B,查找40162B,发现该处为出栈代码,故继续查找该地址的调用,发现一处字符串‘Wrong’,lea     rcx, aWrong     ; "Wrong!",上方有一处跳转 jz      short loc_401614,查看401614,进行了一次加法运算,然后跳转 jmp     short loc_4015A2,查看4015A2,发现回到了Wrong的上方,故关键代码应该在4015A2到Wrong之前,在此处查看与运算相关的代码,发现一处异或xor     eax, 52h,以及一处加法运算,add     eax, 5,此处运算的对象是eax,eax的赋值在上方mov     eax, [rbp+var_4],此处[rbp+var_4]从哪来的没有给出,故猜测为hint的内容,直接exp

  • 具体攻击代码

 a = [0x21,0x6,0x6,0x16,0xb,0x19,0x2e,0x65,0x35,0x6a,0x6f,0x38,0x36,0x84,0x70,0x3b,0x39,0x65,0x38,0x35,0x84,0x6f,0x36,0x3c,0x6a,0x38,0x68,0x84,0x66,0x70,0x3b,0x38,0x6a,0x36,0x34,]
for i in a:
 print(chr((i-5^0x52)),end='')
  • 对该题的考点总结
  • 代码阅读能力
  • 异或运算

Problem: [RE1]P7

[

  • 解题大致思路 首先exeinfo查壳,可知是upx壳,尝试IDA载入,强制忽略错误后发现无法读出函数列表,故需要脱壳 载入X64dbg,ESP定律几个逆向刷题的记录,带点小技巧首先按几次F9,直到程序运行到EntryPoint 然后F7,观察寄存器窗口几个逆向刷题的记录,带点小技巧此处只有RSP高亮,直接在RSP处右键,在内存窗口中转到,然后在内存窗口中会自动选中几个逆向刷题的记录,带点小技巧在此处右键,断点,硬件访问,4字节 然后在断点窗口确认下断成功几个逆向刷题的记录,带点小技巧F9运行,断在几个pop下方几个逆向刷题的记录,带点小技巧下面有一个大跳转的jmp,在jmp下断,F9运行,然后单步步进几个逆向刷题的记录,带点小技巧来到OEP几个逆向刷题的记录,带点小技巧直接脱壳几个逆向刷题的记录,带点小技巧再次将脱壳后的程序载入IDA,忽略错误,成功读取函数列表,F5可以正常出现伪代码几个逆向刷题的记录,带点小技巧分析main函数,可知为Z3解方程类题型几个逆向刷题的记录,带点小技巧直接写exp解方程即可

  • 具体攻击代码

from z3 import *

# 创建Solver实例
s = Solver()

# 为flag的每个字符创建一个整数变量
flag = [Int(f'flag[{i}]'for i in range(16)]

# 添加约束条件
s.add(7 * flag[0] == 546)
s.add(2 * flag[1] == 166)
s.add(6 * flag[2] + flag[3] + 7 * flag[5] == 1055)
s.add(2 * flag[13] + flag[8] + 7 * flag[5] + flag[3] + 4 * flag[1] + 4 * flag[4] + 6 * flag[7] + 8 * flag[15] == 3107)
s.add(4 * flag[4] == 336)
s.add(2 * flag[1] + 7 * flag[5] == 656)
s.add(2 * flag[13] + 3 * flag[11] + 3 * flag[6] + 6 * flag[7] + flag[8] + 5 * flag[9] + 16 * flag[10] + 6 * flag[12] + 8 * flag[15] == 5749)
s.add(6 * flag[7] == 606)
s.add(5 * flag[14] + flag[8] == 652)
s.add(5 * flag[9] + 16 * flag[10] + 6 * flag[12] == 3213)
s.add(2 * flag[13] + 3 * flag[11] + 24 * flag[10] + 5 * flag[9] + 3 * flag[6] + 6 * flag[7] + flag[8] + 6 * flag[12] + 8 * flag[15] == 6717)
s.add(3 * flag[11] == 285)
s.add(2 * flag[8] + 3 * flag[6] + 6 * flag[7] + 8 * flag[10] + 6 * flag[12] + 2 * flag[13] + 5 * flag[14] + 8 * flag[15] == 4573)
s.add(5 * flag[14] == 600)
s.add(flag[3] + 6 * flag[2] + 4 * flag[4] + 7 * flag[5] + 2 * flag[13] == 1615)
s.add(flag[8] + 7 * flag[5] + 2 * flag[1] + 6 * flag[7] + 8 * flag[15] == 2314)

# 尝试求解
if s.check() == sat:
    m = s.model()
    # 输出flag
    flag_str = ''.join(chr(m[flag[i]].as_long()) for i in range(16))
    print(f'Flag: {flag_str}')
else:
    print('No solution found.')

  • 对该题的考点总结
  • 脱壳
  • Z3约束求解

原文始发于微信公众号(YNsec安全实验室):几个逆向刷题的记录,带点小技巧

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月29日14:15:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   几个逆向刷题的记录,带点小技巧http://cn-sec.com/archives/2534330.html

发表评论

匿名网友 填写信息