콘텐츠로 건너뛰기

Ransomware

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

태그:

답글 남기기