콘텐츠로 건너뛰기

ransom

문제 정보

드림이가 FLAG를 가져왔어요!!.
어라… FLAG가 이상하네요…??.??

문제 파일을 다운받아보면, flag.txt와 ransom.exe 파일이 존재한다.
Exeinfo PE 프로그램으로 확인해보면, UPX로 패킹된 실행 파일인걸 알 수 있다.

upx -d 명령어로 언팩한다.

myMac@MacBook-Pro ransom % ls
flag.txt	ransom.exe
myMac@MacBook-Pro ransom % upx -d ransom.exe 
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
     11776 <-      8192   69.57%    win32/pe     ransom.exe

Unpacked 1 file.

분석

int __cdecl main(int argc, const char **argv, const char **envp)
{
  sub_401080();
  sub_401610();
  sub_4011D0();
  return 0;
}

메인 함수에는 3개의 함수가 존재한다.

sub_401080

BOOL sub_401080()
{
  HANDLE hSnapshot; // [esp+0h] [ebp-234h]
  PROCESSENTRY32W pe; // [esp+4h] [ebp-230h] BYREF

  memset(&pe, 0, sizeof(pe));
  pe.dwSize = 556;
  hSnapshot = CreateToolhelp32Snapshot(0xFu, 0);
  if ( Process32FirstW(hSnapshot, &pe) )
  {
    do
    {
      if ( !wcsicmp(pe.szExeFile, L"x32dbg.exe") )
      {
        sub_4019C0("[+] Detect it!!", (char)hSnapshot);
        system("pause");
        exit(0);
      }
      if ( !wcsicmp(pe.szExeFile, L"x64dbg.exe") )
      {
        sub_4019C0("[+] Detect it!!", (char)hSnapshot);
        system("pause");
        exit(0);
      }
      if ( !wcsicmp(pe.szExeFile, L"Ollydbg.exe") )
      {
        sub_4019C0("[+] Detect it!!", (char)hSnapshot);
        system("pause");
        exit(0);
      }
    }
    while ( Process32NextW(hSnapshot, &pe) );
  }
  return CloseHandle(hSnapshot);
}

CreateToolhelp32Snapshot – 프로세스 정보를 얻기 위해 핸들을 생성한다.
Process32First(), Process32Next() – 프로세스 정보를 나열한다.

따라서 실행중인 프로세스에서 x32dbg.exe, x64dbg.exe, Ollydbg.exe 위 이름 중 하나라도 있으면 종료시킨다.


sub_401610

int sub_401610()
{
  ...
  if ( sub_401000() )
  {
    sub_4019C0("[+] Detect it!!");
    system("pause");
    exit(0);
  }
  ...
}

먼저 sub_401000 함수에서 참이면 프로그램을 종료시킨다.

sub_401000 함수를 살펴보자


sub_401000

int sub_401000()
{
  HANDLE CurrentThread; // eax
  int v2; // [esp+0h] [ebp-2D4h]
  CONTEXT Context; // [esp+4h] [ebp-2D0h] BYREF

  v2 = 0;
  Context.ContextFlags = 0x10010;
  CurrentThread = GetCurrentThread();
  GetThreadContext(CurrentThread, &Context);
  if ( Context.Dr0 || Context.Dr1 || Context.Dr2 || Context.Dr3 )
    return 1;
  return v2;
}

하드웨어 브레이크 포인트가 설정되었는지 확인하는 안티 디버깅이였다.

다시 sub_401610 함수로 돌아가보자

int sub_401610()
{
  int v1; // [esp+0h] [ebp-450h]
  int Offset; // [esp+4h] [ebp-44Ch]
  size_t ElementCount; // [esp+Ch] [ebp-444h]
  size_t i; // [esp+10h] [ebp-440h]
  size_t j; // [esp+10h] [ebp-440h]
  FILE *Stream; // [esp+14h] [ebp-43Ch] BYREF
  char Buffer[2]; // [esp+18h] [ebp-438h] BYREF
  char v8; // [esp+1Ah] [ebp-436h]
  char v9; // [esp+1Bh] [ebp-435h]
  char v10; // [esp+1Fh] [ebp-431h]
  _WORD v11[8]; // [esp+418h] [ebp-38h] BYREF
  char v12[20]; // [esp+428h] [ebp-28h] BYREF
  char v13[16]; // [esp+43Ch] [ebp-14h] BYREF

  strcpy(v12, "abc!@#qwe012fgh456");
  strcpy(v13, "1@!@#a134234");
  *(_WORD *)&v13[13] = 0;
  v13[15] = 0;
  strcpy((char *)v11, "zBNdlwi102394");
  v11[7] = 0;
  ...
  sub_4019C0("[+] Good!");
  if ( fopen_s(&Stream, "flag.txt", "r+b") )
    return -1;
  while ( !feof(Stream) )
  {
    Offset = ftell(Stream);
    ElementCount = fread(Buffer, 1u, 0x400u, Stream);
    if ( !ElementCount )
      break;
    for ( i = 0; i < ElementCount; ++i )
      Buffer[i] ^= v12[i % 0x10];
    for ( j = 0; j < ElementCount; ++j )
    {
      if ( !(j % 2) )
        ++Buffer[j];
      if ( j % 2 == 1 )
        Buffer[j] += 2;
      Buffer[0] ^= v9;                          // Buffer[0] ^= Buffer[3]
      v8 ^= v10;                                // Buffer[2] ^= Buffer[7]
    }
    v1 = ftell(Stream);
    fseek(Stream, Offset, 0);
    fwrite(Buffer, 1u, ElementCount, Stream);
    fseek(Stream, v1, 0);
  }
  fclose(Stream);
  return 1;
}

flag.txt 파일을 암호화시키는 함수가 보이기 시작한다.
처음에는 flag.txt 파일에 있는 한 문자와 “abc!@#qwe012fgh456” 문자열에서 ‘반복 카운트 값에 0x10을 나눈 나머지’ 인덱스의 한문자와 XOR한다.
그리고 카운트값이 2로 나누어 떨어지면 Buffer[j] = Buffer[j] +1을 수행하고,
카운트값이 2로 나눴을때 나머지가 1이면 Buffer[j] = Buffer[j] +2를 수행한다.
(여기서 j는 카운트값)

하지만, 41~42줄에 있는 Buffer[0] ^= v9; v8 ^= v10; 연산에 대한 변수가 자세히 나와있지 않았다.

따라서 어셈블리로 한번 봐야할 필요가 있었다.

loc_401867:                             ; CODE XREF: sub_401610+237↑j
   mov     edx, 1          ; edx = 1
   imul    eax, edx, 0     ; eax = edx * 0 = 0
   mov     ecx, 1          ; ecx = 1
   imul    edx, ecx, 3     ; edx = ecx * 3 = 3
   movsx   ecx, [ebp+edx+Buffer] ; ecx = Buffer[3]
   movsx   edx, [ebp+eax+Buffer] ; edx = Buffer[0]
   xor     edx, ecx        ; edx = Buffer[0] ^ Buffer[3]
   mov     eax, 1          ; eax = 1
   imul    ecx, eax, 0     ; ecx = eax * 0 = 0
   mov     [ebp+ecx+Buffer], dl ; Buffer[0] = dl (8bits edx) = Buffer[0] ^ Buffer[3]
   mov     edx, 1          ; edx = 1
   shl     edx, 1          ; edx = edx << 1 = 1 << 1 = 2
   mov     eax, 1          ; eax = 1
   imul    ecx, eax, 7     ; ecx = eax * 7 = 7
   movsx   eax, [ebp+ecx+Buffer] ; eax = Buffer[7]
   movsx   ecx, [ebp+edx+Buffer] ; ecx = Buffer[2]
   xor     ecx, eax        ; ecx = Buffer[2] ^ Buffer[7]
   mov     edx, 1          ; edx = 1
   shl     edx, 1          ; edx = edx << 1 = 1 << 1 = 2
   mov     [ebp+edx+Buffer], cl ; Buffer[2] = cl (8bits ecx) = Buffer[2] ^ Buffer[7]
   jmp     loc_4017E3

간단한 산수 계산으로, v9 = Buffer[3], v8 = Buffer[2], v10 = Buffer[7]이란걸 알 수 있었다.


sub_4011D0

int sub_4011D0()
{
  int v1; // [esp+0h] [ebp-430h]
  int Offset; // [esp+4h] [ebp-42Ch]
  unsigned int ElementCount; // [esp+Ch] [ebp-424h]
  char v4[20]; // [esp+10h] [ebp-420h] BYREF
  unsigned int i; // [esp+24h] [ebp-40Ch]
  FILE *Stream; // [esp+28h] [ebp-408h] BYREF
  char Buffer[3]; // [esp+2Ch] [ebp-404h] BYREF
  char v8; // [esp+2Fh] [ebp-401h]
  char v9; // [esp+30h] [ebp-400h]
  char v10; // [esp+31h] [ebp-3FFh]
  char v11; // [esp+33h] [ebp-3FDh]
  char v12; // [esp+34h] [ebp-3FCh]
  char v13; // [esp+35h] [ebp-3FBh]
  char v14; // [esp+37h] [ebp-3F9h]

  if ( IsDebuggerPresent() )
  {
    system("pause");
    exit(0);
  }
  qmemcpy(v4, "Pp btXHE", 8);
  v4[8] = 0x90;
  v4[9] = 0x90;
  v4[10] = 0x70;
  v4[11] = 0x40;
  v4[12] = 0x36;
  v4[13] = 0x45;
  v4[14] = 0x55;
  v4[15] = 0x71;
  v4[16] = 0x18;
  v4[17] = 0x19;
  v4[18] = 0x70;
  if ( fopen_s(&Stream, "flag.txt", "r+b") )
    return -1;
  while ( !feof(Stream) )
  {
    Offset = ftell(Stream);
    ElementCount = fread(Buffer, 1u, 0x400u, Stream);
    if ( !ElementCount )
      break;
    for ( i = 0; i < ElementCount; ++i )
    {
      Buffer[i] ^= v4[i];
      Buffer[0] -= 0x15;
      v8 ^= 0x18u;                              // Buffer[3] ^= 0x18
      v12 -= 0x33;                              // Buffer[8] -= 0x33
      v14 ^= 0x44u;                             // Buffer[11] ^= 0x44
      v13 += 0x47;                              // Buffer[9] += 0x47
      v9 ^= 0x88u;                              // Buffer[4] ^= 0x88
      v11 ^= 0x68u;                             // Buffer[7] ^= 0x68
      Buffer[0] += 0x15;
      v14 ^= 0x44u;                             // Buffer[11] ^= 0x44
      v13 -= 0x47;                              // Buffer[9] -= 0x47
      v12 += 0x33;                              // Buffer[8] += 0x33
      v10 += 0x11;                              // Buffer[5] += 0x11
      v8 ^= 0x18u;                              // Buffer[3] ^= 0x18
      v9 ^= 0x88u;                              // Buffer[4] ^= 0x88
      v11 ^= 0x68u;                             // Buffer[7] ^= 0x68
    }
    v1 = ftell(Stream);
    fseek(Stream, Offset, 0);
    fwrite(Buffer, 1u, ElementCount, Stream);
    fseek(Stream, v1, 0);
  }
  return fclose(Stream);
}


IsDebuggerPresent 함수로 디버깅 중인지 확인하고
한번더 flag.txt 파일을 열어서 암호화시킨다.

v4 문자열은 아래와 같고

\x50\x70\x20\x62\x74\x58\x48\x45\x90\x90\x70\x40\x36\x45\x55\x71\x18\x19\x70\x00


flag.txt 파일에 있는 한 문자와 v4 문자열에서 ‘반복 카운트 값인’ 인덱스의 한문자와 XOR한다.

그리고 여러 연산이 있는데 XOR을 2번해서 원래 값으로 돌리거나, 뺏다가 더해서 원래값을 돌리지만
57줄에 있는 v10 += 0x11;은 그대로였다.

mov     ecx, [ebp+var_40C]
movsx   edx, [ebp+ecx+var_420]
mov     eax, [ebp+var_40C]
movsx   ecx, [ebp+eax+Buffer]
xor     ecx, edx
mov     edx, [ebp+var_40C]
mov     [ebp+edx-404h], cl
mov     eax, 1
imul    ecx, eax, 0
movsx   edx, [ebp+ecx+Buffer]
sub     edx, 15h
mov     eax, 1
imul    ecx, eax, 0
mov     [ebp+ecx+Buffer], dl ; Buffer[0] -= 0x15
mov     edx, 1
imul    eax, edx, 3
movsx   ecx, [ebp+eax+Buffer]
xor     ecx, 18h
mov     edx, 1
imul    eax, edx, 3
mov     [ebp+eax+Buffer], cl ; Buffer[3] ^= 0x18
mov     ecx, 1
shl     ecx, 3
movsx   edx, [ebp+ecx+Buffer]
sub     edx, 33h ; '3'
mov     eax, 1
shl     eax, 3
mov     [ebp+eax+Buffer], dl ; Buffer[8] -= 0x33
mov     ecx, 1
imul    edx, ecx, 0Bh
movsx   eax, [ebp+edx+Buffer]
xor     eax, 44h
mov     ecx, 1
imul    edx, ecx, 0Bh
mov     [ebp+edx+Buffer], al ; Buffer[11] ^= 0x44
mov     eax, 1
imul    ecx, eax, 9
movsx   edx, [ebp+ecx+Buffer]
add     edx, 47h ; 'G'
mov     eax, 1
imul    ecx, eax, 9
mov     [ebp+ecx+Buffer], dl ; Buffer[9] += 0x47
mov     edx, 1
shl     edx, 2
movsx   eax, [ebp+edx+Buffer]
xor     eax, 88h
mov     ecx, 1
shl     ecx, 2
mov     [ebp+ecx+Buffer], al ; Buffer[4] ^= 0x88
mov     edx, 1
imul    eax, edx, 7
movsx   ecx, [ebp+eax+Buffer]
xor     ecx, 68h
mov     edx, 1
imul    eax, edx, 7
mov     [ebp+eax+Buffer], cl ; Buffer[7] ^= 0x68
mov     ecx, 1
imul    edx, ecx, 0
movsx   eax, [ebp+edx+Buffer]
add     eax, 15h
mov     ecx, 1
imul    edx, ecx, 0
mov     [ebp+edx+Buffer], al ; Buffer[0] += 0x15
mov     eax, 1
imul    ecx, eax, 0Bh
movsx   edx, [ebp+ecx+Buffer]
xor     edx, 44h
mov     eax, 1
imul    ecx, eax, 0Bh
mov     [ebp+ecx+Buffer], dl ; Buffer[11] ^= 0x44
mov     edx, 1
imul    eax, edx, 9
movsx   ecx, [ebp+eax+Buffer]
sub     ecx, 47h ; 'G'
mov     edx, 1
imul    eax, edx, 9
mov     [ebp+eax+Buffer], cl ; Buffer[9] -= 0x47
mov     ecx, 1
shl     ecx, 3
movsx   edx, [ebp+ecx+Buffer]
add     edx, 33h ; '3'
mov     eax, 1
shl     eax, 3
mov     [ebp+eax+Buffer], dl ; Buffer[8] += 0x33
mov     ecx, 1
imul    edx, ecx, 5
movsx   eax, [ebp+edx+Buffer]
add     eax, 11h
mov     ecx, 1
imul    edx, ecx, 5
mov     [ebp+edx+Buffer], al ; Buffer[5] += 0x11
mov     eax, 1
imul    ecx, eax, 3
movsx   edx, [ebp+ecx+Buffer]
xor     edx, 18h
mov     eax, 1
imul    ecx, eax, 3
mov     [ebp+ecx+Buffer], dl ; Buffer[3] ^= 0x18
mov     edx, 1
shl     edx, 2
movsx   eax, [ebp+edx+Buffer]
xor     eax, 88h
mov     ecx, 1
shl     ecx, 2
mov     [ebp+ecx+Buffer], al ; Buffer[4] ^= 0x88
mov     edx, 1
imul    eax, edx, 7
movsx   ecx, [ebp+eax+Buffer]
xor     ecx, 68h
mov     edx, 1
imul    eax, edx, 7
mov     [ebp+eax+Buffer], cl ; Buffer[7] ^= 0x68
jmp     loc_401325

어셈블리로 확인해보니 v10=Buffer[5]였다.
따라서, 문자열의 크기만큼 Buffer[5]는 계속 0x11을 더하는 것을 반복한다.


암호화

암호화시키는 코드를 파이썬 코드로 구현하면 아래와 같다.

0x11을 계속 더하다보면 0~255 char 범위를 넘어버리므로
24줄에 % 256 수식을 추가하였다.

#ransom.py

def encrypt_stage1(data):
    data = list(data)
    v12 = "abc!@#qwe012fgh456"
    for i in range(len(data)):
        data[i] =  data[i] ^ ord(v12[i%0x10])

    for i in range(len(data)):
        if i % 2 == 0:
            data[i] = data[i] + 1
        if i % 2 == 1:
            data[i] = data[i] + 2
        data[0] = data[0] ^ data[3]
        data[2] = data[2] ^ data[7]

    return data

def encrypt_stage2(data):
    result = ""
    v4 = b'\x50\x70\x20\x62\x74\x58\x48\x45\x90\x90\x70\x40\x36\x45\x55\x71\x18\x19\x70\x00'
    for i in range(len(data)):
        data[i] = data[i] ^ v4[i]
        data[5] = (data[5] + 0x11) % 256    #char 범위 0-255

    for i in range(len(data)):
        result = result + chr(data[i])

    return result

data = bytes("DH{Do_y0u_Know_Oni?}", 'ascii')
data = encrypt_stage1(data) 
# print(data)
print(encrypt_stage2(data))

복호화

암호화된 문자열을 역순으로 하나씩 복호화해야 풀리더라

#ransom-dec.py

def decrypt_stage1(data):
    data = list(data)
    v4 = b'\x50\x70\x20\x62\x74\x58\x48\x45\x90\x90\x70\x40\x36\x45\x55\x71\x18\x19\x70\x00'
    for i in range(len(data)-1, -1, -1):
        data[5] = (data[5] - 0x11) % 256
        data[i] = data[i] ^ v4[i]
    return data

def decrypt_stage2(data):
    v12 = "abc!@#qwe012fgh456"
    result = ""
    for i in range(len(data)-1 ,-1, -1):
        data[2] = data[2] ^ data[7]
        data[0] = data[0] ^ data[3]
        if i % 2 == 0:
            data[i] = data[i] - 1
        if i % 2 == 1:
            data[i] = data[i] - 2
        

    for i in range(len(data)-1, -1, -1):
        data[i] = data[i] ^ ord(v12[i%0x10])

    for i in range(len(data)):
        result = result + chr(data[i])
    
    return result

data = b'\x74\x5C\x37\x05\x44\x8A\x41\x0C\x81\xE1\x0B\x1E\x3C\x57\x6D\x0C\x08\x14\x2D\x5E'
data = decrypt_stage1(data)
# print(data)
print(decrypt_stage2(data))

myMac@MacBook-Pro Desktop % python3 ransom-dec.py   
DH{Do_y0u_Know_Oni?}
태그:

답글 남기기