rev-basic-9
_BOOL8 __fastcall sub_140001000(const char *buf)
{
int i; // [rsp+20h] [rbp-18h]
int len; // [rsp+24h] [rbp-14h]
len = strlen(buf);
if ( (len + 1) % 8 )
return 0i64;
for ( i = 0; i < len + 1; i += 8 )
sub_1400010A0(&buf[i]);
return memcmp(buf, &unk_140004000, 0x19ui64) == 0;
}
- buf 길이가 8의 배수여야 한다.
- buf를 8자리씩
sub_1400010A0()
로 검증한다. sub_1400010A0()
이 실행된 후,buf[i] == &unk_140004000
조건이 충족해야 한다.
sub_1400010A0()
가 실행된 후의 buf가 [그림]의 데이터와 일치하면 된다.
다음으로 sub_1400010A0()
를 분석해보자.
__int64 __fastcall sub_1400010A0(unsigned __int8 *buf)
{
__int64 result; // rax
unsigned __int8 v2; // [rsp+0h] [rbp-48h]
int j; // [rsp+4h] [rbp-44h]
int i; // [rsp+8h] [rbp-40h]
char key[16]; // [rsp+10h] [rbp-38h] BYREF
strcpy(key, "I_am_KEY");
result = *buf;
v2 = *buf;
for ( i = 0; i < 16; ++i )
{
for ( j = 0; j < 8; ++j )
{
v2 = __ROR1__(buf[((_BYTE)j + 1) & 7] + byte_140004020[(unsigned __int8)key[j] ^ v2], 5);
buf[((_BYTE)j + 1) & 7] = v2;
}
result = (unsigned int)(i + 1);
}
return result;
}
16번의 반복문을 실행하고 이중 for문에서 key, buf[], byte_140004020[]
를 이용해서 buf[j]
를 변환한다.
- 즉, 각각의
buf[j]
에 대한 변환을 16번 진행한다고 생각하면 된다.
목표는 변환 이전의 원본 buf[]
를 구하는 것이다.
그렇다면, 역연산을 수행해야 한다. 이중 for문의 ROR 연산을 수행하는 코드를 역연산 할 수 있도록 수식을 변경하는 것이 핵심이다.
1)
ROR(buf[((_BYTE)j + 1) & 7] + byte_140004020[(unsigned __int8)key[j] ^ v2], 5) == buf[((_BYTE)j + 1) & 7]
2)
ROL(buf[((_BYTE)j + 1) & 7], 5) == buf[((_BYTE)j + 1) & 7] + byte_140004020[(unsigned __int8)key[j] ^ v2]
3)
buf[((_BYTE)j + 1) & 7] == ROL(buf[((_BYTE)j + 1) & 7], 5) - byte_140004020[(unsigned __int8)key[j] ^ v2]
위 수식은 역연산을 수행하기 위해 1) → 3)의 순서로 수식을 변환한 과정이다.
ROR 연산을 ROL 연산으로 변환했다.
ROR → ROL 로 변환한 이유는 역연산을 수행하기 위해서다.
이중 for문 코드를 역연산으로 표현하면 다음과 같이 변경할 수 있다.
// 원본코드
v2 = __ROR1__(buf[((_BYTE)j + 1) & 7] + byte_140004020[(unsigned __int8)key[j] ^ v2], 5);
buf[((_BYTE)j + 1) & 7] = v2;
// 역연산
v2 = buf[((_BYTE)j + 1) & 7]
buf[((_BYTE)j + 1) & 7] == ROL(buf[((_BYTE)j + 1) & 7], 5) - byte_140004020[(unsigned __int8)key[j] ^ v2]
본격적인 역연산을 수행해보자.
1번째 줄은 처음에 들어가는 v2 == unk_140004000[j]
이므로 위에서 확인한 값을 이용해서 연산하면된다.
2번째 줄은 byte_140004020(), key, ROL()
을 사용한다.
strcpy(key, "I_am_KEY");
key =="I_am_KEY"
이다.
byte_140004020[]
의 인덱스는
(unsigned __int8)key[j] ^ v2
와 같이 구할 수 있다. (주의할 점은 key[]
데이터는 문자이므로 uint8로 형변환해야한다.)
.data:0000000140004020 ; _BYTE byte_140004020[256]
.data:0000000140004020 byte_140004020 db 63h, 7Ch, 77h, 7Bh, 0F2h, 6Bh, 6Fh, 0C5h, 30h, 1, 67h
.data:0000000140004020 ; DATA XREF: sub_1400010A0+90↑o
.data:0000000140004020 db 2Bh, 0FEh, 0D7h, 0ABh, 76h, 0CAh, 82h, 0C9h, 7Dh, 0FAh
.data:0000000140004020 db 59h, 47h, 0F0h, 0ADh, 0D4h, 0A2h, 0AFh, 9Ch, 0A4h, 72h
.data:0000000140004020 db 0C0h, 0B7h, 0FDh, 93h, 26h, 36h, 3Fh, 0F7h, 0CCh, 34h
.data:0000000140004020 db 0A5h, 0E5h, 0F1h, 71h, 0D8h, 31h, 15h, 4, 0C7h, 23h
.data:0000000140004020 db 0C3h, 18h, 96h, 5, 9Ah, 7, 12h, 80h, 0E2h, 0EBh, 27h
.data:0000000140004020 db 0B2h, 75h, 9, 83h, 2Ch, 1Ah, 1Bh, 6Eh, 5Ah, 0A0h, 52h
.data:0000000140004020 db 3Bh, 0D6h, 0B3h, 29h, 0E3h, 2Fh, 84h, 53h, 0D1h, 0
.data:0000000140004020 db 0EDh, 20h, 0FCh, 0B1h, 5Bh, 6Ah, 0CBh, 0BEh, 39h, 4Ah
.data:0000000140004020 db 4Ch, 58h, 0CFh, 0D0h, 0EFh, 0AAh, 0FBh, 43h, 4Dh, 33h
.data:0000000140004020 db 85h, 45h, 0F9h, 2, 7Fh, 50h, 3Ch, 9Fh, 0A8h, 51h, 0A3h
.data:0000000140004020 db 40h, 8Fh, 92h, 9Dh, 38h, 0F5h, 0BCh, 0B6h, 0DAh, 21h
.data:0000000140004020 db 10h, 0FFh, 0F3h, 0D2h, 0CDh, 0Ch, 13h, 0ECh, 5Fh, 97h
.data:0000000140004020 db 44h, 17h, 0C4h, 0A7h, 7Eh, 3Dh, 64h, 5Dh, 19h, 73h
.data:0000000140004020 db 60h, 81h, 4Fh, 0DCh, 22h, 2Ah, 90h, 88h, 46h, 0EEh
.data:0000000140004020 db 0B8h, 14h, 0DEh, 5Eh, 0Bh, 0DBh, 0E0h, 32h, 3Ah, 0Ah
.data:0000000140004020 db 49h, 6, 24h, 5Ch, 0C2h, 0D3h, 0ACh, 62h, 91h, 95h, 0E4h
.data:0000000140004020 db 79h, 0E7h, 0C8h, 37h, 6Dh, 8Dh, 0D5h, 4Eh, 0A9h, 6Ch
.data:0000000140004020 db 56h, 0F4h, 0EAh, 65h, 7Ah, 0AEh, 8, 0BAh, 78h, 25h
.data:0000000140004020 db 2Eh, 1Ch, 0A6h, 0B4h, 0C6h, 0E8h, 0DDh, 74h, 1Fh, 4Bh
.data:0000000140004020 db 0BDh, 8Bh, 8Ah, 70h, 3Eh, 0B5h, 66h, 48h, 3, 0F6h, 0Eh
.data:0000000140004020 db 61h, 35h, 57h, 0B9h, 86h, 0C1h, 1Dh, 9Eh, 0E1h, 0F8h
.data:0000000140004020 db 98h, 11h, 69h, 0D9h, 8Eh, 94h, 9Bh, 1Eh, 87h, 0E9h
.data:0000000140004020 db 0CEh, 55h, 28h, 0DFh, 8Ch, 0A1h, 89h, 0Dh, 0BFh, 0E6h
.data:0000000140004020 db 42h, 68h, 41h, 99h, 2Dh, 0Fh, 0B0h, 54h, 0BBh, 16h
byte_140004020[]
을 보자.
많은 데이터를 담고 있는 배열이다. 데이터를 파싱하는 작업도 필요하다.
def ROL(n, m):
shift = n << m
src = n >> (8 - m)
src &= 255
return shift | src
다음은 ROL 함수다.
위 코드를 이용해서 ROL 연산을 수행할 것이다.
v2 = buf[((_BYTE)j + 1) & 7]
buf[((_BYTE)j + 1) & 7] == ROL(buf[((_BYTE)j + 1) & 7], 5) - byte_140004020[(unsigned __int8)key[j] ^ v2]
지금까지 위 수식을 연산하기 위한 조건들을 모두 알아봤다.
역연산을 수행하고 flag를 생성하는 코드를 작성하자.
import numpy as np
import re
def ROL(n, m):
shift = n << m
src = n >> (8 - m)
src &= 255
return shift | src
key = "I_am_KEY".encode()
import idc
def extract_byte_array(start_address, length):
extracted_data = []
for i in range(length):
byte_value = idc.get_wide_byte(start_address + i)
extracted_data.append(byte_value)
return extracted_data
unk_140004000 = extract_byte_array(0x140004000, 24)
byte_140004020 = extract_byte_array(0x140004020, 256)
# double for statement inverse operation
for bit in range(3):
arr = unk_140004000[bit*8:bit*8+8]
for i in range(15, -1, -1):
for j in range(7, -1, -1):
v2 = np.uint8(arr[(j+1)&7])
arr[(j+1)&7] = np.uint8(ROL(v2, 5) - byte_140004020[key[j] ^ arr[j & 7]])
unk_140004000[bit*8:bit*8+8] = arr
# Output flag!
flag = "DH{"
for i in unk_140004000:
flag += chr(i)
flag += "}"
print(flag)