Skip to content

Instantly share code, notes, and snippets.

@satos---jp
Last active November 28, 2024 07:06
Show Gist options
  • Select an option

  • Save satos---jp/344acc58f542658cfca1de22f117cb2f to your computer and use it in GitHub Desktop.

Select an option

Save satos---jp/344acc58f542658cfca1de22f117cb2f to your computer and use it in GitHub Desktop.
SECCON2024予選 Reaction のwriteup

入出力に関する実験から、[0,3]{2}の出力に対してこちらから[0,3][0,12]の入力を与えてやることでプログラムが進行することがわかる。 バイナリ中の Environment::update 関数を解析すると、forループ中で2次元配列に対して操作を行っていることが伺えるので、2次元配列の内容をgdbを用いてダンプすると、

Breakpoint 2, 0x0000555555557c50 in Environment::update() ()
000000000000
000000000000
000000000000
000000000000
000000000000
000000000000
000000000000
000000000000
000000000000
030000000000
043000000000
041400000030
243402000040
413402000220

のような様子が見らる。遷移の様子から、このプログラムはゲーム『ぷよぷよ』をエミュレートしており、1度に16回以上の消去を行うことでフラグが出力されることがわかる。盤面の幅が12と広いことや連鎖ではなく同時消しも1消去とカウントされることを利用すると、単に階段積みを行い階段の上に乱雑にぷよを配置することで要求された16回以上の消去回数を満たすことができた。

最終的にanswer.binを入力として生成した。これはデバッグのために作成したGUI上(player.py)で表示すると以下のような積み方である。

Screenshot from 2024-11-24 03-59-20

>>> b = open('answer.bin','rb').read()
>>> b.decode()
'\x03\x00\x01\x03\x04\x01\x06\x03\x03\x03\x02\x00\x04\x02\x06\x00\x0b\x02\x03\x00\x04\x01\x05\x02\x04\x03\x00\x01\n\x00\x02\x00\x07\x00\x07\x02\x00\x02\x05\x01\x01\x00\x02\x03\x06\x02\x08\x03\n\x03\x01\x02\x02\x00\x03\x03\x07\x03\t\x03\t\x01\x05\x01\x05\x01\x07\x02\x08\x01\x05\x03\x04\x02\x03\x00\x00\x02\x00\x03\x00\x03\x08\x03\x0b\x00\x00\x03\x00\x01\x02\x00\x03\x03\x05\x01\n\x01\t\x00\x0b\x02\x07\x00\x08\x00\x08\x00\t\x02\x08\x00\x01\x00\x0b\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n'
def x(s,to_string=False):
return gdb.execute(s,to_string=to_string)
def parseXs(s):
res = []
s = s.split('\n')
def f(v):
v = v.strip()[2:]
return int(v,16)
for d in s:
if not ':' in d:
continue
d = d.split(':')[1].split(' ')
d = list(map(f,filter(lambda v: v.strip() != "",d)))
res += d
return res
x('b* 0x555555556674') # rensa check
x('r < gen_h')
print("\n" * 10 + "Current rensa")
x('x/xg $rsp+0x250')
x = 1 // 0
base = 0x55555554000
# x('b* 0x555555557c94') # loop inside
x('b* 0x555555557c50') # loop finished
# 0,2 :: 縦
# 1,3 :: 横
# 縦のとき :: 0から13まで
# 横のとき :: 0から12まで
"""
for d in range(4):
for p in range(12,15):
open('i','wb').write(bytes([p,d]))
x('r < i',to_string=True)
s = x('x/i $rip')
print(d,p,s)
exit()
"""
def gen_h(t):
d = random.randint(0,3)
x = random.randint(0,11 if d in [0,2] else 10)
# x = t % 12
return bytes([x,d])
import random
random.seed(1234)
open('ihs','wb').write(b''.join(gen_h(t) for t in range(1000)))
# open('i','wb').write(bytes(3 for _ in range(30)))
# x('r < ihs > opuyo')
# x('r < ihs')
x('r < gen_h')
def get_class():
b = x('x/4xg $rbx',to_string=True)
# print(b)
d = parseXs(b)
p3 = d[3]
# print(d)
pvs = parseXs(x('x/42xg 0x%x' % p3,to_string=True))
# print(pvs)
v3 = []
for p in pvs[::3]:
a = parseXs(x('x/12xw 0x%x' % p,to_string=True))
v3.append(a)
v3 = '\n'.join(''.join(map(str,v)) for v in v3[::-1])
print(v3)
for _ in range(30):
get_class()
x('c',to_string=True)
"""
for i in range(10):
open('i','wb').write(bytes([i,i]))
x('r < i',to_string=True)
s = x('x/i $rip')
print(i,s)
"""
import pygame
import sys
y = 0
H = 15
W = 14
bo = [[(10 if y == 0 or x == 0 or x == W - 1 else 0) for x in range(W)] for y in range(H)]
# puyos = [[(i)%4+1,(i+1)%4+1] for i in range(100)]
def load_v2(fn):
v = open(fn,'rb').read()
if b'Wrong' in v:
v = v.split(b'W')[0]
def tt(x):
return (x[0],x[1])
return [tt(v[i*2:][:2]) for i in range(len(v)//2)]
def main():
puyos = load_v2('opuyo')
puyos = load_v2('remote_o2')
puyos += [(0,0) for _ in range(1000)]
# ihs = load_v2('ihs')
# (cat save_3; python -c "print(b'\x00'*100)") | nc reaction.seccon.games 5000
ihs = load_v2('save_4')
ihs = [(x+1,d) for x,d in ihs] # XXX:!!
my_hands = []
pygame.init()
screen = pygame.display.set_mode((800, 800))
font = pygame.font.SysFont(None, 50)
cs = {
0: " ",
1: "R",
2: "G",
3: "B",
4: "Y",
10: "X"
}
cs = {k: font.render(s,True,
(255,0,0) if s == "R" else
(0,255,0) if s == "G" else
(0,0,255) if s == "B" else
(255,255,0) if s == "Y" else
(255,255,255)
) for k,s in cs.items()}
dx, dd = 1,0
ds = [(-1,0),(0,1),(1,0),(0,-1)]
def get_d2p():
ddy,ddx = ds[dd]
tx = dx
if ddx == -1:
tx += 1
return [
(H-2, tx),
(H-2+ddy,tx+ddx)
]
running = True
nps = puyos
dx,dd = ihs[0]
while running:
screen.fill((0,0,0))
for y in range(H):
for x in range(W):
screen.blit(cs[bo[y][x]], (x*30,(H-y+1)*30))
ofs = get_d2p()
screen.blit(cs[nps[0][0]], (ofs[0][1]*30,(H-ofs[0][0]+1)*30))
screen.blit(cs[nps[0][1]], (ofs[1][1]*30,(H-ofs[1][0]+1)*30))
for i in range(20):
for j in range(2):
screen.blit(cs[nps[1:][i][j]], (i*30,j*30+700))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
wh = [(x-1,d) for x,d in my_hands]
# XXX: !
open('gen_h','wb').write(b''.join(bytes(v) for v in wh))
print("Wrote")
if event.key == pygame.K_LEFT:
dx = max(dx-1,1)
elif event.key == pygame.K_RIGHT:
ddx = ds[dd][1]
dx = min(dx+1,W-2-(0 if ddx == 0 else 1))
elif event.key == pygame.K_UP:
ddx = ds[dd][1]
if ddx == 0 and dx == W-2:
continue
dd = (dd + 1) % 4
if event.key == pygame.K_DOWN:
np = nps[0]
nps = nps[1:]
my_hands.append((dx,dd))
ofs = get_d2p()
bo[ofs[0][0]][ofs[0][1]] = np[0]
bo[ofs[1][0]][ofs[1][1]] = np[1]
for x in range(W):
mo = True
while mo:
y = 0
mo = False
for y in range(H-1):
if bo[y][x] == 0 and bo[y+1][x] != 0:
bo[y][x] = bo[y+1][x]
bo[y+1][x] = 0
mo = True
ihs = ihs[1:]
if len(ihs) > 0:
dx,dd = ihs[0]
else:
dx,dd = 0,0
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment