Tags: stack-strings reverse
Rating:
## 129 stack strings 2
- Category: `rev`
- Value: `50`
- Solves: `172`
- Solved by me: `True`
- Local directory: `N/A`
### 题目描述
> Nothing to see here part 2
### 连接信息
- 无
### 内存布局
- 暂无可解析二进制 或 本题主要是非二进制方向
### WP
Flag verification passed!
Flag: ENO{W0W_D1D_1_JU5T_UNLUCK_4_N3W_SK1LL???}
Compared to the medium version, the hard version introduces three major increases in complexity:
1.Substitution (out-of-order access) — Instead of reading input[i] sequentially, the program uses the hash value hash1 as an index and reads input[hash1], effectively applying a permutation to the input
2.Accumulator mechanism — Introduces the bl register as a chained accumulator. Each comparison result is computed as
bl = rol8(prev, 1) + (hash3 ^ input[idx]),
making the current state depend on the previous round and forming a data dependency chain
3.Dual hash streams — Two different hash values are derived from the same edx register (with XOR constants 0xec8804a0 and 0x19e0463b). One is used to compute the input read index, and the other to compute the expected value
solve.py:
```
#!/usr/bin/env python3
import struct
with open("stackstrings_hard", "rb") as f:
binary = f.read()
# Buffer data at VA 0x20e0, size 0xfa
buf = bytearray(binary[0x20e0:0x20e0 + 0xfa])
# === Decrypt first prompt buf[0..0x40] ===
buf[0] = 0x3d
for i in range(16):
buf[1 + i] ^= binary[0x2010 + i]
buf[0x11 + i] ^= binary[0x2020 + i]
buf[0x21 + i] ^= binary[0x2030 + i]
buf[0x31 + i] ^= binary[0x2040 + i]
prompt1 = bytes(buf[0:0x41])
print(f"Prompt 1: {prompt1}")
# === Re-XOR buf[0..0x40] ===
for i in range(16):
buf[0 + i] ^= binary[0x2050 + i]
buf[0x10 + i] ^= binary[0x2060 + i]
buf[0x20 + i] ^= binary[0x2070 + i]
buf[0x30 + i] ^= binary[0x2080 + i]
buf[0x40] ^= 0x93
# === Decrypt second prompt buf[0x41..0x62] ===
for i in range(16):
buf[0x41 + i] ^= binary[0x2090 + i]
buf[0x51 + i] ^= binary[0x20a0 + i]
buf[0x61] ^= 0x34
buf[0x62] ^= 0xf7
prompt2 = bytes(buf[0x41:0x41 + 0x22])
print(f"Prompt 2: {prompt2}")
# === Re-XOR buf[0x41..0x62] ===
for i in range(16):
buf[0x41 + i] ^= binary[0x2090 + i]
buf[0x51 + i] ^= binary[0x20a0 + i]
buf[0x61] ^= 0x34
buf[0x62] ^= 0xf7
# === Extract keys ===
expected_len = buf[0xf4] ^ 0xa7
key_b1 = buf[0xf5] ^ 0x3a
key_b2 = buf[0xf6] ^ 0xd3
key_b3 = buf[0xf7] ^ 0x4b
key_b4 = buf[0xf8] ^ 0x13
r15 = (key_b4 << 24) | (key_b3 << 16) | (key_b2 << 8) | key_b1
r13_init = (buf[0xf9] ^ 0x77) & 0xff
print(f"Expected length: {expected_len}")
print(f"r15: 0x{r15:08x}")
print(f"r13_init: 0x{r13_init:02x}")
# Cipher tables
table_a2 = buf[0xa2:0xa2 + expected_len] # used to compute hash1 (index into input)
table_cb = buf[0xcb:0xcb + expected_len] # used to compute expected comparison value
def rol32(val, n):
n = n & 31
val &= 0xffffffff
return ((val << n) | (val >> (32 - n))) & 0xffffffff
def rol8(val, n):
n = n & 7
val &= 0xff
return ((val << n) | (val >> (8 - n))) & 0xff
# === Solve ===
# Loop state
edx = 0x9e3779b9
r10 = 0xa97288ed
r9 = 0
r13 = r13_init # accumulator carried across iterations
flag = [0] * 256
print("\nSolving each iteration:")
for i in range(expected_len):
bl = r13 & 0xff # bl = r13 from previous iteration
cl = i & 7
# --- hash1: compute index into input ---
esi = (edx ^ 0xec8804a0) & 0xffffffff
esi = rol32(esi, cl)
temp = ((esi >> 16) ^ esi) & 0xffffffff
hash1_low = ((temp >> 8) ^ temp) & 0xff
hash1_low ^= table_a2[i]
index = hash1_low
# --- hash2: compute expected comparison value ---
eax = (edx ^ 0x19e0463b) & 0xffffffff
eax = rol32(eax, cl)
temp = ((eax >> 16) ^ eax) & 0xffffffff
expected = ((temp >> 8) ^ temp) & 0xff
expected ^= table_cb[i]
# --- hash3: from r10 and r15 ---
esi3 = (r10 ^ r15) & 0xffffffff
cl3 = r9 & 7
esi3 = rol32(esi3, cl3)
temp3 = ((esi3 >> 15) ^ esi3) & 0xffffffff
hash3_low = ((temp3 >> 7) ^ temp3) & 0xff
# --- Equation ---
# bl_new = rol8(bl, 1) + (hash3_low ^ input[index])
# bl_new must equal expected
# => input[index] = hash3_low ^ ((expected - rol8(bl, 1)) & 0xff)
bl_rot = rol8(bl, 1)
diff = (expected - bl_rot) & 0xff
input_byte = hash3_low ^ diff
flag[index] = input_byte
r13 = input_byte # r13 for next iteration = current input byte
ch = chr(input_byte) if 32 <= input_byte < 127 else '?'
print(f" i={i:2d}: index={index:3d} (0x{index:02x}), byte=0x{input_byte:02x} '{ch}'")
# Update loop variables
r9 = (r9 + 3) & 0xffffffff
r10 = (r10 + 0x85ebca6b) & 0xffffffff
edx = (edx + 0x9e3779b9) & 0xffffffff
flag_str = bytes(flag[:expected_len])
print(f"\nFlag: {flag_str}")
# === Also decrypt messages ===
# Wrong message at buf[0x7f], 0x23 bytes
wrong_buf = bytearray(buf)
for i in range(16):
wrong_buf[0x7f + i] ^= binary[0x20b0 + i]
wrong_buf[0x8f + i] ^= binary[0x20c0 + i]
wrong_buf[0x9f] ^= 0x88
wrong_buf[0xa0] ^= 0x8e
wrong_buf[0xa1] ^= 0xe5
print(f"Wrong msg: {bytes(wrong_buf[0x7f:0x7f+0x23])}")
# Correct message at buf[0x63], 0x1c bytes
correct_buf = bytearray(buf)
for i in range(16):
correct_buf[0x63 + i] ^= binary[0x20d0 + i]
# Additional XOR loop for buf[0x73..0x7e] 12 bytes
eax_c = 0x81af1549
for j in range(12):
cl_c = (j + 0x10) & 7
esi_c = (eax_c ^ 0x76d51373) & 0xffffffff
esi_c = rol32(esi_c, cl_c)
temp_c = ((esi_c >> 16) ^ esi_c) & 0xffffffff
key_c = ((temp_c >> 8) ^ temp_c) & 0xff
correct_buf[0x73 + j] ^= key_c
eax_c = (eax_c + 0x9e3779b9) & 0xffffffff
print(f"Correct msg: {bytes(correct_buf[0x63:0x63+0x1c])}")
```
# pwn
### Exploit
- Exploit 代码未在本地标准 `solution/` 目录找到,可能嵌在外部 WP 文本中。
- 已在上方 WP 小节插入相关文本来源,可继续抽取为独立脚本。
---