multiarch-1 - GoogleCTF 2025
#GoogleCTF_2025 #VM
レジスタマシンとスタックマシン両方の命令を持つVM
gdbスクリプトでトレースをとった
code:python
# gdx -q -x dump.py
import gdb
ops = {
0x1cda: 'stack_add',
0x1d24: 'stack_sub',
0x1d6e: 'stack_xor',
0x1db8: 'stack_and',
0x1e3a: 'stack_cmp',
0x1f63: 'stack_srand',
0x1f7a: 'stack_rand',
0x2431: 'reg_srand',
0x2448: 'reg_rand',
0x2558: 'reg_add1',
0x25c0: 'reg_add2',
0x260a: 'reg_sub1',
0x268c: 'reg_sub2',
0x26ce: 'reg_xor1',
0x273e: 'reg_xor2',
0x2784: 'reg_mul1',
0x27f8: 'reg_mul2',
0x232a: 'reg_cmp1',
0x2367: 'reg_cmp2',
}
gdb.execute('file multiarch')
gdb.execute('main')
for addr, _ in ops.items():
gdb.execute(f'brva {addr:#x}')
with open('input', 'wb') as f:
f.write(b'2405061754\n')
f.write(int.to_bytes(0x507b2f7b, 4, 'little') + int.to_bytes(0x507b1f01, 4, 'little') + b'\n')
f.write(b'2688102\n')
gdb.execute('r ./crackme.masm < input')
logs = []
try:
while True:
gdb.execute('c')
pc = int(gdb.parse_and_eval('$rip'))
for addr, op in ops.items():
if pc & 0x1ff == addr & 0x1ff:
lhs, rhs = None, None
if op.startswith('stack_'):
if 'srand' in op:
lhs = int(gdb.parse_and_eval('$rdi')) & 0xffffffff
rhs = 0
elif 'rand' in op:
lhs = int(gdb.parse_and_eval('*(int *)($rsp+0x38-0x2c)')) & 0xffffffff
rhs = 0
else:
lhs = int(gdb.parse_and_eval('*(int *)($rsp+0x38-0x30)')) & 0xffffffff
rhs = int(gdb.parse_and_eval('*(int *)($rsp+0x38-0x2c)')) & 0xffffffff
elif 'add' in op or 'sub' in op or 'xor2' in op:
lhs = int(gdb.parse_and_eval('*(int *)($rbx+$rax*4+0x3b)')) & 0xffffffff
rhs = int(gdb.parse_and_eval('$edx')) & 0xffffffff
elif 'xor1' in op:
lhs = int(gdb.parse_and_eval('*(int *)($rbx+$rdx*4+0x3b)')) & 0xffffffff
rhs = int(gdb.parse_and_eval('$eax')) & 0xffffffff
elif 'mul' in op:
lhs = int(gdb.parse_and_eval('$eax')) & 0xffffffff
rhs = int(gdb.parse_and_eval('$edx')) & 0xffffffff
elif 'cmp' in op:
lhs = int(gdb.parse_and_eval('$esi')) & 0xffffffff
rhs = int(gdb.parse_and_eval('$edx')) & 0xffffffff
elif 'srand' in op:
lhs = int(gdb.parse_and_eval('$rdi')) & 0xffffffff
rhs = 0
elif 'rand' in op:
lhs = int(gdb.parse_and_eval('*(int *)($rsp+0x58-0x44)')) & 0xffffffff
rhs = 0
logs.append(f'op: {op}, lhs: {lhs:#x}, rhs: {rhs:#x}')
break
except:
for log in logs:
print(log)
code:txt
op: stack_add, lhs: 0x1b505630, rhs: 0x8f5a547a
op: stack_cmp, lhs: 0xaaaaaaaa, rhs: 0xaaaaaaaa
op: reg_add1, lhs: 0x8ee0, rhs: 0x20
op: reg_mul2, lhs: 0x507b2f7b, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x0, rhs: 0x3fd14865
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8ee0
op: reg_add2, lhs: 0x8ee0, rhs: 0x4
op: reg_mul2, lhs: 0x507b1f01, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x3fd14865, rhs: 0x3fd13b54
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8ee4
op: reg_add2, lhs: 0x8ee4, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8ee8
op: reg_add2, lhs: 0x8ee8, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8eec
op: reg_add2, lhs: 0x8eec, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8ef0
op: reg_add2, lhs: 0x8ef0, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8ef4
op: reg_add2, lhs: 0x8ef4, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8ef8
op: reg_add2, lhs: 0x8ef8, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8efc
op: reg_add2, lhs: 0x8efc, rhs: 0x4
op: reg_mul2, lhs: 0x0, rhs: 0xcafebabe
op: reg_xor1, lhs: 0x7331, rhs: 0x0
op: reg_cmp1, lhs: 0x8f00, rhs: 0x8f00
op: reg_cmp2, lhs: 0x7331, rhs: 0x7331
op: reg_srand, lhs: 0x290466, rhs: 0x0
op: stack_rand, lhs: 0xbf66d898, rhs: 0x0
op: reg_xor1, lhs: 0x133700, rhs: 0xbf66d898
op: stack_xor, lhs: 0xf2f2f2f2, rhs: 0xbf75ef98
op: stack_and, lhs: 0x4d871d6a, rhs: 0xffffff
op: stack_cmp, lhs: 0xc0ffee, rhs: 0x871d6a
op: reg_add2, lhs: 0x0, rhs: 0x1
op: reg_cmp2, lhs: 0x1, rhs: 0xa
op: stack_rand, lhs: 0xe608057b, rhs: 0x0
op: reg_xor1, lhs: 0x133700, rhs: 0xe608057b
op: stack_xor, lhs: 0xf2f2f2f2, rhs: 0xe61b327b
op: stack_and, lhs: 0x14e9c089, rhs: 0xffffff
op: stack_cmp, lhs: 0xc0ffee, rhs: 0xe9c089
op: reg_add2, lhs: 0x1, rhs: 0x1
op: reg_cmp2, lhs: 0x2, rhs: 0xa
op: stack_rand, lhs: 0x5a611da6, rhs: 0x0
op: reg_xor1, lhs: 0x133700, rhs: 0x5a611da6
op: stack_xor, lhs: 0xf2f2f2f2, rhs: 0x5a722aa6
op: stack_and, lhs: 0xa880d854, rhs: 0xffffff
op: stack_cmp, lhs: 0xc0ffee, rhs: 0x80d854
op: reg_add2, lhs: 0x2, rhs: 0x1
op: reg_cmp2, lhs: 0x3, rhs: 0xa
op: stack_rand, lhs: 0x73213a1c, rhs: 0x0
op: reg_xor1, lhs: 0x133700, rhs: 0x73213a1c
op: stack_xor, lhs: 0xf2f2f2f2, rhs: 0x73320d1c
op: stack_and, lhs: 0x81c0ffee, rhs: 0xffffff
op: stack_cmp, lhs: 0xc0ffee, rhs: 0xc0ffee
1問目は0x1b505630を足して0xaaaaaaaaになる数→0x8f5a547a
2問目は0xcafebabeと64bit乗算→XORを繰り返して0x7331と比較
以下のコードで求めた
code:python
from z3 import *
N = 2
x = BitVec(f'x{i}', 64) for i in range(N)
s = Solver()
for i in range(len(x)):
s.add(ULE(xi, 0xffffffff))
s.add(xi & 0x80808080 == 0)
v = 0
for i in range(len(x)):
v ^= LShR(xi * 0xcafebabe, 32)
s.add(v == 0x7331)
assert s.check() == sat
m = s.model()
for i in range(len(x)):
print(f'x = {m[xi].as_long():#x}')
3問目は10回の試行で(f(rand()) ^ 0xf2e1c5f2) & 0xffffff) == 0xc0ffeeになるseed
以下のコードで求めた
code:python
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
int gen_rand() {
return (rand() & 0xffff) | (rand() << 16);
}
int f(int x) {
int v1 = x ^ 0x133700;
int v2 = v1 ^ 0xf2f2f2f2;
int v3 = v2 & 0xffffff;
return v3;
}
int main() {
int i = 0;
while (true) {
srand(i);
for (int j = 0; j < 10; j++) {
if (f(gen_rand()) == 0xc0ffee) {
printf("%d\n", i);
srand(i);
for (int j = 0; j < 10; j++) {
int r = gen_rand();
printf("%x, %x\n", r, f(r));
}
return 0;
}
}
i++;
}
}