main
함수
윗 부분은 배열의 값이다 (STAGE별 이름)
총 10번, 즉 10단계까지 있다
ptr
값을 가져온다
ptr
을 이리 저리 변환시켜서 출력한다
sub_138D
를 분석하자
이런 모양새로 출력이 된다
처음에는 이게 뭔지 몰랐다
HIWORD, (unsigend __int8), BYTE1, BYTE2...
이 뜻하는 것이 무엇인지 아는 것이 중요했다
HIWORD
: 값의 상위 1바이트
unsigned __int8
: 값의 하위 1바이트
BYTE1
: 가장 낮은 비트 옆 1바이트
...
예시 :
0x11223344
unsigned __int8 == LOBYTE
: 0x44
BYTE1
: 0x33
BYTE2
: 0x22
BYTE3
: 0x11
HIBYTE
: 0x11
출력된 결과를 잘 조합해서 ptr
을 조합해주면 된다
if
가 1
이면 실패하고, 0
이면 성공한다
0
으로 만들기 위해서는 sub_1407(s, ptr)
이 1
이여야 한다
s
는 user_input
이다
sub_1407
뭔소린지 몰라서 python
으로 정리해봤다
v7
이 a2
와 같아야 한다
a2
는 ptr
이다
목표 :
v7
==a2(ptr)
이 코드가 하는 일은 단순하다
A
: v7에 1을 더한다
B
: v7에 2를 곱한다
나머지
: ERROR
이 두 연산을 사용해서 v7 = 0
에서 ptr
까지 만들면 된다
int
형 숫자를 입력하고 이를 A,B
연산으로 역연산 하는 함수를 만들었다
def reverse(n):
result = ""
while n != 0:
if n % 2 == 0:
result += "B"
n //= 2
else:
result += "A"
n -= 1
return "".join([i for i in reversed(result)])
print(reverse(int(input())))
그냥 이건 PS다
이 문제는 reversing
이지만 pwntools
를 사용해야 된다
ptr
을 조합하기 위해서 이 숫자들을 입력받아야 한다
p.recvuntil(b"HP:")
hp = int(p.recvuntil(b",")[:-1])
print(f"HP : {hp} / {hex(hp)}")
p.recvuntil(b"STR:")
dungeon_str = int(p.recvuntil(b",")[:-1])
print(f"STR : {dungeon_str} / {hex(dungeon_str)}")
p.recvuntil(b"AGI:")
agi = int(p.recvuntil(b",")[:-1])
print(f"AGI : {agi} / {hex(agi)}")
p.recvuntil(b"VIT:")
vit = int(p.recvuntil(b",")[:-1])
print(f"VIT : {vit} / {hex(vit)}")
p.recvuntil(b"INT:")
dungeon_int = int(p.recvuntil(b",")[:-1])
print(f"INT : {dungeon_int} / {hex(dungeon_int)}")
p.recvuntil(b"END:")
end = int(p.recvuntil(b",")[:-1])
print(f"END : {end} / {hex(end)}")
p.recvuntil(b"DEX:")
dex = int(p.recvuntil(b"\n")[:-1])
print(f"DEX : {dex} / {hex(dex)}")
그럼 이들을 조합해야 한다
아까의 HIBYTE, LOBYTE, BYTE1, BYTE2...
개념을 활용하야 이를 비트 쉬프트 연산으로 조합하면 된다
ptr = ((hp<<48) | (dex<<40) | (end<<32) | (dungeon_int<<24) | (vit<<16) | (agi<<8) | dungeon_str)
ptr
까지 구했다!
payload = reverse(ptr)
p.sendlineafter(b": ", payload)
reverse(ptr)
을 구해서 pwntool
로 전송해주면 된다
from pwn import *
p = remote("host3.dreamhack.games", [PORT])
def reverse(n):
result = ""
while n != 0:
if n % 2 == 0:
result += "B"
n //= 2
else:
result += "A"
n -= 1
return "".join([i for i in reversed(result)])
def check(result):
num = 0
for i in result:
if i == "A":
num += 1
elif i == "B":
num *= 2
#print(i, num)
return num
for i in range(10):
p.recvuntil(b"HP:")
hp = int(p.recvuntil(b",")[:-1])
print(f"HP : {hp} / {hex(hp)}")
p.recvuntil(b"STR:")
dungeon_str = int(p.recvuntil(b",")[:-1])
print(f"STR : {dungeon_str} / {hex(dungeon_str)}")
p.recvuntil(b"AGI:")
agi = int(p.recvuntil(b",")[:-1])
print(f"AGI : {agi} / {hex(agi)}")
p.recvuntil(b"VIT:")
vit = int(p.recvuntil(b",")[:-1])
print(f"VIT : {vit} / {hex(vit)}")
p.recvuntil(b"INT:")
dungeon_int = int(p.recvuntil(b",")[:-1])
print(f"INT : {dungeon_int} / {hex(dungeon_int)}")
p.recvuntil(b"END:")
end = int(p.recvuntil(b",")[:-1])
print(f"END : {end} / {hex(end)}")
p.recvuntil(b"DEX:")
dex = int(p.recvuntil(b"\n")[:-1])
print(f"DEX : {dex} / {hex(dex)}")
ptr = ((hp<<48) | (dex<<40) | (end<<32) | (dungeon_int<<24) | (vit<<16) | (agi<<8) | dungeon_str)
payload = reverse(ptr)
p.sendlineafter(b": ", payload)
p.interactive()
냠냠
분석이 그렇게 어렵지는 않았다...?
ptr
을 쪼개놓고 다시 비트 쉬프트 연산
으로 합치는 것이 어려웠다
reverse()
함수를 짜는게 PS
같아서 좋았다
시험기간이 아니였다면 이틀정도? 갈아넣으면 풀 수 있었을 거 같은데 시험기간이라 오래 걸린점이 아쉬웠다
BYTE 매크로
를 무시하고 ptr
을 구하려다가 시간을 너무 날렸다
리버싱에서 pwntool
을 써서 새로웠다
포너블하면서 자주 썼더니 여기서 막히지는 않았다
문제가 재밋었다
맛도리