ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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

    댓글

Designed by Tistory.