# Simple Reverse - 0x20(2023 Lab - WinMalware - Extract Next Stage Payload - 2) ## Background * [CreateToolhelp32Snapshot](https://learn.microsoft.com/zh-tw/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot) * [Process32FirstW](https://learn.microsoft.com/zh-tw/windows/win32/api/tlhelp32/nf-tlhelp32-process32firstw) * [GetCurrentProcess](https://learn.microsoft.com/zh-tw/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess) * [OpenProcess](https://learn.microsoft.com/zh-tw/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess) * [EqualSid](https://learn.microsoft.com/zh-tw/windows/win32/api/securitybaseapi/nf-securitybaseapi-equalsid) * [Process32NextW](https://learn.microsoft.com/zh-tw/windows/win32/api/tlhelp32/nf-tlhelp32-process32nextw) * [OpenProcessToken](https://learn.microsoft.com/zh-tw/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken) * [GetTokenInformation](https://learn.microsoft.com/zh-tw/windows/win32/api/securitybaseapi/nf-securitybaseapi-gettokeninformation) ## Source code * sub_1400016B0 :::spoiler IDA Source Code解析前 ```cpp __int64 returnTargetPid() { DWORD LastError; // eax DWORD v2; // eax WCHAR *szExeFile; // rax signed __int64 v4; // rcx WCHAR v5; // dx int v6; // eax DWORD th32ProcessID; // [rsp+20h] [rbp-288h] HANDLE hSnapshot; // [rsp+28h] [rbp-280h] HANDLE hObject; // [rsp+30h] [rbp-278h] HANDLE CurrentProcess; // [rsp+38h] [rbp-270h] PSID pSid1; // [rsp+40h] [rbp-268h] BYREF PSID pSid2; // [rsp+48h] [rbp-260h] BYREF PROCESSENTRY32W pe; // [rsp+50h] [rbp-258h] BYREF hSnapshot = CreateToolhelp32Snapshot(2u, 0); if ( hSnapshot == (HANDLE)-1i64 ) { LastError = GetLastError(); sub_140001260("CreateToolhelp32Snapshot failed with error %lu\n", LastError); return 0i64; } else { pe.dwSize = 568; if ( Process32FirstW(hSnapshot, &pe) ) { pSid2 = malloc(0x44ui64); CurrentProcess = GetCurrentProcess(); sub_140001500(CurrentProcess, &pSid2); th32ProcessID = 0; do { pSid1 = malloc(0x44ui64); hObject = OpenProcess(0x400u, 0, pe.th32ProcessID); if ( hObject ) { if ( (unsigned int)sub_140001500(hObject, &pSid1) ) { if ( EqualSid(pSid1, pSid2) ) { szExeFile = pe.szExeFile; v4 = (char *)L"msedge.exe" - (char *)pe.szExeFile; while ( 1 ) { v5 = *szExeFile; if ( *szExeFile != *(WCHAR *)((char *)szExeFile + v4) ) break; ++szExeFile; if ( !v5 ) { v6 = 0; goto LABEL_14; } } v6 = v5 < *(WCHAR *)((char *)szExeFile + v4) ? -1 : 1; LABEL_14: if ( !v6 ) th32ProcessID = pe.th32ProcessID; } free(pSid1); } CloseHandle(hObject); } } while ( !th32ProcessID && Process32NextW(hSnapshot, &pe) ); free(pSid2); CloseHandle(hSnapshot); return th32ProcessID; } else { v2 = GetLastError(); sub_140001260("Process32First failed with error %lu\n", v2); CloseHandle(hSnapshot); return 0i64; } } } ``` ::: ## Recon 1. 首先,他先利用`CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)`把當前系統中所有的process都snapshot,並回傳指定快照集的開啟控制碼(handle) 2. 在[MSDN](https://learn.microsoft.com/zh-tw/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot#parameters)中有提到 > 若要列舉process,請參閱 [Process32FirstW](https://learn.microsoft.com/zh-tw/windows/win32/api/tlhelp32/nf-tlhelp32-process32firstw) 所以的確,該function底下就有使用到這個API(#27),主要目的是擷取系統snapshot中所遇到的第一個process相關資訊 3. 接著進到`sub_140001500`中看一下,如果不看其他exception handler的話其實蠻簡單的 1. 也就是他會先取得`currentProcess`的token handle(暫時不需要知道handle是啥),然後再取得儲存資料所需要的buffer size,這邊很tricky的地方是,在#14的地方原本是設定`TokenInformationLength = 0`,而後面呼叫的`GetTokenInformation`就一定會報錯,但他只是想知道TokenInformation的length為多少,所以當他執行完#15行之後,`&TokenInformationLength`會儲存Length,而我可以利用報錯error code(也就是0x7A, `ERROR_INSUFFICIENT_BUFFER`),進到if statement中,然後利用得到的length再malloc一個空間,做後續的操作 ![](https://hackmd.io/_uploads/ByZ4tHpMp.png) 2. 再次呼叫GetTokenInformation 取得資料,此時因為我們已經明確知道需要多大的空間了,所以就不會再報錯了,此時才能真正的取得資料 ![](https://hackmd.io/_uploads/SJoEKSpfT.png) 3. 將User SID 複製到a2這個變數 :::spoiler ```cpp __int64 __fastcall sub_140001500(void *a1, PSID *a2) { DWORD v2; // eax DWORD v4; // eax DWORD v5; // eax DWORD LastError; // eax PSID *TokenInformation; // [rsp+30h] [rbp-28h] DWORD TokenInformationLength; // [rsp+38h] [rbp-20h] BYREF HANDLE TokenHandle; // [rsp+40h] [rbp-18h] BYREF TokenHandle = 0i64; if ( OpenProcessToken(a1, 0x20008u, &TokenHandle) ) { TokenInformationLength = 0; GetTokenInformation(TokenHandle, TokenUser, 0i64, 0, &TokenInformationLength); if ( GetLastError() == 122 ) { TokenInformation = (PSID *)malloc(TokenInformationLength); if ( TokenInformation ) { if ( GetTokenInformation( TokenHandle, TokenUser, TokenInformation, TokenInformationLength, &TokenInformationLength) ) { if ( CopySid(0x44u, *a2, *TokenInformation) ) { if ( !IsValidSid(*a2) ) sub_140001260("Sid is invalid\n"); free(TokenInformation); CloseHandle(TokenHandle); return 1i64; } else { LastError = GetLastError(); sub_140001260("CopySid failed, %d\n", LastError); return 0i64; } } else { v5 = GetLastError(); sub_140001260("GetTokenInformatoin failed, %d\n", v5); CloseHandle(TokenHandle); return 0i64; } } else { CloseHandle(TokenHandle); return 0i64; } } else { v4 = GetLastError(); sub_140001260("GetTokenInformatoin 1 failed, %d\n", v4); CloseHandle(TokenHandle); return 0i64; } } else { v2 = GetLastError(); sub_140001260("OpenProcessToken failed, %d\n", v2); return 0i64; } } ``` ::: 4. 回到`sub_1400016B0`,可以看到#33~#67是一個do_while loop,該loop就是把現在存取的process再打開(利用PID指定),並取得他的handle,然後找到該process的User SID,再去比對前一個步驟取得的User SID和現在取得的User SID有沒有一樣,如果一樣就做==#43~#57==(其實就是`memcmp("msedge.exe", process’s executable file name)`),他會去比對目前的這一支程式的filename是不是`msedge.exe`,如果這個Edge Process的User SID和目前的current User SID一樣且`memcmp`也回傳是,就return PID :::info 簡略流程如下: 1. snapshot目前所有的process 2. 取得目前執行這支程式(A)的User SID 3. 遍歷snapshot中所有的process,如果遍歷的process(B)的User SID和剛剛取得的一樣就再memcmp,看目前的這支程式(B)是不是msedge.exe,如果是就回傳PID,若否就再遍歷下一個process(B') :::