mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
222 lines
11 KiB
Markdown
222 lines
11 KiB
Markdown
# Common API used in Malware
|
||
|
||
{{#include ../banners/hacktricks-training.md}}
|
||
|
||
## Generic
|
||
|
||
### Networking
|
||
|
||
| Raw Sockets | WinAPI Sockets |
|
||
| ------------- | -------------- |
|
||
| socket() | WSAStratup() |
|
||
| bind() | bind() |
|
||
| listen() | listen() |
|
||
| accept() | accept() |
|
||
| connect() | connect() |
|
||
| read()/recv() | recv() |
|
||
| write() | send() |
|
||
| shutdown() | WSACleanup() |
|
||
|
||
### TLS pinning and chunked transport
|
||
|
||
Many loaders wrap their TCP stream in `SslStream` and pin the server’s leaf certificate against an embedded copy (certificate pinning). Bot info/tasks are compressed (e.g., GZip). When responses exceed a threshold (~1 MB), data is fragmented into small chunks (e.g., 16 KB segments) to avoid size-based heuristics and reduce memory spikes during deserialisation.
|
||
|
||
|
||
### Persistence
|
||
|
||
| Registry | File | Service |
|
||
| ---------------- | ------------- | ---------------------------- |
|
||
| RegCreateKeyEx() | GetTempPath() | OpenSCManager |
|
||
| RegOpenKeyEx() | CopyFile() | CreateService() |
|
||
| RegSetValueEx() | CreateFile() | StartServiceCtrlDispatcher() |
|
||
| RegDeleteKeyEx() | WriteFile() | |
|
||
| RegGetValue() | ReadFile() | |
|
||
|
||
### Encryption
|
||
|
||
| Name |
|
||
| --------------------- |
|
||
| WinCrypt |
|
||
| CryptAcquireContext() |
|
||
| CryptGenKey() |
|
||
| CryptDeriveKey() |
|
||
| CryptDecrypt() |
|
||
| CryptReleaseContext() |
|
||
|
||
### Anti-Analysis/VM
|
||
|
||
| Function Name | Assembly Instructions |
|
||
| --------------------------------------------------------- | --------------------- |
|
||
| IsDebuggerPresent() | CPUID() |
|
||
| GetSystemInfo() | IN() |
|
||
| GlobalMemoryStatusEx() | |
|
||
| GetVersion() | |
|
||
| CreateToolhelp32Snapshot \[Check if a process is running] | |
|
||
| CreateFileW/A \[Check if a file exist] | |
|
||
|
||
### Emulator API fingerprinting & sleep evasion
|
||
|
||
Malware often fingerprints sandbox emulators by searching for Defender’s virtualised exports (seen in the Malware Protection Emulator). If any of these symbols are present (case-insensitive scan of the process), execution is delayed for 10–30 minutes and re-checked to waste analysis time.
|
||
|
||
Examples of API names used as canaries:
|
||
- `MpVmp32Entry`, `MpVmp32FastEnter`, `MpCallPreEntryPointCode`, `MpCallPostEntryPointCode`, `MpFinalize`, `MpReportEvent*`, `MpSwitchToNextThread*`
|
||
- `VFS_*` family: `VFS_Open`, `VFS_Read`, `VFS_MapViewOfFile`, `VFS_UnmapViewOfFile`, `VFS_FindFirstFile/FindNextFile`, `VFS_CopyFile`, `VFS_DeleteFile`, `VFS_MoveFile`
|
||
- `ThrdMgr_*`: `ThrdMgr_GetCurrentThreadHandle`, `ThrdMgr_SaveTEB`, `ThrdMgr_SwitchThreads`
|
||
|
||
Typical delay primitive (user-land):
|
||
```cmd
|
||
cmd /c timeout /t %RANDOM_IN_[600,1800]% > nul
|
||
```
|
||
|
||
Argument gatekeeping
|
||
- Operators sometimes require a benign-looking CLI switch to be present before running the payload (e.g., `/i:--type=renderer` to mimic Chromium child processes). If the switch is absent, the loader exits immediately, hindering naive sandbox execution.
|
||
|
||
|
||
### Stealth
|
||
|
||
| Name | |
|
||
| ------------------------ | -------------------------------------------------------------------------- |
|
||
| VirtualAlloc | Alloc memory (packers) |
|
||
| VirtualProtect | Change memory permission (packer giving execution permission to a section) |
|
||
| ReadProcessMemory | Injection into external processes |
|
||
| WriteProcessMemoryA/W | Injection into external processes |
|
||
| NtWriteVirtualMemory | |
|
||
| CreateRemoteThread | DLL/Process injection... |
|
||
| NtUnmapViewOfSection | |
|
||
| QueueUserAPC | |
|
||
| CreateProcessInternalA/W | |
|
||
|
||
### Execution
|
||
|
||
| Function Name |
|
||
| ---------------- |
|
||
| CreateProcessA/W |
|
||
| ShellExecute |
|
||
| WinExec |
|
||
| ResumeThread |
|
||
| NtResumeThread |
|
||
|
||
### Miscellaneous
|
||
|
||
- GetAsyncKeyState() -- Key logging
|
||
- SetWindowsHookEx -- Key logging
|
||
- GetForeGroundWindow -- Get running window name (or the website from a browser)
|
||
- LoadLibrary() -- Import library
|
||
- GetProcAddress() -- Import library
|
||
- CreateToolhelp32Snapshot() -- List running processes
|
||
- GetDC() -- Screenshot
|
||
- BitBlt() -- Screenshot
|
||
- InternetOpen(), InternetOpenUrl(), InternetReadFile(), InternetWriteFile() -- Access the Internet
|
||
- FindResource(), LoadResource(), LockResource() -- Access resources of the executable
|
||
|
||
## Malware Techniques
|
||
|
||
### DLL Injection
|
||
|
||
Execute an arbitrary DLL inside another process
|
||
|
||
1. Locate the process to inject the malicious DLL: CreateToolhelp32Snapshot, Process32First, Process32Next
|
||
2. Open the process: GetModuleHandle, GetProcAddress, OpenProcess
|
||
3. Write the path to the DLL inside the process: VirtualAllocEx, WriteProcessMemory
|
||
4. Create a thread in the process that will load the malicious DLL: CreateRemoteThread, LoadLibrary
|
||
|
||
Other functions to use: NTCreateThreadEx, RtlCreateUserThread
|
||
|
||
### Reflective DLL Injection
|
||
|
||
Load a malicious DLL without calling normal Windows API calls.\
|
||
The DLL is mapped inside a process, it will resolve the import addresses, fix the relocations and call the DllMain function.
|
||
|
||
### Thread Hijacking
|
||
|
||
Find a thread from a process and make it load a malicious DLL
|
||
|
||
1. Find a target thread: CreateToolhelp32Snapshot, Thread32First, Thread32Next
|
||
2. Open the thread: OpenThread
|
||
3. Suspend the thread: SuspendThread
|
||
4. Write the path to the malicious DLL inside the victim process: VirtualAllocEx, WriteProcessMemory
|
||
5. Resume the thread loading the library: ResumeThread
|
||
|
||
### PE Injection
|
||
|
||
Portable Execution Injection: The executable will be written in the memory of the victim process and it will be executed from there.
|
||
|
||
### Process Hollowing (a.k.a **RunPE**)
|
||
|
||
`Process Hollowing` is one of the favourite **defence-evasion / execution** tricks used by Windows malware. The idea is to launch a *legitimate* process in the **suspended** state, remove (hollow) its original image from memory and copy an **arbitrary PE** in its place. When the primary thread is finally resumed the malicious entry-point executes under the guise of a trusted binary (often signed by Microsoft).
|
||
|
||
Typical workflow:
|
||
|
||
1. Spawn a benign host (e.g. `RegAsm.exe`, `rundll32.exe`, `msbuild.exe`) **suspended** so that no instructions run yet.
|
||
```c
|
||
STARTUPINFOA si = { sizeof(si) };
|
||
PROCESS_INFORMATION pi;
|
||
CreateProcessA("C:\\Windows\\Microsoft.NET\\Framework32\\v4.0.30319\\RegAsm.exe",
|
||
NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
|
||
```
|
||
2. Read the malicious payload into memory and parse its PE headers to obtain `SizeOfImage`, sections and the new `EntryPoint`.
|
||
3. **NtUnmapViewOfSection** / **ZwUnmapViewOfSection** – unmap the original image base of the suspended process.
|
||
4. **VirtualAllocEx** – reserve RWX memory of `SizeOfImage` inside the remote process.
|
||
5. **WriteProcessMemory** – copy the `Headers` first, then iterate over sections copying their raw data.
|
||
6. **SetThreadContext** – patch the value of `EAX/RAX` (`RCX` on x64) or `Rip` in the context structure so that `EIP` points to the payload’s `EntryPoint`.
|
||
7. **ResumeThread** – the thread continues, executing the attacker-supplied code.
|
||
|
||
Minimal proof-of-concept (x86) skeleton:
|
||
```c
|
||
void RunPE(LPCSTR host, LPVOID payload, DWORD payloadSize){
|
||
// 1. create suspended process
|
||
STARTUPINFOA si = {sizeof(si)}; PROCESS_INFORMATION pi;
|
||
CreateProcessA(host, NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);
|
||
|
||
// 2. read remote PEB to get ImageBaseAddress
|
||
CONTEXT ctx; ctx.ContextFlags = CONTEXT_FULL;
|
||
GetThreadContext(pi.hThread,&ctx);
|
||
PVOID baseAddr;
|
||
ReadProcessMemory(pi.hProcess,(PVOID)(ctx.Ebx+8),&baseAddr,4,NULL);
|
||
|
||
// 3. unmap original image & allocate new region at same base
|
||
NtUnmapViewOfSection(pi.hProcess,baseAddr);
|
||
PVOID newBase = VirtualAllocEx(pi.hProcess,baseAddr,pHdr->OptionalHeader.SizeOfImage,
|
||
MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
|
||
// 4-5. copy headers & sections …
|
||
// 6. write new image base into PEB and set Eip
|
||
WriteProcessMemory(pi.hProcess,(PVOID)(ctx.Ebx+8),&baseAddr,4,NULL);
|
||
ctx.Eax = (DWORD)(newBase) + pHdr->OptionalHeader.AddressOfEntryPoint;
|
||
SetThreadContext(pi.hThread,&ctx);
|
||
// 7. run!
|
||
ResumeThread(pi.hThread);
|
||
}
|
||
```
|
||
|
||
Practical notes observed in the **DarkCloud Stealer** campaign:
|
||
|
||
* The loader picked `RegAsm.exe` (part of the .NET Framework) as host – a signed binary unlikely to draw attention.
|
||
* The decrypted VB6 stealer (`holographies.exe`) is *not* dropped on disk; it only ever exists inside the hollowed process making static detection harder.
|
||
* Sensitive strings (regexes, paths, Telegram credentials) are **RC4-encrypted** per-string and only decrypted at runtime, further complicating memory scanning.
|
||
|
||
Detection ideas:
|
||
* Alert on `CREATE_SUSPENDED` processes that never create GUI/console windows before a memory region is allocated as **RWX** (rare for benign code).
|
||
* Look for a call sequence `NtUnmapViewOfSection ➜ VirtualAllocEx ➜ WriteProcessMemory` across different processes.
|
||
|
||
|
||
|
||
## Hooking
|
||
|
||
- The **SSDT** (**System Service Descriptor Table**) points to kernel functions (ntoskrnl.exe) or GUI driver (win32k.sys) so user processes can call these functions.
|
||
- A rootkit may modify these pointer to addresses that he controls
|
||
- **IRP** (**I/O Request Packets**) transmit pieces of data from one component to another. Almost everything in the kernel uses IRPs and each device object has its own function table that can be hooked: DKOM (Direct Kernel Object Manipulation)
|
||
- The **IAT** (**Import Address Table**) is useful to resolve dependencies. It's possible to hook this table in order to hijack the code that will be called.
|
||
- **EAT** (**Export Address Table**) Hooks. This hooks can be done from **userland**. The goal is to hook exported functions by DLLs.
|
||
- **Inline Hooks**: This type are difficult to achieve. This involve modifying the code of the functions itself. Maybe by putting a jump at the beginning of this.
|
||
|
||
|
||
## References
|
||
|
||
- [Unit42 – New Infection Chain and ConfuserEx-Based Obfuscation for DarkCloud Stealer](https://unit42.paloaltonetworks.com/new-darkcloud-stealer-infection-chain/)
|
||
- [Check Point Research – Under the Pure Curtain: From RAT to Builder to Coder](https://research.checkpoint.com/2025/under-the-pure-curtain-from-rat-to-builder-to-coder/)
|
||
|
||
{{#include ../banners/hacktricks-training.md}}
|
||
|
||
|
||
|