[Dreamhack] Times

pandas·2024년 11월 5일
0

Dreamhack

목록 보기
12/14

00. 문제 파일



래퍼런스도 준다

01. 바이너리 분석


분석을 굉장히 하게 싫게 생겼다

__int64 __fastcall sub_174A(unsigned int a1)
{
  return (unsigned int)__ROL4__(
                         (((16
                          * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF,
                         16);
}

무서운 함수도 있다

일단 실행시키면 시간이 안된다

컴퓨터 시간을 2030년으로 바꾸고 하면 실행은 되다? 안되나 하는데 플래그는 안나온다

cf. 이 문제에는 낚시가 많다

Fishing 0x01. Reference

ptrace에 대한 레퍼런스가 주어진다
영어로 된 어려워보이는 문헌이지만, 쓸모 없다
읽느라 시간 낭비할 필요가 없다

Fishing 0x02. ptrace

왜일까? 일단 ptrace는 동적 분석을 하지 못하도록 해주는 함수?이다
동적분석을 막아놓았다는 소리이다
그러면 어떻게 할까?
동적분석을 안하고 정적분석을 하면 된다

Fishing 0x03. rand() and XOR


여기서 랜덤값 2개를 구하고 xor을 해준다
뭔가 구조적으로는 비슷한데, random의 값만 바뀌는 것이다
xor의 중요한 특징 중에는

xor두번하면 원래 값이 나온다

그럼 다시 보면 여기서 차이점은 random값의 차이이다

random값은 time을 기준으로 seed를 생성하고, 동일한 seed에는 동일한 random값이 나온다

비슷한 pwnable문제도 많다 (cat jump, randerer, Rock Paper Scissors등등)

의문점은

"여기서 seed가 바뀔까?"

이다
seed가 바뀌려면 프로그램 코드 실행 후 1초의 시간이 흘러야 되는데 아무리 오래 걸려도 그렇지 않다

정리하면

동일한 random값으로 두번 xor연산을 하기에
"XOR 연산들은 쓸모가 없다"
이다

다시 바이너리 분석으로


이 부분만 확인해주면 된다

sub_174A가 무섭게 생긴 함수이고
unk_4020의 값은

 [0x66, 0x0C, 0x4C, 0x86, 0xA6, 0x2C, 0x1C, 0x9C, 0x1C, 0x66, 
  0x1C, 0x2C, 0x9C, 0x6C, 0xA6, 0xCC, 0xA6, 0x6C, 0x6C, 0xAC, 
  0xA6, 0xA6, 0x86, 0x4C, 0x2C, 0x46, 0xEC, 0x8C, 0xEC, 0x46, 
  0x8C, 0x9C, 0x4C, 0xEC, 0xC6, 0x66, 0x4C, 0x46, 0x86, 0x4C]

이다

sub_174A를 이쁘게 python으로 바꾸면 (with. GPT)

def sub_174A(a1):
    t1 = (2*a1) & 0xAAAAAAAA | (a1>>1) & 0x55555555
    t2 = (4 * t1) & 0xCCCCCCCC | (t1 >> 2) & 0x33333333
    t3 = (16 * t2) & 0xF0F0F0F0 | (t2 >> 4) & 0x0F0F0F0F
    t4 = ((t3 << 8) & 0xFF00FF00) | ((t3 >> 8) & 0x00FF00FF)
    result = rol(t4, 16)
    return result

이렇게 된다

참고로 python에서 ror, rol연산이 정의가 안되어 있기에,

def rol(data, shift, size=32):
    shift %= size
    remains = data >> (size - shift)
    body = (data << shift) - (remains << size )
    return (body + remains)
    

def ror(data, shift, size=32):
    shift %= size
    body = data >> shift
    remains = (data << (size - shift)) - (body << size)
    return (body + remains)

정의를 해줘야 한다

거꾸로 역연산 하는 코드는 GPT와 함께 써보면

def inverse_sub_174A(result):
    t4 = ror(result, 16)
    t3 = ((t4 & 0xFF00FF00) >> 8) | ((t4 & 0x00FF00FF) << 8)
    t2 = ((t3 & 0xF0F0F0F0) >> 4) | ((t3 & 0x0F0F0F0F) << 4)
    t1 = ((t2 & 0xCCCCCCCC) >> 2) | ((t2 & 0x33333333) << 2)
    a1 = ((t1 & 0xAAAAAAAA) >> 1) | ((t1 & 0x55555555) << 1)
    return a1

이렇게 된다

그러면 unk_4020inverse_sub_174A에 돌려보면

0x66303261
0x65343839
0x38663834
0x39366533
0x65363635
0x65656132
0x34623731
0x37623139
0x32376366
0x32626132

이렇게 나온다
이걸 두개씩 끊어서 list로 만들면

arr = [0x66, 0x30, 0x32, 0x61, 0x65, 0x34, 0x38, 0x39, 0x38, 0x66, 0x38, 0x34, 0x39, 0x36, 0x65, 0x33, 0x65, 0x36, 0x36, 0x35, 0x65, 0x65, 0x61, 0x32, 0x34, 0x62, 0x37, 0x31, 0x37, 0x62, 0x31, 0x39, 0x32, 0x37, 0x63, 0x66, 0x32, 0x62, 0x61, 0x32]

이렇게 되고, 이를 문자열로 바꾸면

for i in arr:
    print(chr(i), end='')


이렇게 나온다

DH{f02ae4898f8496e3e665eea24b717b1927cf2ba2}

로 제출하면
안된다

이유는


여기에 있다
끊어서 넣어준다

그래서 답을 4글자씩 그룹을 만들고, 그 그룹을 뒤집어서 합쳐주면 된다

txt = "f02ae4898f8496e3e665eea24b717b1927cf2ba2"

# f02a
# e489
# 8f84
# 96e3
# e665
# eea2
# 4b71
# 7b19
# 27cf
# 2ba2

# a20f 984e 48f8 3e69 566e 2aee 17b4 91b7 fc72 2ab2

마지막은 손으로 했다

02. Exploit

def rol(data, shift, size=32):
    shift %= size
    remains = data >> (size - shift)
    body = (data << shift) - (remains << size )
    return (body + remains)
    

def ror(data, shift, size=32):
    shift %= size
    body = data >> shift
    remains = (data << (size - shift)) - (body << size)
    return (body + remains)

def sub_174A(a1):
    t1 = (2*a1) & 0xAAAAAAAA | (a1>>1) & 0x55555555
    t2 = (4 * t1) & 0xCCCCCCCC | (t1 >> 2) & 0x33333333
    t3 = (16 * t2) & 0xF0F0F0F0 | (t2 >> 4) & 0x0F0F0F0F
    t4 = ((t3 << 8) & 0xFF00FF00) | ((t3 >> 8) & 0x00FF00FF)
    result = rol(t4, 16)
    return result

def inverse_sub_174A(result):
    t4 = ror(result, 16)
    t3 = ((t4 & 0xFF00FF00) >> 8) | ((t4 & 0x00FF00FF) << 8)
    t2 = ((t3 & 0xF0F0F0F0) >> 4) | ((t3 & 0x0F0F0F0F) << 4)
    t1 = ((t2 & 0xCCCCCCCC) >> 2) | ((t2 & 0x33333333) << 2)
    a1 = ((t1 & 0xAAAAAAAA) >> 1) | ((t1 & 0x55555555) << 1)
    return a1

arr = [0x66, 0x30, 0x32, 0x61, 0x65, 0x34, 0x38, 0x39, 0x38, 0x66, 0x38, 0x34, 0x39, 0x36, 0x65, 0x33, 0x65, 0x36, 0x36, 0x35, 0x65, 0x65, 0x61, 0x32, 0x34, 0x62, 0x37, 0x31, 0x37, 0x62, 0x31, 0x39, 0x32, 0x37, 0x63, 0x66, 0x32, 0x62, 0x61, 0x32]

for i in arr:
    print(chr(i), end='')

txt = "f02ae4898f8496e3e665eea24b717b1927cf2ba2"

# cnt = 0
# temp = ""

# for i in txt:
#     if cnt % 5 ==0:
#         #print(temp, end=' ')
#         print("".join(reversed(temp)), end=' ')
#         temp = ""
#     else:
#         temp += i
#     cnt+=1

# f02a
# e489
# 8f84
# 96e3
# e665
# eea2
# 4b71
# 7b19
# 27cf
# 2ba2

# a20f 984e 48f8 3e69 566e 2aee 17b4 91b7 fc72 2ab2

03. Review

ptrace를 별생각 안하고 늘 정적분석만해서 그런지 낚시에 안 낚였다
xor이 필요없는건 신박한 구현이였다
sub_174A함수가 이해하기 어려웠고, GPT없이는 못 풀었을 것 같다
리버싱에서 역연산이나 &연산, >>, <<비트 쉬프트 연산과 ror, rol연산을 잘 안써보다보니 어려웠고, 많이 배운 문제였다

삽질을 했다면 오히려 안티 리버싱에 대해서 배울 수 있었을텐데,, 많은것을 배운 문제이다

profile
KDMHS 23 WP

2개의 댓글

comment-user-thumbnail
2025년 1월 9일

inverse_sub_174A 함수에서 나온 결과를 2개씩 끊지 마시고 .to_bytes(4, "little")을 이용하여 출력하면 좋아요~

1개의 답글

관련 채용 정보