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