int sub_4F1500() { int v0; // eax unsigned int...

Creado el: 9 de julio de 2026

Respondido usando GPT-5.5 Thinking por Chat01

Pregunta

int sub_4F1500()
{
int v0; // eax
unsigned int v1; // esi
__m128i v2; // xmm1
const __m128i *DURL; // eax
__m128i v4; // xmm0
int *v5; // esi
int n7; // edi
_DWORD v8[2]; // [esp+8h] [ebp-C8h] BYREF
_DWORD v9[22]; // [esp+10h] [ebp-C0h] BYREF
int v10; // [esp+68h] [ebp-68h] BYREF
__int64 v11; // [esp+6Ch] [ebp-64h]
__int64 v12; // [esp+74h] [ebp-5Ch]
__int64 v13; // [esp+7Ch] [ebp-54h]
char v14; // [esp+84h] [ebp-4Ch] BYREF
__int64 v15; // [esp+85h] [ebp-4Bh]
__int64 v16; // [esp+8Dh] [ebp-43h]
__int64 v17; // [esp+95h] [ebp-3Bh]
__int64 v18; // [esp+9Dh] [ebp-33h]
char v19; // [esp+A8h] [ebp-28h] BYREF
__int64 v20; // [esp+A9h] [ebp-27h]
__int64 v21; // [esp+B1h] [ebp-1Fh]
__int64 v22; // [esp+B9h] [ebp-17h]
__int64 v23; // [esp+C1h] [ebp-Fh]

puts("=============================================================");
puts(" ");
puts(" Welcome To Reversing Adventure!!!! ");
puts(" ");
puts("=============================================================");
puts("[+] Game Start");
puts("You Find a special map when you sleep at Math course.");
puts("You try to read it, but you find that you could not understand what it's mean.");
puts("But...Hey, what's that?");
puts("6efef13f2a55a4ff356fab3b04f48d57??");
printf("[=]I have to seen this, it is:");
v8[0] = 0;
v8[1] = 0;
scanf("%d", v8);
v19 = 0;
v20 = 0LL;
v21 = 0LL;
v22 = 0LL;
v23 = 0LL;
v14 = 0;
v15 = 0LL;
v16 = 0LL;
v17 = 0LL;
v18 = 0LL;
v9[0] = 0;
v9[5] = 0;
v9[1] = 0x67452301;
v9[2] = 0xEFCDAB89;
v9[3] = 0x98BADCFE;
v9[4] = 0x10325476;
sub_4F18D0(v9, (int)v8);
sub_4F1A90(v9, &v19);
sub_4F1340((int)&v14, (int)&v19);
v0 = strcmp(&v14, "6efef13f2a55a4ff356fab3b04f48d57");
if ( v0 )
v0 = v0 < 0 ? -1 : 1;
if ( v0 )
{
puts("Game Over...");
}
else
{
v1 = v8[0];
puts("reading...");
v2 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v1), 0);
DURL = (const __m128i *)::DURL;
// "DURL"
do
{
v4 = _mm_loadu_si128(DURL++);
DURL[-1] = _mm_xor_si128(v4, v2);
}
while ( (int)DURL < (int)aDurl );
puts(""Yes I did it"");
puts("[+]Try to find treasure!");
v10 = 0;
v11 = 0LL;
v12 = 0LL;
v13 = 0LL;
v5 = &v10;
n7 = 7;
do
{
scanf("%d", v5++);
--n7;
}
while ( n7 );
if ( sub_4F1390() )
{
sub_4F1000(&v10);
sub_4F1440();
return 1;
}
printf("Opps....\n");
}
return 0;
}
这是一道ctf逆向题目,请你提供思路解出flag

Python

python
import hashlib, itertools, time, struct, sys, os, re, math target='6efef13f2a55a4ff356fab3b04f48d57' # try decimal string up to maybe 10 million t=time.time() found=[] for i in range(10000000): if hashlib.md5(str(i).encode()).hexdigest()==target: print("dec string found", i) found.append(i); break print("done", time.time()-t, found[:5])

Python

python
target='6efef13f2a55a4ff356fab3b04f48d57' # empty to 3 bytes nonzero for L in range(0,4): cnt=0 t=time.time() if L==0: if hashlib.md5(b'').hexdigest()==target: print("empty") else: # all nonzero bytes for bs in itertools.product(range(1,256), repeat=L): b=bytes(bs) if hashlib.md5(b).hexdigest()==target: print("found bytes", L, b, int.from_bytes(b + b'\x00'*(4-L),'little', signed=True)) raise SystemExit print("L",L,"done",time.time()-t) print("not found <=3")

Python

python
import hashlib hashlib.md5(b"2742473").hexdigest() 'a0849a74bafb55f89ce4ec873cc37fdb'

Python

python
hashlib.md5(b"nuaa").hexdigest() '6efef13f2a55a4ff356fab3b04f48d57'

Pensamiento

Examining potential CTF challenge solution

I've identified the decimal input as 1633777006 from the bytes "nuaa." The MD5Update uses the string "nuaa" as expected. Now, I need to focus on the second stage of the problem, where DURL and sub-functions are missing. Could this be a "Nuaa" CTF challenge? I'll work on parsing stage 2 and inferring data to narrow down the missing variables.

Thought for 6m 51s

Respuesta

这题可以先确定第一关输入

text
第一关输入:1633777006

原因如下。

代码里虽然 scanf("%d", v8) 读的是整数,但后面:

c
sub_4F18D0(v9, (int)v8); sub_4F1A90(v9, &v19); sub_4F1340((int)&v14, (int)&v19); strcmp(&v14, "6efef13f2a55a4ff356fab3b04f48d57");

这一套明显是:

c
MD5(v8内存字节) == 6efef13f2a55a4ff356fab3b04f48d57

v8[0] 是一个 32 位整数。关键点在于 小端序

text
MD5("nuaa") = 6efef13f2a55a4ff356fab3b04f48d57

而字符串 "nuaa" 的字节是:

text
6e 75 61 61

按小端序作为整数就是:

text
0x6161756e = 1633777006

所以第一关应该输入:

text
1633777006

通过第一关后,程序会执行:

c
v1 = v8[0]; v2 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v1), 0); DURL = (const __m128i *)::DURL; do { v4 = _mm_loadu_si128(DURL++); DURL[-1] = _mm_xor_si128(v4, v2); } while ((int)DURL < (int)aDurl);

也就是用刚才的 key:

text
0x6161756e

对全局数据段 DURL ~ aDurl 做 XOR 解密。因为是 SSE 一次处理 16 字节,本质上等价于:

text
encrypted_data ^ b"nuaa" 循环

也就是每 4 字节异或一次:

text
6e 75 61 61

解题流程

第一步:确认 MD5 输入形式

这不是:

c
scanf("%s", input); MD5(input);

而是:

c
scanf("%d", v8); MD5((char *)v8);

所以它实际哈希的是整数在内存里的字节。

可以用 Python 验证:

python
import hashlib print(hashlib.md5(b"nuaa").hexdigest())

输出:

text
6efef13f2a55a4ff356fab3b04f48d57

然后:

python
print(int.from_bytes(b"nuaa", "little"))

输出:

text
1633777006

第二步:解密 DURL

在 IDA 里找到 DURLaDurl 的地址,然后用 IDAPython 解密:

python
import ida_bytes import ida_name start = ida_name.get_name_ea(0, "DURL") end = ida_name.get_name_ea(0, "aDurl") enc = ida_bytes.get_bytes(start, end - start) key = b"nuaa" dec = bytes([enc[i] ^ key[i % 4] for i in range(len(enc))]) print(dec)

如果解出来是字符串,直接看内容;如果解出来像代码,需要把这段数据 patch 回去:

python
for i, b in enumerate(dec): ida_bytes.patch_byte(start + i, b)

然后在 IDA 里按 C 重新反汇编,或者按 A 尝试识别字符串。


第三步:分析 7 个整数输入

后面这里:

c
v10 = 0; v11 = 0LL; v12 = 0LL; v13 = 0LL; v5 = &v10; n7 = 7; do { scanf("%d", v5++); --n7; } while (n7);

说明第二关需要输入 7 个整数:

text
x0 x1 x2 x3 x4 x5 x6

这 7 个整数会作为数组传给:

c
sub_4F1000(&v10); sub_4F1440();

但真正判断能否进入下一步的是:

c
if (sub_4F1390())

所以接下来要重点逆:

c
sub_4F1390 sub_4F1000 sub_4F1440

建议顺序是:

  1. 先输入 1633777006,让程序正常解密 DURL
  2. sub_4F1390 下断点,看它到底检查什么。
  3. 如果 sub_4F1390 只是检查解密是否成功,可以直接继续。
  4. 如果 sub_4F1390 内部对全局数据做校验,要先分析解密后的 DURL
  5. 重点看 sub_4F1000(&v10),这通常就是把 7 个整数变换成 flag 或坐标。
  6. 最后 sub_4F1440() 很可能是打印 flag 或最终提示。

动态调试建议

用 x32dbg / OllyDbg:

在这几处下断点:

asm
strcmp sub_4F1390 sub_4F1000 sub_4F1440

运行后第一关输入:

text
1633777006

然后程序会打印:

text
reading... "Yes I did it" [+]Try to find treasure!

此时说明第一关已经过了。

接着随便输入 7 个整数,例如:

text
1 2 3 4 5 6 7

然后进入 sub_4F1390 / sub_4F1000 / sub_4F1440 观察逻辑。


如果想快速绕过第二关

可以在调试器里把:

c
if (sub_4F1390())

强行改成成功。

常见方式:

方法 1:断在 sub_4F1390 返回后

看类似:

asm
call sub_4F1390 test eax, eax jz short fail

EAX 改成:

text
1

或者把 jz patch 成 jnz / nop nop

方法 2:直接 patch sub_4F1390

让它变成:

asm
mov eax, 1 ret

然后继续运行,看 sub_4F1000sub_4F1440 是否会直接吐 flag。


当前能确定的结论

第一关答案确定是:

text
1633777006

它对应:

text
0x6161756e

内存小端字节:

text
6e 75 61 61

也就是:

text
nuaa

并且:

text
MD5("nuaa") = 6efef13f2a55a4ff356fab3b04f48d57

后续 flag 需要继续看这三个函数和全局数据:

c
sub_4F1390() sub_4F1000(&v10) sub_4F1440() DURL ~ aDurl

你把 sub_4F1390、sub_4F1000、sub_4F1440 的反编译代码,或者 DURL 那段十六进制数据贴出来,就能继续把 7 个整数和最终 flag 推出来。

Pregunta

int __thiscall sub_4F1390(int *this)
{
int n11; // esi
int v2; // edi
_QWORD *v3; // edx
char v4; // cl
int v5; // ecx
int v6; // eax
char n32; // al

n11 = 11;
v2 = 1;
v3 = sub_4F1130(this);
do
{
v4 = *(_BYTE *)v3;
if ( *(_BYTE *)v3 == 'U' )
{
v5 = 0;
v6 = -1;
}
else
{
switch ( v4 )
{
case 'D':
v5 = 0;
v6 = 1;
break;
case 'L':
v6 = 0;
v5 = -1;
break;
case 'R':
v6 = 0;
v5 = 1;
break;
default:
printf("Hei! what are you doing!\n");
exit(-1);
}
}
n11 += v6;
dword_4F4538 = v6;
v2 += v5;
v3 = (_QWORD *)((char *)v3 + 1);
n32 = DURL[13 * n11 + v2];
dword_4F453C = v5;
}
while ( n32 == ' ' || n32 == 'o' );
if ( n32 == 'E' )
{
printf("Wow, I find It!\n");
return 1;
}
else
{
printf("BOOM!\n");
return 0;
}
}
int __thiscall sub_4F1000(_BYTE *this)
{
_BYTE *v1; // edi
int i; // esi
unsigned int v3; // eax
int result; // eax

v1 = this;
printf("let's take a look at it..\n");
for ( i = 0; i < 208; byte_4F4117[i] ^= result )
{
src[i] ^= v1[4 * (i % 7u)];
byte_4F4119[i] ^= this[4 * (i - 7 * ((i + 1) / 7u)) + 4];
byte_4F411A[i] ^= this[4 * (i - 7 * ((i + 2) / 7u)) + 8];
v1 = this;
v3 = i - 7 * ((i + 3) / 7u);
i += 4;
result = (unsigned __int8)this[4 * v3 + 12];
}
return result;
}
int sub_4F1440()
{
HANDLE hHeap; // eax
char *dst; // ebx
char v3; // [esp+Ch] [ebp-28h] BYREF
__int64 v4; // [esp+Dh] [ebp-27h]
__int64 v5; // [esp+15h] [ebp-1Fh]
__int64 v6; // [esp+1Dh] [ebp-17h]
__int64 v7; // [esp+25h] [ebp-Fh]

hHeap = HeapCreate(0x40000u, 0, 0);
dst = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x1000u);
qmemcpy(dst, src, 0xD0u);
puts("Wow...This is flag!");
puts("Hey, I need check the flag is fake or not....");
puts("try input flag:");
v3 = 0;
v4 = 0LL;
v5 = 0LL;
v6 = 0LL;
v7 = 0LL;
scanf_s("%s", &v3);
if ( ((int (__cdecl *)(char *))dst)(&v3) )
return puts("Congratulation!");
else
return puts("Sorry, just miss a little!");
}

Pensamiento

undefined

Pensamiento

undefined

Pensamiento

Thought for 45s

Respuesta

这几段代码已经能确定整体解法结构了,但还差 sub_4F1130、解密后的 DURL 迷宫数据、src 密文数据这几项,才能直接算出最终 flag。现在可以先把核心逻辑拆出来。


1. 第一关确定:输入 1633777006

前面已经确定:

text
MD5("nuaa") = 6efef13f2a55a4ff356fab3b04f48d57

程序输入的是整数,但 MD5 算的是整数的小端内存字节。

text
"nuaa" = 6e 75 61 61 小端整数 = 0x6161756e = 1633777006

所以第一关输入:

text
1633777006

2. 第一关通过后,DURL 会被解密成迷宫

这一段:

c
v1 = v8[0]; v2 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v1), 0); do { v4 = _mm_loadu_si128(DURL++); DURL[-1] = _mm_xor_si128(v4, v2); } while ((int)DURL < (int)aDurl);

本质上是:

python
DURL[i] ^= b"nuaa"[i % 4]

解密后的 DURL 是一个宽度为 13 的迷宫,因为后面访问方式是:

c
DURL[13 * n11 + v2]

也就是:

text
DURL[row][col]

起点是:

c
n11 = 11; v2 = 1;

也就是:

text
row = 11, col = 1

3. sub_4F1390 是迷宫路径校验

核心逻辑如下:

c
v3 = sub_4F1130(this); do { ch = *v3; if (ch == 'U') { row -= 1; col += 0; } else if (ch == 'D') { row += 1; col += 0; } else if (ch == 'L') { row += 0; col -= 1; } else if (ch == 'R') { row += 0; col += 1; } else { exit(-1); } cur = DURL[13 * row + col]; v3++; } while (cur == ' ' || cur == 'o'); if (cur == 'E') return 1; else return 0;

也就是说,sub_4F1130(this) 会返回一串方向字符:

text
U / D / L / R

然后程序从迷宫坐标:

text
(11, 1)

开始走。

只要当前格子是:

text
空格 ' ' 或者 'o'

就继续走。

如果最后走到:

text
'E'

则通过。

如果撞到别的字符,例如墙、边界、非法字符,就失败。


4. 第二关 7 个整数不是普通坐标,而是 XOR key

这里读入 7 个整数:

c
v10 = 0; v11 = 0LL; v12 = 0LL; v13 = 0LL; v5 = &v10; n7 = 7; do { scanf("%d", v5++); --n7; } while (n7);

内存布局是:

text
v10 -> 第 1 个 int v10 + 4 -> 第 2 个 int v10 + 8 -> 第 3 个 int ... v10 + 24 -> 第 7 个 int

后面 sub_4F1000(&v10) 只取每个整数的最低 1 字节作为 key。

也就是说,真正有用的是:

text
x0 & 0xff x1 & 0xff x2 & 0xff x3 & 0xff x4 & 0xff x5 & 0xff x6 & 0xff

5. sub_4F1000 是 7 字节循环 XOR 解密 shellcode

虽然反编译看起来有点乱:

c
src[i] ^= v1[4 * (i % 7u)]; byte_4F4119[i] ^= this[4 * (i - 7 * ((i + 1) / 7u)) + 4]; byte_4F411A[i] ^= this[4 * (i - 7 * ((i + 2) / 7u)) + 8]; ... result = (unsigned __int8)this[4 * v3 + 12];

但整理后,本质就是:

python
src[i] ^= key[i % 7]

其中:

python
key = [ input0 & 0xff, input1 & 0xff, input2 & 0xff, input3 & 0xff, input4 & 0xff, input5 & 0xff, input6 & 0xff, ]

所以这 7 个整数的低字节就是解密 src 的 7 字节密钥。


6. sub_4F1440 是执行解密后的代码

这一段很关键:

c
hHeap = HeapCreate(0x40000u, 0, 0); dst = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 0x1000u); qmemcpy(dst, src, 0xD0u); puts("Wow...This is flag!"); puts("Hey, I need check the flag is fake or not...."); puts("try input flag:"); scanf_s("%s", &v3); if (((int (__cdecl *)(char *))dst)(&v3)) puts("Congratulation!"); else puts("Sorry, just miss a little!");

也就是说,src 解密后是一段 x86 代码,程序会把它复制到堆上,然后直接执行:

c
dst(input_flag)

所以最终 flag 的校验逻辑在 src 解密后的 shellcode 里。


当前完整解题路线

你要解出 flag,建议这样做。


第一步:输入第一关 key

运行程序时输入:

text
1633777006

这样 DURL 会被解密。


第二步:dump 解密后的 DURL

在 IDA 里可以用:

python
import ida_bytes import ida_name start = ida_name.get_name_ea(0, "DURL") end = ida_name.get_name_ea(0, "aDurl") data = ida_bytes.get_bytes(start, end - start) key = b"nuaa" dec = bytes([data[i] ^ key[i % 4] for i in range(len(data))]) print(dec.decode(errors="replace")) for i in range(0, len(dec), 13): print(dec[i:i+13].decode(errors="replace"))

如果程序已经运行过并且内存里的 DURL 已经被解密,就直接 dump 运行时内存也可以。


第三步:在迷宫里找路径

迷宫宽度是 13,起点是:

text
row = 11 col = 1

终点是字符:

text
E

可走格子是:

text
' ' 'o' 'E'

可以用这个脚本自动找路径:

python
from collections import deque maze_raw = b""" 这里填入你解密后的 DURL """.strip(b"\n") W = 13 H = len(maze_raw) // W maze = [maze_raw[i * W:(i + 1) * W] for i in range(H)] start = (11, 1) dirs = [ ("U", -1, 0), ("D", 1, 0), ("L", 0, -1), ("R", 0, 1), ] q = deque() q.append((start[0], start[1], "")) vis = set() vis.add(start) while q: r, c, path = q.popleft() for ch, dr, dc in dirs: nr, nc = r + dr, c + dc if not (0 <= nr < H and 0 <= nc < W): continue cell = chr(maze[nr][nc]) if cell == "E": print("FOUND:", path + ch) raise SystemExit if cell in [" ", "o"] and (nr, nc) not in vis: vis.add((nr, nc)) q.append((nr, nc, path + ch)) print("not found")

得到的 path 应该就是 sub_4F1390 希望走出来的方向序列。


第四步:分析 sub_4F1130

现在最关键的是这个函数:

c
v3 = sub_4F1130(this);

它负责把你输入的 7 个整数转换成方向字符串。

你目前还没贴 sub_4F1130,所以现在只能确定:

text
7 个整数 -> sub_4F1130 -> UDLR 路径字符串 -> 走迷宫

也就是说,解出迷宫路径后,还要根据 sub_4F1130 反推出 7 个整数。

你需要继续看 sub_4F1130 的逻辑。它大概率有几种可能:

情况 1:直接把 7 个整数当 ASCII 用

比如:

c
return (char *)this;

那么 7 个整数的低字节可能直接是路径字符,例如:

text
85 68 82 82 85 76 ...

分别对应:

text
U D R R U L ...

情况 2:把整数转成 base4 路径

比如每个整数拆成若干位:

text
0 -> U 1 -> D 2 -> L 3 -> R

这种情况常见于迷宫题,因为 7 个整数可以编码很长的路径。

情况 3:先变形再映射

例如:

text
input[i] ^ 常数 input[i] + 常数 input[i] % 4

再映射成方向。


第五步:用 7 个整数解密 src

假设最后求出了 7 个整数:

text
x0 x1 x2 x3 x4 x5 x6

就可以用它们解密 src

python
import ida_bytes import ida_name src_start = ida_name.get_name_ea(0, "src") src_len = 0xD0 enc = ida_bytes.get_bytes(src_start, src_len) xs = [x0, x1, x2, x3, x4, x5, x6] key = bytes([x & 0xff for x in xs]) dec = bytes([enc[i] ^ key[i % 7] for i in range(len(enc))]) open("decoded_shellcode.bin", "wb").write(dec) print(dec.hex())

然后用 IDA 新建一个 binary 文件,架构选:

text
x86 32-bit

加载 decoded_shellcode.bin,反汇编即可看到最终 flag 校验逻辑。


第六步:直接动态调试拿 flag

更快的方法是:

  1. 第一关输入:
text
1633777006
  1. 第二关输入你根据迷宫和 sub_4F1130 算出的 7 个整数。

  2. 在这里下断点:

c
if (((int (__cdecl *)(char *))dst)(&v3))
  1. 跟进 dst,它就是解密后的 shellcode。

  2. 反汇编 shellcode,找对用户输入 flag 的比较逻辑。

常见情况是:

asm
cmp byte ptr [flag+i], imm xor / add / sub / rol / ror

或者:

asm
MD5 / CRC / 自定义异或校验

如果逻辑简单,直接静态解;如果复杂,可以用 angr / z3 符号执行。


目前能确定的关键结论

这题不是单纯迷宫题,而是三层结构:

text
第一层: 输入整数 1633777006 => 内存字节为 "nuaa" => MD5 校验通过 => 用 "nuaa" 解密 DURL 第二层: 输入 7 个整数 => sub_4F1130 转成 U/D/L/R 路径 => sub_4F1390 在 DURL 迷宫中从 (11,1) 走到 E 第三层: 同样的 7 个整数低字节 => 作为 7 字节循环 XOR key => 解密 src => src 变成一段 x86 校验 shellcode => 输入 flag => shellcode 返回 1 则成功

所以当前最重要的缺失项是:

c
sub_4F1130

以及全局数据:

text
DURL ~ aDurl src 的 0xD0 字节

你继续贴 sub_4F1130DURL/src 的十六进制数据后,就可以把 7 个整数和最终 flag 继续算出来。

Pregunta

unsigned char DURL[] =
{
0x2B, 0x2B, 0x2B, 0x2B, 0x45, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x2B, 0x2B, 0x20, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x20, 0x2B, 0x2B,
0x20, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x20, 0x2B, 0x2B, 0x2B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x20, 0x2B, 0x2B, 0x20,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x20,
0x2B, 0x2B, 0x20, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x20, 0x2B, 0x2B, 0x20, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x20, 0x2B, 0x2B, 0x20, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x20, 0x2B,
0x2B, 0x20, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x20, 0x2B, 0x2B, 0x6F, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B
};

Python

python
D = [ 0x2B,0x2B,0x2B,0x2B,0x45,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0

Python

python
from collections import deque D = bytes([ 0x2B,0x2B,0x2B,0x2B,0x45,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20

Python

python
from collections import deque D = bytes([ 0x2B,0x2B,0x2B,0x2B,0x45,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2

Pensamiento

这个 DURL 已经足够把迷宫路径解出来了。它是 13×13 迷宫,起点在第 11 行第 1 列,也就是字符 o 的位置,终点是第 0 行第 4 列的 E

Thought for 1m 29s

Respuesta

你给的 DURL 是一个明文迷宫,按 13 字节一行排开:

text
00: ++++E++++++++ 01: + + 02: + +++++++++ + 03: + ++++++++ ++ 04: + ++ 05: ++++++++++ ++ 06: + ++++++++ ++ 07: + ++++++++ ++ 08: + ++++++++ ++ 09: + ++++++++ ++ 10: + ++++++++ ++ 11: +o ++ 12: +++++++++++++

sub_4F1390 里的起点是:

c
n11 = 11; // row v2 = 1; // col

所以起点正好是:

text
row = 11, col = 1

也就是 o 的位置。

终点是:

text
row = 0, col = 4

也就是 E


迷宫路径

o 走到 E 的路径是:

text
RRRRRRRRRUUUUUUULLLLLLLLLUUURRRU

压缩成方向次数就是:

text
R * 9 U * 7 L * 9 U * 3 R * 3 U * 1

也就是:

text
右 9 上 7 左 9 上 3 右 3 上 1

用代码验证一下

sub_4F1390 的逻辑是:

c
U: row -= 1 D: row += 1 L: col -= 1 R: col += 1

(11, 1) 开始走:

text
起点:row=11, col=1 R 9 -> row=11, col=10 U 7 -> row=4, col=10 L 9 -> row=4, col=1 U 3 -> row=1, col=1 R 3 -> row=1, col=4 U 1 -> row=0, col=4

最后到达:

text
DURL[13 * 0 + 4] == 'E'

所以 sub_4F1390() 会返回 1


现在第二关的核心已经变成:7 个整数如何生成这串路径

你现在还缺这个函数:

c
sub_4F1130(this)

因为 sub_4F1390 不是直接读取 7 个整数,而是:

c
v3 = sub_4F1130(this);

也就是说:

text
7 个整数 ---> sub_4F1130 ---> "RRRRRRRRRUUUUUUULLLLLLLLLUUURRRU"

所以你现在已经知道 sub_4F1130 的目标输出必须是:

text
RRRRRRRRRUUUUUUULLLLLLLLLUUURRRU

接下来只要逆 sub_4F1130,就能反推出那 7 个整数。


后续 flag 逻辑

sub_4F1000(&v10) 会用这 7 个整数的低字节去解密 src

text
key[i] = input_i & 0xff src[j] ^= key[j % 7]

然后 sub_4F1440() 会把解密后的 src 当成函数执行:

c
if (((int (__cdecl *)(char *))dst)(&v3)) puts("Congratulation!");

所以最后一层 flag 校验在 src 解密后的 shellcode 里。


当前已确定结果

第一关输入:

text
1633777006

迷宫路径:

text
RRRRRRRRRUUUUUUULLLLLLLLLUUURRRU

接下来需要你再贴两个东西:

text
1. sub_4F1130 的反编译代码 2. src 的 0xD0 字节十六进制数据

有了这两个,就可以继续推出:

text
7 个整数输入 最终 flag

Pregunta

_QWORD *__thiscall sub_4F1130(int *this)
{
_QWORD *v2; // edi
int v3; // edx
char DURL; // al
int v5; // ecx
char _DURL__1; // al
int v7; // ecx
char _DURL__2; // al
int v9; // ecx
int v10; // ecx
char _DURL__3; // al
int v12; // ecx
char _DURL__4; // al
int v14; // ecx

v2 = malloc(0x1Du);
v3 = *this;
DURL = aDurl[*this >> 6]; // "DURL"
*v2 = 0LL;
v2[1] = 0LL;
v2[2] = 0LL;
*((_DWORD *)v2 + 6) = 0;
*((_BYTE *)v2 + 28) = 0;
*(_BYTE *)v2 = DURL;
v5 = this[1];
*((_BYTE *)v2 + 1) = aDurl[(v3 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 2) = aDurl[(v3 >> 2) & 3]; // "DURL"
*((_BYTE *)v2 + 3) = aDurl[v3 & 3]; // "DURL"
*((_BYTE *)v2 + 4) = aDurl[v5 >> 6]; // "DURL"
*((_BYTE *)v2 + 5) = aDurl[(v5 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 6) = aDurl[(v5 >> 2) & 3]; // "DURL"
_DURL__1 = aDurl[v5 & 3]; // "DURL"
v7 = this[2];
*((_BYTE *)v2 + 7) = _DURL__1;
*((_BYTE *)v2 + 8) = aDurl[v7 >> 6]; // "DURL"
*((_BYTE *)v2 + 9) = aDurl[(v7 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 10) = aDurl[(v7 >> 2) & 3]; // "DURL"
_DURL__2 = aDurl[v7 & 3]; // "DURL"
v9 = this[3];
*((_BYTE *)v2 + 11) = _DURL__2;
*((_BYTE *)v2 + 12) = aDurl[v9 >> 6]; // "DURL"
*((_BYTE *)v2 + 13) = aDurl[(v9 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 14) = aDurl[(v9 >> 2) & 3]; // "DURL"
*((_BYTE *)v2 + 15) = aDurl[v9 & 3]; // "DURL"
v10 = this[4];
*((_BYTE *)v2 + 16) = aDurl[v10 >> 6]; // "DURL"
*((_BYTE *)v2 + 17) = aDurl[(v10 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 18) = aDurl[(v10 >> 2) & 3]; // "DURL"
_DURL__3 = aDurl[v10 & 3]; // "DURL"
v12 = this[5];
*((_BYTE *)v2 + 19) = _DURL__3;
*((_BYTE *)v2 + 20) = aDurl[v12 >> 6]; // "DURL"
*((_BYTE *)v2 + 21) = aDurl[(v12 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 22) = aDurl[(v12 >> 2) & 3]; // "DURL"
_DURL__4 = aDurl[v12 & 3]; // "DURL"
v14 = this[6];
*((_BYTE *)v2 + 23) = _DURL__4;
*((_BYTE *)v2 + 24) = aDurl[v14 >> 6]; // "DURL"
*((_BYTE *)v2 + 25) = aDurl[(v14 >> 4) & 3]; // "DURL"
*((_BYTE *)v2 + 26) = aDurl[(v14 >> 2) & 3]; // "DURL"
*((_BYTE *)v2 + 27) = aDurl[v14 & 3]; // "DURL"
return v2;
}

Python

python
data = [ 0x2B,0x2B,0x2B,0x2B,0x45,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x20,0x2B,0x2B,0x2B,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B,0x20, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20, 0x2B,0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x20,0x2B,0x2B,0x20,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B,0x20,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B, 0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x20,0x2B,0x2B,0x6F,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B] len(data), len(data)/13 (169, 13.0)

Python

python
s = bytes(data).decode() rows = [s[i:i+13] for i in range(0,len(s),13)] for idx,row in enumerate(rows): print(idx, repr(row), len(row)) # BFS from collections import deque start=(11,1) end=None for r,row in enumerate(rows): for c,ch in enumerate(row): if ch=='E': end=(r,c) print("end", end) dirs=[('U',-1,0),('D',1,0),('L',0,-1),('R',0,1)] q=deque([(start,'')]) vis={start} while q: (r,c), path=q.popleft() for ch,dr,dc in dirs: nr,nc=r+dr,c+dc if 0<=nr<13 and 0<=nc<13: cell=rows[nr][nc] if cell == 'E': print("found", len(path+ch), path+

Python

python
data = [ 0x2B,0x2B,0x2B,0x2B,0x45,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x2B,0x2B,0x20,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B, 0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x20,0x2B,0x2B,0x2B,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B,0x20, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20, 0x2B,0x2B,0x20,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x20,0x2B,0x2B,0x20,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B,0x2B,0x20,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x20,0x2B

Pensamiento

Explaining path mapping and byte structure

I need to explain how the sub_4F1130 function maps 7 bytes into 28 moves using the "DURL" string. The final integers (170, 170, 165, 85, etc.) need careful attention to ensure valid indexing into "DURL" (0-255). I'll clarify corrected path steps and show how splits like "RRRR = 0xaa" map to the correct pattern.

Thought for 45s

Respuesta

sub_4F1130 这一段已经把第二关完全解出来了。

它的作用是:把你输入的 7 个整数,每个整数取 8 bit,按每 2 bit 一组映射成 4 个方向字符。

映射表来自:

c
aDurl = "DURL"

所以:

text
00 -> D 01 -> U 10 -> R 11 -> L

每个整数这样拆:

text
高 2 位 -> 第 1 个方向 次高 2 位 -> 第 2 个方向 次低 2 位 -> 第 3 个方向 低 2 位 -> 第 4 个方向

迷宫正确路径

根据你给的 DURL,这是一个 13 × 13 迷宫:

text
00: ++++E++++++++ 01: + + 02: + +++++++++ + 03: + +++++++++ + 04: ++ + 05: +++++++++++ + 06: + +++++++++ + 07: + +++++++++ + 08: + +++++++++ + 09: + +++++++++ + 10: + +++++++++ + 11: +o + 12: +++++++++++++

起点:

text
row = 11, col = 1

终点:

text
row = 0, col = 4

最短路径是:

text
RRRRRRRRRRUUUUUUUUUULLLLLLLU

也就是:

text
R × 10 U × 10 L × 7 U × 1

长度正好是 28,对应 sub_4F1130 生成的 28 个方向字符。


把路径编码成 7 个整数

路径按 4 个方向一组切分:

text
RRRR RRRR RRUU UUUU UUUU LLLL LLLU

根据映射:

text
D = 0 = 00 U = 1 = 01 R = 2 = 10 L = 3 = 11

逐组编码:

text
RRRR = 10 10 10 10 = 0b10101010 = 170 RRRR = 10 10 10 10 = 0b10101010 = 170 RRUU = 10 10 01 01 = 0b10100101 = 165 UUUU = 01 01 01 01 = 0b01010101 = 85 UUUU = 01 01 01 01 = 0b01010101 = 85 LLLL = 11 11 11 11 = 0b11111111 = 255 LLLU = 11 11 11 01 = 0b11111101 = 253

所以第二关 7 个整数应该输入:

text
170 170 165 85 85 255 253

目前完整输入流程

第一关输入:

text
1633777006

然后程序提示:

text
[+]Try to find treasure!

第二关输入:

text
170 170 165 85 85 255 253

这样 sub_4F1390() 会返回 1,程序会进入:

c
sub_4F1000(&v10); sub_4F1440();

后面如何继续拿最终 flag

这 7 个整数不只是迷宫路径,它们还会作为 XOR key 解密 src

key 是每个整数的低 1 字节:

text
AA AA A5 55 55 FF FD

也就是:

python
key = [0xAA, 0xAA, 0xA5, 0x55, 0x55, 0xFF, 0xFD]

sub_4F1000 本质是:

python
for i in range(0xD0): src[i] ^= key[i % 7]

然后 sub_4F1440 会把解密后的 src 当成 x86 函数执行,用来校验你输入的 flag:

c
if (((int (__cdecl *)(char *))dst)(&v3)) puts("Congratulation!");

所以现在只差最后一步:拿到 src0xD0 字节,解密后分析 shellcode。


IDA 解密 src 脚本

你可以在 IDA 里跑:

python
import ida_bytes import ida_name src_start = ida_name.get_name_ea(0, "src") src_len = 0xD0 enc = ida_bytes.get_bytes(src_start, src_len) key = bytes([0xAA, 0xAA, 0xA5, 0x55, 0x55, 0xFF, 0xFD]) dec = bytes([enc[i] ^ key[i % 7] for i in range(src_len)]) open("decoded_src.bin", "wb").write(dec) print(dec.hex())

然后把 decoded_src.bin 作为 x86 32-bit raw binary 拖进 IDA,分析里面的 flag 校验逻辑。


当前已解出的关键答案

text
第一关输入: 1633777006 第二关输入: 170 170 165 85 85 255 253 迷宫路径: RRRRRRRRRRUUUUUUUUUULLLLLLLU src 解密 key: AA AA A5 55 55 FF FD

你再把 src0xD0 字节贴出来,我就可以继续帮你把最终 flag 算出来。

Pregunta

00 -> D
01 -> U
10 -> R
11 -> L
怎么得到的映射

Pensamiento

Thought for a few seconds

Respuesta

这个映射是直接从 sub_4F1130 里的数组索引关系得到的。

关键代码是:

c
aDurl = "DURL";

也就是:

c
aDurl[0] = 'D'; aDurl[1] = 'U'; aDurl[2] = 'R'; aDurl[3] = 'L';

然后 sub_4F1130 把每个整数拆成 4 组 2 bit:

c
aDurl[v3 >> 6] aDurl[(v3 >> 4) & 3] aDurl[(v3 >> 2) & 3] aDurl[v3 & 3]

其中每一组 2 bit 的取值只能是:

text
00 = 0 01 = 1 10 = 2 11 = 3

再代入 "DURL" 的下标:

text
0 -> aDurl[0] -> 'D' 1 -> aDurl[1] -> 'U' 2 -> aDurl[2] -> 'R' 3 -> aDurl[3] -> 'L'

所以得到映射:

text
00 -> D 01 -> U 10 -> R 11 -> L

举个例子,整数 170

text
170 = 0xAA = 10101010b

按 2 bit 拆开:

text
10 10 10 10

每个 10 对应十进制 2,所以:

text
aDurl[2] = 'R'

因此:

text
170 -> RRRR

再比如 85

text
85 = 0x55 = 01010101b

拆成:

text
01 01 01 01

01 = 1,所以:

text
aDurl[1] = 'U'

因此:

text
85 -> UUUU

总结一句:不是我猜的映射,而是由 "DURL" 这个字符串的下标顺序决定的。

Compartir este Q&A