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
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.
- 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.
- Used another plugin in IDA, Findcrypt, to check if the file has been crypted and it seems that has not been crypted.
- Used text search in search icon
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.
- 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]
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.
Adding the bmp extension, we get a desktop screenshot image properly.
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!
- First of all, we used x32dbg to analyze RoboAuth.exe and search some useful string such as
- 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 andMOVZX EDX,BYTE PTR DS:[EAX]
in line 7. In line 6, EAX stored the character you entered.And in line 7, it stored a whole new value
0x0060FDBC
. Try to trace it in dump.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 isu1nnf2lg xor 2
.
- Observe the following code first.
- 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
The flag in this question is `r0b0RUlez!_w3lld0ne`.
Appendix
- Install findcrypt-yara Install python 2.7 ↦ add python and python/script to environment variable ↦ follow findcrypt-yara IDA的一个插件安装 and IDA7.0安装findcrypt插件
- 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
- Something related to Immunity Debugger