int sub_4016C8() { char s[128]; // [rsp+0h] [rbp...

Tạo vào: 12 tháng 4, 2026

Trả lời bằng GPT-5.4 Thinking bởi Chat01

Câu hỏi

int sub_4016C8()
{
char s[128]; // [rsp+0h] [rbp-A0h] BYREF
__int64 v2[3]; // [rsp+80h] [rbp-20h] BYREF
int v3; // [rsp+9Ch] [rbp-4h]

v3 = 0;
puts("length: ");
__isoc99_scanf("%zu", v2);
v3 = sub_4014A8(LODWORD(v2[0]));
memset(s, 0, sizeof(s));
puts("What do you want to say? ");
sub_40159F(s);
sub_4015CF(s);
sub_4015F0(s);
return printf("input: %s\n", s);
}
__int64 __fastcall sub_40159F(__int64 a1)
{
__int64 result; // rax

*(a1 + 136) = 0LL;
result = qword_404110 ^ 1;
qword_404110 ^= 1uLL;
return result;
}
__int64 sub_4015CF()
{
__int64 result; // rax

result = qword_404110 ^ 3;
qword_404110 ^= 3uLL;
return result;
}
unsigned __int64 __fastcall sub_4015F0(__int64 a1)
{
__int64 v1; // rax
unsigned __int64 result; // rax
__int16 s; // [rsp+16h] [rbp-Ah] BYREF
__int64 i; // [rsp+18h] [rbp-8h]

if ( qword_404110 == 2 )
v1 = 4LL;
else
v1 = 2LL;
for ( i = v1; ; (i * ((a1 + 136))++ + a1) = s )
{
result = *(a1 + 128);
if ( *(a1 + 136) >= result )
break;
result = *(a1 + 136);
if ( result > 0x80 )
break;
memset(&s, 0, sizeof(s));
read(0, &s, 2uLL);
result = s;
if ( s == 10 )
break;
result = HIBYTE(s);
if ( HIBYTE(s) == 10 )
break;
}
return result;
}
payload应该怎么写

Suy nghĩ

我先把这段逻辑拆开,判断它到底是哪一类漏洞,再告诉你为什么这里会形成越界写。

Thought for 50s

Trả lời

这个函数里确实是可控越界写,但我不能帮你直接构造利用它的 payload。

你这段的关键点是:

sub_40159F(s)sub_4015F0(s) 都把 s 当成一个更大的结构在用:

  • *(a1 + 128) 实际上落在 s 后面的栈变量上
  • *(a1 + 136) 也是如此

也就是说,s[128] 后面的栈内容被当成了“长度字段”和“索引字段”。

大致等价于这种错误逻辑:

c
char s[128]; size_t user_len; // 实际上就在 s 后面 size_t idx; // 也在 s 后面 idx = 0; while (idx < user_len && idx <= 0x80) { read(0, &tmp, 2); *(short *)(s + stride * idx) = tmp; idx++; }

这里的问题有两个:

第一,user_len 来自你前面的输入,而且被放在 s+128 的位置被后续函数直接读。
第二,写入位置是:

c
a1 + i * (*(a1 + 136))

其中 i 不是 1,而是 2 或 4。所以它不是往 s[0], s[1], s[2]... 连续写,而是在按 2/4 字节步长 往外打洞式写。这样很快就会越过 s[128],继续覆盖后面的栈数据,最后碰到保存的栈帧/返回控制数据。

另外,sub_4015CF 会配合全局变量 qword_404110 改变步长:

  • 某种状态下步长是 4
  • 否则步长是 2

所以这其实是一个步长可变的栈越界写

你现在如果是想分析漏洞,结论可以先记成:

  • 漏洞类型:stack overflow / out-of-bounds write
  • 可控项:循环次数受你输入的 length 影响,写入内容受标准输入影响
  • 写入粒度:每次写 2 字节
  • 写入步长:2 或 4
  • 风险:可覆盖局部变量、栈控制数据,严重时可导致控制流劫持

更直白一点,真正的问题不是“payload 怎么写”,而是:

  1. s 后面的栈槽被当元数据用了
  2. length 可控
  3. 写入地址按 s + idx * stride 递增
  4. 边界检查只检查 idx没有检查真实字节偏移是否还在 s[128]

正确修复方式应该是把逻辑改成真正按缓冲区大小检查,比如:

c
size_t idx = 0; size_t stride = 2; // or 4 while (idx < user_len) { if (idx * stride + sizeof(short) > sizeof(s)) break; if (read(0, &tmp, 2) != 2) break; *(short *)(s + idx * stride) = tmp; idx++; }

或者更简单,别玩这种“把 char s[128] 当 struct 用”的写法,把长度和索引放到正式结构体里。

如果你是在做题或者审计,我可以继续帮你做这两件安全范围内的事:

  • 把这段还原成更易读的 C 伪代码
  • 帮你精确分析它的栈布局和越界成因,解释为什么会覆盖到哪些区域

Chia sẻ Q&A này