dreamhack.io Reversing Basic Challenge #6
Static analysis in IDA of a routine that checks user input character by character.
-
문제 개요 이번 문제는 실행 파일을 IDA로 분석하여 올바른 입력값을 찾아내는 Reverse Engineering 문제이다. 프로그램은 사용자 로부터 문자열을 입력받은 뒤, 내부 검증 함수를 통해 입력값이 올바른지 확인한다. 입력값이 맞으면 Correct를 출력하고, 틀리면 Wrong을 출력한다.
-
main 함수 분석 IDA에서 main 함수를 확인하면 다음과 같은 코드 구조를 볼 수 있다.
int __fastcall main(int argc, const char **argv, const char **envp) { char v4[256]; // [rsp+20h] [rbp-118h]
BYREF memset(v4, 0, sizeof(v4)); sub_1400011B0(“Input : ”, argv, envp); sub_140001210(“%256s”, v4); if (
(unsigned int)sub_140001000(v4) ) puts("Correct"); else puts("Wrong"); return 0; }
위 코드를 보면 프로그램은 먼저 v4라는 256바이트 크기의 버퍼를 생성하고, memset을 이용해 전체를 0으로 초기화한 다. 그 후 “Input : “이라는 문자열을 출력하고, sub_140001210(“%256s”, v4);를 통해 사용자 입력을 v4에 저장한다. 가장 중요한 부분은 다음 코드이다.
if ( (unsigned int)sub_140001000(v4) ) puts("Correct"); else puts("Wrong");
즉, 입력값 v4가 sub_140001000 함수로 전달되고, 이 함수가 1을 반환하면 Correct, 0을 반환하면 Wrong이 출력된 다. 따라서 핵심은 sub_140001000 함수가 입력값을 어떻게 검증하는지 분석하는 것이다.
- 검증 함수 분석 IDA에서 sub_140001000 함수를 확인하면 다음과 같은 코드가 보인다.
__int64 __fastcall sub_140001000(__int64 a1) { int i; // [rsp+0h] [rbp-18h] for ( i = 0; (unsigned __int64)i <
0x12; ++i ) { if ( byte_140003020[*(unsigned __int8 *)(a1 + i)] != byte_140003000[i] ) return 0LL; } return
1LL; }
이 함수는 입력값의 앞부분 18바이트를 검사한다. 여기서 0x12는 10진수로 18이다. 즉, 반복문은 다음과 같이 18번 실행된다. 검증 조건은 해석하면 다음과 같다.
byte_140003020[input[i]] == byte_140003000[i]
만약 하나라도 다르면 즉시 0을 반환하고, 모든 18바이트가 조건을 만족하면 1을 반환한다. 따라서 입력값을 찾기 위해서는 byte_140003000의 각 값이 byte_140003020 배열의 몇 번째 인덱스에 위치하는지 찾 아야 한다. 그 인덱스 값을 문자로 변환하면 정답 문자열이 된다.
- 풀이 아이디어 검증 함수의 조건은 다음과 같다.
byte_140003020[input[i]] == byte_140003000[i]
이를 반대로 생각하면 다음과 같다.
input[i] = index of byte_140003000[i] in byte_140003020
즉, byte_140003000[i] 값이 byte_140003020 배열 안에서 몇 번째 위치에 있는지 찾으면 된다. 예를 들어, 만약 byte_140003000[0] 값이 0x00이고, byte_140003020 배열에서 0x00이 0x52번째 위치에 있다면, 첫 번째 입력 문자는 ASCII 0x52, 즉 ‘R’이 된다. 이 과정을 18번 반복하면 전체 정답 문자열을 얻을 수 있다.
IDA의 Script command 창에서 Scripting language가 IDC로 되어 있었기 때문에, 다음과 같은 IDC 코드를 작성했다. 사실 IDC 코드 못짜서 지피티한테 로직 설명하고 시켰다.
auto target, table, answer, i, b, j; answer = ""; for (i = 0; i < 0x12; i++) { b = Byte(0x140003000 + i); for (j = 0;
j < 0x100; j++) { if (Byte(0x140003020 + j) == b) { answer = answer + sprintf("%c", j); break; } } }
Message("%s\n", answer);