NTU Malware Reverse Lab 2 write up

NTU Malware Reverse Lab 2 write up

tags: NTU_MR Malware Reverse Engineering and Analysis NTU

| Name| 何秉學 |StuID| R11921A16| | ——– | ——– | ——– | ——– |

Analyze the malware 1 found in the file Lab13-02.zip -> Lab13-02.exe, generate ‘yourself’ write ups (with screenshots) and answer the follow questions :

1.1 Using dynamic analysis, determine what this malware creates. Ans:

  • According to reference, you can also use procmon(Process Monitor) to monitor the whole progress after executing this file. The progress of first iteration is shown as below result from process monitor Then use PEiD to check if the file has shell or not. And refer to the result as below, it seems has not shell, so that we can use IDA directly. result from PEiD
  • I used IDA to analyze this executable file and observe that it’ll execute sub_401851 function every 10 seconds. And when I execute it in VM, it’ll create a temp file name temp00XXXXXX.
      int __cdecl main(int argc, const char **argv, const char **envp)
      {
        while ( 1 )
        {
          Sleep(5000u);
          sub_401851();
          Sleep(5000u);
        }
      }
    

1.2 Use static techniques such as an xor search, FindCrypt2, KANAL, and the IDA Entropy Plugin to look for potential encoding. What do you find?(Install findcrypt and IDA Entropy plugin can refer to Appendix 1, 2) Ans:

  • Refer to [4], I used KANAL, a plugin in PEiD, to examine whether it has been crypto or not. Apparently, it has not crypto signature. result from KANAL
  • Used another plugin in IDA, Findcrypt, to check if the file has been crypted and it seems that has not been crypted. result from Findcrypt plugin
  • Used text search in search icon use text search You can see that there’re 12 xor result used by 0x00401739 and one used by 0x0040128D. Especially, 4 of the xor instructions did not belong to any function. result of text search
  • So, check out the structure of main function and sub_401851 and renamed the sub-function by [1].
      .text:00401851 ; =============== S U B R O U T I N E =======================================
      .text:00401851
      .text:00401851 ; Attributes: bp-based frame
      .text:00401851
      .text:00401851 sub_401851      proc near               ; CODE XREF: _main+2D↓p
      .text:00401851
      .text:00401851 FileName        = byte ptr -20Ch
      .text:00401851 hMem            = dword ptr -0Ch
      .text:00401851 nNumberOfBytesToWrite= dword ptr -8
      .text:00401851 var_4           = dword ptr -4
      .text:00401851
      .text:00401851                 push    ebp
      .text:00401852                 mov     ebp, esp
      .text:00401854                 sub     esp, 20Ch
      .text:0040185A                 mov     [ebp+hMem], 0
      .text:00401861                 mov     [ebp+nNumberOfBytesToWrite], 0
      .text:00401868                 lea     eax, [ebp+nNumberOfBytesToWrite]
      .text:0040186B                 push    eax
      .text:0040186C                 lea     ecx, [ebp+hMem]
      .text:0040186F                 push    ecx
      .text:00401870                 call    Take_Screenshot
      .text:00401875                 add     esp, 8
      .text:00401878                 mov     edx, [ebp+nNumberOfBytesToWrite]
      .text:0040187B                 push    edx
      .text:0040187C                 mov     eax, [ebp+hMem]
      .text:0040187F                 push    eax
      .text:00401880                 call    Encode_Func
      .text:00401885                 add     esp, 8
      .text:00401888                 call    ds:GetTickCount
      .text:0040188E                 mov     [ebp+var_4], eax
      .text:00401891                 mov     ecx, [ebp+var_4]
      .text:00401894                 push    ecx
      .text:00401895                 push    offset aTemp08x ; "temp%08x"
      .text:0040189A                 lea     edx, [ebp+FileName]
      .text:004018A0                 push    edx             ; char *
      .text:004018A1                 call    _sprintf
      .text:004018A6                 add     esp, 0Ch
      .text:004018A9                 lea     eax, [ebp+FileName]
      .text:004018AF                 push    eax             ; lpFileName
      .text:004018B0                 mov     ecx, [ebp+nNumberOfBytesToWrite]
      .text:004018B3                 push    ecx             ; nNumberOfBytesToWrite
      .text:004018B4                 mov     edx, [ebp+hMem]
      .text:004018B7                 push    edx             ; lpBuffer
      .text:004018B8                 call    Write_File
      .text:004018BD                 add     esp, 0Ch
      .text:004018C0                 mov     eax, [ebp+hMem]
      .text:004018C3                 push    eax             ; hMem
      .text:004018C4                 call    ds:GlobalUnlock
      .text:004018CA                 mov     ecx, [ebp+hMem]
      .text:004018CD                 push    ecx             ; hMem
      .text:004018CE                 call    ds:GlobalFree
      .text:004018D4                 mov     esp, ebp
      .text:004018D6                 pop     ebp
      .text:004018D7                 retn
      .text:004018D7 sub_401851      endp
    
  • Used IDA Entropy plugin to check the file and it seems has nothing unusual. The config is as [4] result of IDA Entropy

1.3 Based on your answer to question 1, which imported function would be a good prospect for finding the encoding functions? Ans: As reference [1], we can see what sub-function did. And as [2] said, we can start at WriteFile to trace back and to find encoding function, because most of the process will encode the file right before WriteFile funtion.

HGLOBAL sub_401851()
{
  CHAR FileName; // [esp+0h] [ebp-20Ch]
  HGLOBAL hMem; // [esp+200h] [ebp-Ch]
  DWORD nNumberOfBytesToWrite; // [esp+204h] [ebp-8h]
  DWORD v4; // [esp+208h] [ebp-4h]

  hMem = 0;
  nNumberOfBytesToWrite = 0;
  Take_Screenshot(&hMem, &nNumberOfBytesToWrite);
  Encode_Func((int)hMem, nNumberOfBytesToWrite);
  v4 = GetTickCount();
  sprintf(&FileName, aTemp08x, v4);
  Write_File(hMem, nNumberOfBytesToWrite, &FileName);
  GlobalUnlock(hMem);
  return GlobalFree(hMem);
}

1.4 Where is the encoding function in the disassembly? Ans:

  • After accessing to sub_401851(), we can analyze these function clearly, such as sub_401070(Take_Screenshot), sub_40181F(Encode_Func), sub_401000(WriteFile). Especially speaking, we can find an API named GetTickCout. Refer to [5]:

    Retrieves the number of milliseconds that have elapsed since the system was started, up to 49.7 days.

    The return value is the number of milliseconds that have elapsed since the system was started

    This seems to be related to file name.

  • Now, we can check sub_401070 first
      void *__cdecl Take_Screenshot(void **a1, _DWORD *a2)
      {
        SIZE_T dwBytes; // ST2C_4
        HGLOBAL hMem; // ST24_4
        void *result; // eax
        char pv; // [esp+Ch] [ebp-6Ch]
        LONG v6; // [esp+10h] [ebp-68h]
        UINT cLines; // [esp+14h] [ebp-64h]
        HGLOBAL v8; // [esp+24h] [ebp-54h]
        void *v9; // [esp+28h] [ebp-50h]
        HDC hdc; // [esp+2Ch] [ebp-4Ch]
        struct tagBITMAPINFO bmi; // [esp+30h] [ebp-48h]
        int v12; // [esp+5Ch] [ebp-1Ch]
        HGDIOBJ h; // [esp+60h] [ebp-18h]
        __int16 v14; // [esp+64h] [ebp-14h]
        SIZE_T v15; // [esp+66h] [ebp-12h]
        int v16; // [esp+6Eh] [ebp-Ah]
        int cy; // [esp+74h] [ebp-4h]
    
        v12 = GetSystemMetrics(0);
        cy = GetSystemMetrics(1);
        hWnd = GetDesktopWindow();
        hDC = GetDC(hWnd);
        hdc = CreateCompatibleDC(hDC);
        h = CreateCompatibleBitmap(hDC, v12, cy);
        SelectObject(hdc, h);
        BitBlt(hdc, 0, 0, v12, cy, hDC, 0, 0, 0xCC0020u);
        GetObjectA(h, 24, &pv);
        bmi.bmiHeader.biSize = 40;
        bmi.bmiHeader.biWidth = v6;
        bmi.bmiHeader.biHeight = cLines;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = 0;
        bmi.bmiHeader.biSizeImage = 0;
        bmi.bmiHeader.biXPelsPerMeter = 0;
        bmi.bmiHeader.biYPelsPerMeter = 0;
        bmi.bmiHeader.biClrUsed = 0;
        bmi.bmiHeader.biClrImportant = 0;
        dwBytes = cLines * 4 * ((32 * v6 + 31) / 32);
        hMem = GlobalAlloc(0x42u, cLines * 4 * ((32 * v6 + 31) / 32));
        bmi.bmiColors[0] = (RGBQUAD)GlobalLock(hMem);
        GetDIBits(hDC, (HBITMAP)h, 0, cLines, *(LPVOID *)bmi.bmiColors, &bmi, 0);
        v16 = 54;
        v15 = dwBytes + 54;
        v14 = 19778;
        v8 = GlobalAlloc(0x42u, dwBytes + 54);
        v9 = GlobalLock(v8);
        memcpy(v9, &v14, 0xEu);
        memcpy((char *)v9 + 14, &bmi, 0x28u);
        memcpy((char *)v9 + 54, *(const void **)bmi.bmiColors, dwBytes);
        GlobalUnlock(hMem);
        GlobalFree(hMem);
        ReleaseDC(hWnd, hDC);
        DeleteDC(hdc);
        DeleteObject(h);
        result = v9;
        *a1 = v9;
        *a2 = dwBytes + 54;
        return result;
      }
    

    In line 22, refer to [6]

    Retrieves a handle to the desktop window. The desktop window covers the entire screen. The desktop window is the area on top of which other windows are painted.

    The return value is a handle to the desktop window.

    These codes aim to take a screenshot on your monitor.

  • Check sub_40181F()
      int __cdecl Encode_Func(int a1, int a2)
      {
        char v3; // [esp+0h] [ebp-44h]
    
        memset(&v3, 0, 0x44u);
        return sub_401739(&v3, (_DWORD *)a1, (int *)a1, a2);
      }
    

    And sub_401739()

      int __cdecl sub_401739(_DWORD *a1, _DWORD *a2, int *a3, unsigned int a4)
      {
        int result; // eax
        unsigned int i; // [esp+0h] [ebp-4h]
    
        for ( i = 0; i < a4; i += 16 )
        {
          sub_4012DD(a1);
          *a3 = (a1[3] << 16) ^ (a1[5] >> 16) ^ *a1 ^ *a2;
          a3[1] = (a1[5] << 16) ^ (a1[7] >> 16) ^ a1[2] ^ a2[1];
          a3[2] = (a1[7] << 16) ^ (a1[1] >> 16) ^ a1[4] ^ a2[2];
          a3[3] = (a1[1] << 16) ^ (a1[3] >> 16) ^ a1[6] ^ a2[3];
          a2 += 4;
          a3 += 4;
          result = i + 16;
        }
        return result;
      }
    

    It used a lot of bit wise XOR and shift after sub_4012DD(a1).

1.5 Trace from the encoding function to the source of the encoded content. What is the content? Ans: The answer is shown as above.

1.6 Can you find the algorithm used for encoding? If not, how can you decode the content? Ans: The answer is shown as Q1.4.

1.7 Using instrumentation, can you recover the original source of one of the encoded files? Ans:

  • Survey many website, I still can not handle Immunity Debugger. The website is as below 開始寫Immunity Debugger PyCommand Immunity Debugger简介 Python筆記—Immunity Debugger
  • So, I can not return the encrypted bmp files to original image that can be visualized. Instead, I used x64dbg to revise encode function that will not allow to be accessed. Then we can get a bmp file that haven’t encrypted. x64dbg result Adding the bmp extension, we get a desktop screenshot image properly. original image

Analyze the RoboAuth sample 2,3: RoboAuth.exe.zip -> RoboAuth.exe

Answer how to resolve the password 1 and password 2 (as shown in below), then generate ‘yourself’ write ups (with screenshots).

  • Step 1:
    • First of all, we used x32dbg to analyze RoboAuth.exe and search some useful string such as You Passed level1! at 0x00401B75. And set the break point at 0x00401B6C(call strcmp). But, I can not recognized the answer like the write up video. So, I used Ollydbg like the video and it showed the compare string right beside the memory window. ↦ r0b0RUlez! ollydbg result string in ollydbg
  • Step 2
    • After entering the password, we can continue to step over(press F8) and it’ll call 0x004015EA at 0x00401B88.
    • Now, you can see an ASCII string above that show "You win. Congratulations!"
    • Set the break point at and enter random string as second password. Then step into(press F7) the function 0x00401547.
  • Step 3
    • Observe the following code first.
        Address   Hex dump                              Command                   
        00401547  /$  55                                PUSH EBP
        00401548  |.  89E5                              MOV EBP,ESP
        0040154A  |.  EB 22                             JMP SHORT 0040156E
        0040154C  |>  8B45 08                           /MOV EAX,DWORD PTR SS:[ARG.1]
        0040154F  |.  0FB610                            |MOVZX EDX,BYTE PTR DS:[EAX]
        00401552  |.  8B45 0C                           |MOV EAX,DWORD PTR SS:[ARG.2]
        00401555  |.  0FB600                            |MOVZX EAX,BYTE PTR DS:[EAX]
        00401558  |.  83F0 02                           |XOR EAX,00000002
        0040155B  |.  38C2                              |CMP DL,AL
        0040155D  |.  74 07                             |JE SHORT 00401566
        0040155F  |.  B8 01000000                       |MOV EAX,1
        00401564  |.  EB 17                             |JMP SHORT 0040157D
        00401566  |>  8345 08 01                        |ADD DWORD PTR SS:[ARG.1],1
        0040156A  |.  8345 0C 01                        |ADD DWORD PTR SS:[ARG.2],1
        0040156E  |>  8B45 0C                           |MOV EAX,DWORD PTR SS:[ARG.2]
        00401571  |.  0FB600                            |MOVZX EAX,BYTE PTR DS:[EAX]
        00401574  |.  3C 02                             |CMP AL,2
        00401576  |.^ 75 D4                             \JNE SHORT 0040154C
        00401578  |.  B8 00000000                       MOV EAX,0
        0040157D  |>  5D                                POP EBP
        0040157E  \.  C3                                RETN
      

      You can see MOVZX EDX,BYTE PTR DS:[EAX] in line 6 and MOVZX EDX,BYTE PTR DS:[EAX] in line 7. In line 6, EAX stored the character you entered. result of register in line 6 And in line 7, it stored a whole new value 0x0060FDBC. Try to trace it in dump. result of register in line 7 You can see ASCII characters u1nnf2lg. Then observe the following code, you’ll see it execute a XOR operation with 0x00000002. And then compare every single character to the real password, that is u1nnf2lg xor 2. value in dump

  • Step 4
    • So, if we want to know what the real password is, we just need to XOR by hand or use some code that [2] provided.
    • The result of the real password is shown as below. ↦ w3lld0ne 2nd real password of lab 2

The flag in this question is `r0b0RUlez!_w3lld0ne`.

Appendix

  1. Install findcrypt-yara Install python 2.7 ↦ add python and python/script to environment variable ↦ follow findcrypt-yara IDA的一个插件安装 and IDA7.0安装findcrypt插件
  2. Install IDA Entropy Plugin Description Install ↦ Install repo. and untar it(Must choose python2 version) ↦ place the .py file to plugin folder ↦ use pip to install matplotlib ↦ restart IDA
  3. Something related to Immunity Debugger

Reference