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

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다