-
rev-baic-9리버싱 엔지니어링/드림핵 2022. 5. 3. 21:15
rev-basic-9: https://dreamhack.io/wargame/challenges/23/
rev-basic-9
Reversing Basic Challenge #9 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다. 해당 바이너리를 분석하여 correct를 출
dreamhack.io
eax : strlen
- 주로 함수 return 값을 eax에 저장하기 때문
cdq : 나누기 연산시 사용
- eax 부호비트를 edx까지 확장 -> edx는 0으로 설정됨
- 나누기 연산 시 몫을 eax에 나머지를 edx에 넣기 위해 사용됨
test : and 연산 수행, 결과값은 저장하지 않음
jz : 왼쪽 인자 값이 0이면 점프
-> eax가 0이 되어야함 -> 7로 나누었을 때 몫과 나머지가 같아야함 -> 문자열 길이는 8의 배수-1
: (strlen+1) % 8 = 7 이면 loc_7FF7403C1030로 점프
loc_7FF7403C1030
[rsp+38h+var_18]을 0으로 초기화하고 이동
int i = 0; 으로 예측 가능
loc_7FF7403C1045
jge: 왼쪽 인자 값이 오른쪽 값보다 크거나 같으면 점프
i >= strlen([rsp+38h+var_14])+1 이면 loc_7FF7403C106B로 점프(왼쪽)
-> i < strlen+1 인 동안 반복
점프하지 않은 경우는(오른쪽),
rax와 rcx를 주소 str+i로 세팅하고 함수 sub_7FF7403C10A0 부르고
loc_7FF7403C103A로 점프
loc_7FF7403C106B
memcmp(buf1, buf2, 0x19) 호출
buf2는 { 0x7E, 0x7D, 0x9A, 0x8B, 0x25, 0x2D, 0xD5, 0x3D, 0x03, 0x2B, 0x38, 0x98, 0x27, 0x9F, 0x4F, 0xBC, 0x2A, 0x79, 0x00, 0x7D, 0xC4, 0x2A, 0x4F, 0x58, 0x00 }
buf1이 연산으로 구해지는 듯함, 따라서 buf1이 buf2처럼 만들어져야함!
문자열 길이를 추측해보면, 우선 strlen은 8의 배수- 1이어야함.
따라서 25(0x19)가 아닌 23로 추측되고 마지막은 '0x00'(문자열 마지막)
나머지 하나는 연산하며 추가되는 걸로 추측해볼 수 있다.
(실제로, bp를 걸고 실행했을 때 문자열 길이가 23인 경우에만 bp가 실행되고 24, 25는 그냥 종료되었다.)
loc_7FF7403C103A
i를 8 증가시킨다.
아무래도 아까 call한 함수에서 8개의 문자로 연산을 진행하는듯
F7로 해당 함수를 들어가 살펴보아야할 필요가 있어보임
sub_7FF7403C10A0
여기서는 I_am_KEY 문자열과 0x10번의 반복문이 있다는 것을 확인 가능함
밑으로 내려가면,
또 반복문이 있는 것을 확인
따라서 여기서는 중첩반복문을 사용했음을 알 수 있다.
그리고 오른쪽 아래의 박스에서 연산을 진행함을 알 수 있다.
연산이 복잡해서 실제 코드로 작성해 정리하며 분석했다.
정리한 코드 아래와 같다.
#include <iostream> #include <cmath> #include <string> using namespace std; void ROR(int& data, int c) { for (int i = 0; i < c; i++) { if (data % 2 == 0) { data /= 2; } else { data /= 2; data += pow(2, 7); } } } int main() { int array[256]; cout << "array : "; for (int i = 0; i < 256; i++) { // 입력 int k; cin >> hex >> k; array[i] = k; } int Buf1[25] = { 0, }; for (int i = 0; i < 23; i++) { // 입력 char a; cin >> a; Buf1[i] = (int)a; } for (int i = 0; i < 23+1; i += 8) { string iam = "I_am_KEY"; for (int j = 0; j < 0x10; j++) { for (int k = 0; k < 8; k++) { int var_48 = Buf1[i + k]; var_48 ^= (int)iam[k]; int arr = array[var_48]; int eax = arr + Buf1[i + ((k + 1) % 8)]; eax %= 256; ROR(eax, 5); Buf1[i + ((k + 1) % 8)] = eax; } cout << endl; } } }
void ROR(int& data, int c) ROR연산을 수행하는 함수 int array[256] byte_7FF741E54020에 저장된 값
(코드 실행시 입력하여 사용)int Buf1[25] 사용자가 입력하는 값 for(int k=0;k<8;k++){} 어셈블리어로 되어있는 연산을 이해하기 쉽도록 분석 후 소스코드로 작성 : 명령어를 해석하고 헷갈리는 부분은 값을 넣고 실행하며 진행과정을 보면서 해석할 수 있었다!
이제 연산을 반대로 진행하여 원하는 답을 구하면 문제를 해결할 수 있다.
답을 구하는 코드는 아래와 같다.
#include <iostream> #include <string> using namespace std; void ROL(int& data, int c) { for (int i = 0; i < c; i++) { if (data >= 128) { data %= 128; data = data * 2 + 1; } else { data *= 2; } } } int main() { int array[256]; cout << "array : "; for (int i = 0; i < 256; i++) { int k; cin >> hex >> k; array[i] = k; } int Buf[25] = { 0x7E, 0x7D, 0x9A, 0x8B, 0x25, 0x2D, 0xD5, 0x3D, 0x03, 0x2B, 0x38, 0x98, 0x27, 0x9F, 0x4F, 0xBC, 0x2A, 0x79, 0x00, 0x7D, 0xC4, 0x2A, 0x4F, 0x58, 0x00 }; int str[25] = { 0, }; string iam = "I_am_KEY"; for (int i = 0; i < 24; i += 8) { // 연산 순서에 맞게 변경 for (int j = 0; j < 8; j++) { if (j == 0) str[i + j] = Buf[i + j]; else str[i + j] = Buf[i + 8 - j]; } } for (int i = 0; i < 23 + 1; i += 8) { for (int j = 0; j < 0x10; j++) { for (int k = 0; k < 8; k++) { int c_var = str[i + k]; int m_var = str[i + (k + 1) % 8]; ROL(c_var, 5); // ROR(eax, 5); ok m_var ^= (int)iam[7 - k]; // var_48 ^= (int)iam[k]; int arr = array[m_var]; // int arr = array[var_48]; str[i + k] = c_var - arr; // int eax = arr + Buf1[i + ((k + 1) % 8)]; if (str[i + k] < 0) str[i + k] += 256; //eax %= 256 } } } for (int i = 0; i < 24; i += 8) { // 다시 되돌리기 for (int j = 0; j < 8; j++) { if (j == 0)Buf[i + j] = str[i + j]; else Buf[i + j] = str[i + 8 - j]; } } for (int i = 0; i < 24; i++) { cout << char(Buf[i]); } return 0; }
void ROL(int& data, int c) ROL연산을 수행하는 함수 int array[256] byte_7FF741E54020에 저장된 값
(코드 실행시 입력하여 사용)int Buf[25] memcmp에서 연산 결과와 비교하는 값
-> 이 값을 반대로 연산해서 답을 얻을 수 있다.int str[25] 연산을 반대로 진행하기 위해 순서를 바꾼 배열
-> 값을 저장한 순서가 Buf에서는 8 1 2 3 4 5 6 7 이기 때문에 8 7 6 5 4 3 2 1이 되도록 수정함for(int k=0;k<8;k++){} 반대로 연산을 진행 cout<< char(Buf[i]) 연산하기 편하도록 순서를 변경했으므로 다시 원래대로 순서를 변경해서 Buf를 출력해 답 확인 위 코드에 array값을 입력하면 답을 얻을 수 있다.
<답>
'리버싱 엔지니어링 > 드림핵' 카테고리의 다른 글
patch(+ IDA 주소 점프하도록 패치하기) (0) 2022.05.06 rev-baic-7,8 (0) 2022.04.19 rev-basic-5,6 (0) 2022.04.09 rev-basic-4 (0) 2022.04.05 rev-basic-3 (0) 2022.04.05