readme.txt
Decrypt File (EXE)
By Pyutic
Exeinfo PE
UPX로 패킹되어있다.
C:\Users\seo\Documents\upx-4.2.2-win64>upx.exe -d C:\Users\seo\Desktop\ransomware\run.exe Ultimate Packer for eXecutables Copyright (C) 1996 - 2024 UPX 4.2.2 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 3rd 2024 File size Ratio Format Name -------------------- ------ ----------- ----------- 311296 <- 10240 3.29% win32/pe run.exe Unpacked 1 file.
정적분석을 위해 언패킹해보자.
Decompiled-src
main
.text:004135E9 pusha .text:004135EA popa .text:004135EB nop .text:004135EC push eax .text:004135ED pop eax .text:004135EE push ebx .text:004135EF pop ebx ...
main 함수의 프롤로그 쪽을 살펴보면 위와 같은 명령어, 쓸모없는 더미 코드가 계속 반복된다.
이로 인해 IDA에서 Pseudo Code로 변환하는데 어려움이 생기는데,
.text:004135E0 push ebp .text:004135E1 mov ebp, esp .text:004135E3 sub esp, 24h .text:004135E6 push ebx .text:004135E7 push esi .text:004135E8 push edi .text:004135E9 jmp loc_44A775
004135E9: 60 61 90 50 58 53 -> E9 87 71 03 00 90 (jmp loc_44A775)
004135E9 주소를 더미 코드가 끝난 뒤의 지점으로 점프하도록 명령어를 패치해주면 된다.
int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int v4; // [esp+Ch] [ebp-24h] FILE *v5; // [esp+1Ch] [ebp-14h] unsigned int v6; // [esp+20h] [ebp-10h] int v7; // [esp+28h] [ebp-8h] unsigned int i; // [esp+28h] [ebp-8h] unsigned int j; // [esp+28h] [ebp-8h] FILE *Stream; // [esp+2Ch] [ebp-4h] printf("Key : "); sub_401000(); scanf("%s", byte_44D370); v4 = strlen(byte_44D370); sub_401000(); v7 = 0; Stream = fopen("file", "rb"); sub_401000(); if ( !Stream ) { sub_401000(); printf(asc_44C1C4); sub_401000(); exit(0); } fseek(Stream, 0, 2); sub_401000(); v6 = ftell(Stream); sub_401000(); rewind(Stream); sub_401000(); while ( !feof(Stream) ) { sub_401000(); byte_5415B8[v7] = fgetc(Stream); sub_401000(); ++v7; sub_401000(); } sub_401000(); for ( i = 0; i < v6; ++i ) { byte_5415B8[i] ^= byte_44D370[i % v4]; sub_401000(); byte_5415B8[i] = ~byte_5415B8[i]; sub_401000(); } fclose(Stream); sub_401000(); v5 = fopen("file", "wb"); sub_401000(); sub_401000(); for ( j = 0; j < v6; ++j ) { fputc(byte_5415B8[j], v5); sub_401000(); } printf(asc_44C1E8); sub_401000(); return getch(); }
Key 값을 입력받고 file
을 fopen하여
key값의 한바이트씩 순회하면서 XOR, 0xff와 한번더 XOR 연산을 하고 있었다.
그렇게 연산된 데이터는 file로 다시 저장한다.
sub_401000
main 함수의 중간중간에 sub_401000 함수가 호출되는 것을 알 수 있다.
.text:00401006 pusha .text:00401007 popa .text:00401008 nop .text:00401009 push eax .text:0040100A pop eax .text:0040100B push ebx .text:0040100C pop ebx ...
마찬가지로 프롤로그에는 위와 같은 더미코드가 있었고,
.text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 push ebx .text:00401004 push esi .text:00401005 push edi .text:00401006 jmp loc_4135CE
00401006: 60 61 90 50 58 -> E9 C3 25 01 00 (jmp loc_4135CE)
이것 역시 더미 코드가 끝난 뒤의 지점으로 점프하도록 명령어를 패치해주면 된다.
void sub_401000() { ; }
따라서 암호화시키는 코드를 파이썬3로 구현하다면 아래와 같다. (Key의 경우, 임의로 ABCD로 지정)
with open("file", 'rb') as f: data = f.read() key = b"ABCD" enc = [] for i in range(len(data)): val = data[i] ^ key[i%len(key)] val = val ^ 0xff print(hex(val), end=' ') enc.append(val) enc = bytearray(enc) with open("file_encrypted", 'wb') as f: f.write(enc)
Solution
get_key.py
exe_header = b"\x4D\x5A\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xFF\xFF\x00\x00" exe_header += b"\xB8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00" with open("file", 'rb') as f: data = f.read() for i in range(len(data)): val = data[i] ^ exe_header[i%len(exe_header)] val = val ^ 0xff print(chr(val), end=' ') if(i>25): break
readme.txt 파일에 암호화된 파일 exe라는 것을 알려주고 있다.
따라서 exe 시그니처와 xor하면 key값을 알 수 있다.
PS C:\Users\Seo Hyun-gyu\Desktop\reversing_ransomware> python3 .\get_key.py l e t s p l a y c h e s s l e t s p l a y c h e s s l
key는 letsplaychess였다.
복호화된 exe 실행파일을 얻었다.
마찬가지로 upx 패킹이 되어있었고, 패킹을 풀어서 실행 파일을 확인해보자
int wmain_0() { printf("Key -> Colle System"); getch(); return 0; }
FLAG
Colle System