OutDoorFrog의 리버싱 이야기

Ajou OpenCTF 2018 WriteUp [recovery] 본문

공부/CTF 문제 풀이

Ajou OpenCTF 2018 WriteUp [recovery]

OutDoorFrog 2018. 9. 28. 15:40

리버서가 바이너리를 복구하는 이야기입니다.



복구 이미지에 대한 이미지 검색결과




0. 문제 설명



파이썬 암호화 코드가 있는 "ENC.py"


파이썬 코드에 의해 암호화된 데이터를 지닌 "ENCRYPTED"


문제는 두 파일로 구성이 되어 있습니다.



Python, 파일 입출력, 정렬 알고리즘에 지식이 있으면 풀 수 있습니다.



1. Python 코드를 봅시다.



from struct import unpack,pack


table = [48, 27, 50, 8, 47, 73, 12, 4, 66, 0, 14, 77, 56, 26, 63, 67, 17, 11, 68, 22, 72, 69, 60, 64, 74, 58, 54, 42, 65, 32, 33, 40, 39, 37, 51, 59, 24, 35, 38, 61, 21, 31, 57, 20, 76, 13, 10, 43, 9, 78, 46, 44, 45, 49, 3, 75, 23, 2, 19, 25, 28, 41, 29, 6, 30, 53, 70, 7, 16, 18, 34, 1, 62, 52, 5, 55, 36, 79, 71, 15]


lt = len(table)

f = open("./ORIGINAL","r")

f2 = open("./ENCRYPTED","w")

d = f.read()

f.close()

a = ""

result = ""

for i in range(0,len(d),lt):

a = d[i:i+lt]

for j in range(0,lt):

result += a[table[j]]

f2.write(result)

f2.close()


(코드 설명)



숫자를 모은 배열이 있습니다.


테이블의 길이를 잽니다.


ORIGINAL 파일을 읽기 권한을 가지고 오픈합니다.

ENCRYPTED 파일을 쓰기 권한을 가지고 오픈합니다.


a 와 result 라는 문자열 변수를 만든 후


0부터 d까지 반복합니다.(lt의 크기만큼증가)

a의 문자열에 i부터 i+lt 만큼의 파일의 데이터를 넣습니다..

table 안에 나열된 숫자를 이용하여 데이터를 섞은 후 result에 저장합니다.


Result의 데이터를 ENCRYPTED에 쓴 후 핸들을 반환합니다.



2. 복호화할 수 있는 방법을 생각해봅시다.



간단합니다.



데이터를 80개씩 받아들여서 순번이라는 개념을 사용하여 원복합시다.



해캠에서 파일을 뒤집는 문제를 경험한 이후, 파일 포인터를 다루는 방법을 조금 연습했습니다.



조금 써먹어봅시다.



3. C가 Python보다 편한 사람


 

제가 특이 취향이라 Python보다 C를 더 좋아하는 사람입니다.



C 같은 경우 굉장히 익숙해서 편안합니다.



오류가 나와도 모르는 함수가 나와도 바로 사용법을 구하고 이용할 수 있습니다.



하지만 Python은 다릅니다.



C보다 간략한 표현, 사용자를 위한 프레임워크는 편리하지만



아직 연습량이 부족해 손에 익지 않았습니다.



(오류가 나오면 대처하기 어려움)



4. Python 문제를 C로 푼다.




typedef struct Data 

char Data;     // 데이터

int Number;    // 순번

}


int main(void){


int table = {48, 27, 50, 8, 47, 73, 12, 4, 66, 0, 14, 77, 56, 26, 63, 67, 17, 11, 68, 22, 72, 69, 60, 64, 74, 58, 54, 42, 65, 32, 33, 40, 39, 37, 51, 59, 24, 35, 38, 61, 21, 31, 57, 20, 76, 13, 10, 43, 9, 78, 46, 44, 45, 49, 3, 75, 23, 2, 19, 25, 28, 41, 29, 6, 30, 53, 70, 7, 16, 18, 34, 1, 62, 52, 5, 55, 36, 79, 71, 15}; // 데이터 순번


Data data[80] = {0,};


for(int i=0; i< (sizeof(table) / 4); i++) // 데이터 초기화

{

Data[i].Number = table[i];

}


FILE * en; // ENCRYPTED 파일 핸들

FILE * de; // DECRYPTED 파일 핸들


fopen_s(&en,  " 절대경로 + 이름 ", "r");     // 파일을 읽는다.

fopen_s(&de,  " 절대경로 + 이름 ", "w+");  // 파일을 쓴다.


fseek_ok = fseek(en, SEEK_SET, SEEK_END);  // 포인터 끝으로 옮겨서

int FileSize = ftell(en);                        // 파일 크기를 구함.


fseek(en, SEEK_END, SEEK_SET);

fseek(en, -2, SEEK_CUR);                 // 포인터 처음으로 정렬



for (int i = 0; i < FileSize; i + 80)

{

fread(buf, sizeof(char), 80, en);


for (int i = 0; i < sizeof(table) / 4; i++) // 80 개의 데이터를 담는다.

{


    data[i].Data = buf[i];


      }


for (int i = 0; i < ( sizeof(table) / 4 ); i++) 

      {

for (int j = 0; j < sizeof(table) / 4; j++)

   {

if (Encrypt_data[j].Number == i)

{

    Decrypt[i] = data[j].Data;

    j += sizeof(table); // 찾았을 시 건너뜀

}

   }

}


fwrite(Decrypt, sizeof(char), 80, de);


fseek(en, 80, SEEK_CUR); // 포인터 옮김

fseek(de, 80, SEEK_CUR); // 포인터 옮김


}




5. 결과


변경 전





변경 후




The key is To_test_the_limit_and_breakthrough




Comments