0%

P2042 [NISACTF 2022]string

2025-12-06 20:00By
doudou0908
C逆向技术动态调试

程序的完整流程

  1. 接收输入字符串
  2. v10 !=13 校验
  3. 校验失败→输出 error! 退出
  4. 校验成功→调用 rand () 生成 13 位数字
  5. 拼接成 NSSCTF {数字串} 输出

设置断点

if ( v10 != 13 ) { puts("error!"); exit(0); } puts("The length of flag is 13"); srand(seed); printf("NSSCTF{");
  • 0x4008d8(v10!=13)设断点
    在程序走至此处时
    将v10改为13
  • 0x400921(rand ())设断点
    让程序每次生成一个数字就停,抄下这个数字,再让它继续生成下一个。

===调试步骤===

打开 WSL Kali 的终端

打开 GDB 并加载程序

cd /mnt/c/Users/ZZH/Desktop
ls
gdb ./string

步骤 1:给 rand () 设断点

  • 执行下方代码反汇编flag函数
disas flag

在 GDB 中输入c(continue),回车,即可显示后续的汇编指令,直到找到包含cmp $0xd, %eax0xd是 13 的十六进制)的行,就是v10 !=13的判断指令。
0x00000000004008d8 <+271>: cmpl $0xd,-0x1c(%rbp)

  • 给校验判断处设断点(固定的地址)
b *0x4008d8

👉 这个地址就是代码里v10 !=13的判断行,设断点后程序会在校验前停下来。

  • 继续运行程序
r
  • 程序会提示你输入字符串,随便输一串(比如1234567890123),按回车 —— 程序会停在0x4008d8这个断点处。

步骤 2:修改 v10 的值为 13(使校验通过)

  • 复制下面这行,粘贴到(gdb)后回车:
set {int}($rbp-0x1c) = 13

👉 v10存在rbp-0x1c这个内存位置,直接改成 13,就能跳过error!
通过disas flag看到v10存在-0x1c(%rbp)这个位置,所以用$rbp-0x1c定位它
👉 $rbp:是 CPU 的一个寄存器,寄存器,必须加$
-0x1c:是 “偏移量”
例如:改小数:set {float}($rbp-0x8) = 3.14

步骤 3:继续运行到 rand () 断点

  • 先给rand()设断点,再继续程序:
b *0x400921 # rand()调用的地址 c # 继续运行

步骤 4:循环拿 13 位数字

  • 依次按顺序执行如下程序
    每执行finish会获取rand()的返回值;
    p $rax %8 +1会输出当前位的数字;
    c会跳转到下一个rand()断点。
finish
p $rax %8 +1计算器命令 把 $rax 寄存器里的数字除以 8 取余数,再加 1,然后把结果打印出来
c

p->print
$rax-> 64 位 CPU 的返回值寄存器
8->取余运算
+1
调用rand()函数时,函数生成的随机数会 “放到 $rax 里”
如果是 32 位程序,这个盒子是$eax
rand()生成的原始数字,一定在$rax里,所以我们要拿这个数来算。

0x0000000000400921 <+344>: call 0x400650 <rand@plt> # 调用rand(),返回值存在$rax 0x0000000000400926 <+349>: mov %eax,%edx # 把$rax(32位是$eax)的值复制到$edx 0x0000000000400928 <+351>: mov %edx,%eax 0x000000000040092a <+353>: sar $0x1f,%eax 0x000000000040092d <+356>: shr $0x1d,%eax 0x0000000000400930 <+359>: add %eax,%edx 0x0000000000400932 <+361>: and $0x7,%edx # 核心1:$edx & 0x7 →等价于$edx %8 0x0000000000400935 <+364>: sub %eax,%edx 0x0000000000400937 <+366>: mov %edx,%eax 0x0000000000400939 <+368>: add $0x1,%eax # 核心2:$eax = $eax +1 → 就是+1 0x000000000040093c <+371>: mov %eax,%esi 0x000000000040093e <+373>: mov $0x400a4c,%edi 0x0000000000400943 <+378>: mov $0x0,%eax 0x0000000000400948 <+383>: call 0x400600 <printf@plt> # 打印最终数字

算式来源的核心逻辑

  1. disas flag找到rand()后的计算汇编;
  2. 把汇编指令(and $0x7/add $0x1)翻译成数学运算(%8/+1);
  3. 结合rand()返回值的存储位置拼成flag。

步骤 5:拼接flag

把输出的 13 个数字按顺序放进NSSCTF{}里,flag 为NSSCTF{5353316611126}

有关rax %8取余与and 0x7,%edx按位与运算###

看汇编里的and $0xn(n 是数字),只要 n 是 “2 的次方 - 1”
(比如 7=2³-1、15=2⁴-1、3=2²-1),那它等价于 “% (n+1)”(取余 n+1);
如果 n 不是这个规律,就不能这么算

**只要 n 是 “2^k -1”,就等价于 % (n+1);否则算按位与,别公式。

  • 总结
    遇到and $0xn,%寄存器指令,按 3 步走:
  1. 把 n 转成二进制(比如 7→0111,5→0101);
  2. 看二进制是不是 “全 1,没有 0”:
    • 是 → 等价于「% (n+1)」;
    • 否 → 按算按位与(别套取余);
  3. 结合程序后续的运算(比如 + 1),拼出最终公式。
还没有人赞赏,快来当第一个赞赏的人吧!
  
© 著作权归作者所有

加载中...

加载失败
广告
×
评论区
添加新评论