Malware can inject code into other processes by writing into target processes memory space. This enabled the malware to spy on the victim activities. It also enable the malware to infect processes from alerting the malware by cloaking the rootkit. To identify this functionality using code level involves examining the sample API calls.
What should the rootkit write into the beginning of the targeted function? One option is to insert a JMP instruction that specifies the relative address of the malicious code that the hooked function will execute. The hex value of this instruction begins with 0xE9. Sometimes the rootkit uses a sneakier approach to perform a jump: It might insert the PUSH instruction (0x68) followed by the RET instruction (0xC3). Push places the address of the jump’s destination to the stack. RET is typically used to return from a function, but it accomplishes this by popping an address from the top of the stack and jumping there. As a result, PUSH followed by RET can be used as a jump.
Assuming that a function was successfully hooked. When the infected process attempts to invoke it, the patched function will execute rootkit code. That code will typically want to invoke the original version of the function; otherwise, it might crash and behave unexpected. What the rootkit can do is backing up the overwritten function, it can execute the original function using the backup instructions. It can there after jump into the hooked function just past the hook portion and continue executing the remainder of the function. The effort of executing the original bytes and then jumping into the function to execute the rest of its code is called a trampoline.
Malware often uses the API call ReadProcessMemory to read the targeted function’s contents, and using VirtualProtect If necessary, to set permissions of memory where the targeted function resides to writeable. WriteProcessMemory to overwrite the start of the targeted function with jump into the rootkit. There are multiple method of achieving this a common method is to insert a JMP to a relative address. Another is to implement a jump using PUSH to push the destination address to the stack followed by RET to pop the address and jump there. When examining malicious code, look for the combination of calls to ReadProcessMemory near calls to WriteProcessMemory to spot potential API hooking instructions.
Here are some of the common techniques usage of windows built-in API:
malapi can help identify what each API does. They have a list of the commonly used API calls by malware. Below is a collection of how API calls can be strung together to achieve difference goals.
Code injection
Allowing a program to inject code into it own process or another process, depending if it uses the Ex version of virtualAlloc.
- List of running processes
- CreatToolhelp32Snapshot or EnumProcesses
- Open handle to target processes
- OpenProcess
- Allocates memory space in the child process for the injected coded
- VirtualAlloc(Ex)
- Inject code into the newly allocated memory of the child process
- WriteProcessMemory
- Resumes the process to run the code.
- CreateRemoteThread
Process hollowing
This will carve out the virtual memory of a process. The memory segment then gets written to with the payload and executed.
- List of running processes
- CreatToolhelp32Snapshot or EnumProcesses
- Lunch process with suspended flag
- CreateProcesss
- Hollow out the virtual memory of a process
- NtUnmapViewOfSection or ZwUnmapViewOfSection
- Allocates memory space in the child process for the injected coded
- VirtualAlloc(Ex)
- Inject code into the newly allocated memory of the child process
- WriteProcessMemory
- Resume the process to run the code.
- ResumeThread
Helper
Dynamic loading API
Some malware authors will load libraries dynamically in real-time using the LoadLibraryA to find the necessary API needed for the software to run.
- Get kernel32.dll
- GetModuleHandle
- Loadlibary in Kernel32
- GetProcAddress
- Call using the address returned from LoadLibrary
- CreateRemoteTread
Processes iteration
Iterate all the running processes on the system.
- List of running processes
- CreatToolhelp32Snapshot or EnumProcesses
- Get the first processes from the list returned by CreateToolhelp32Snapshot
- Process32FirstW
- Get the next process in the list
- Process32NextW
Virtual project manipulation
It is common to set the new memory to location protection to RWX[40], Another way it first set the protection level to RW[04] and once the code has been written change the protection again to RX[20] to avoid AV detection
Obfuscation techniques
Structured Exception handling (SEH)
Structured Exception Handling (SEH) is a mechanism for handling errors. It enables the programmer to define functions that Windows executes if an unexpected error (“exception”) occurs.
In C+++ it can be implemented using a __try and __except block.
SEH record in a 32-bit program is comprised of two memory addresses. The first is a pointer to an exception-handling function and the other points to the previous SEH record. This allows Windows to maintain a linked list “Chain” of SEH components.
The official name is the _EXCEPTION_REGISTRATION structure. This structure allows the operating system to determine the address of the exception-handling function. The first record is stored at the address pointed to by the FS register, which points to the Thread information block (TIB). It contains information about the currently running thread. The SEH chain is the beginning of this structure at offset 0. In the assembly code, any reference will look like FS:[0]. This will allow you to identify when samples interact with the SEH chain.
64-bit uses a table-based approach that defines the program exception handlers (http://blog.talosintel.com/2014/06/exceptional-behavior-windows-81-x64-seh.html). in the assembly code, it will look like GS[0]
Malware are use this function to bypass return pointer protection, it is also used as a form of anti-debugging and anti-disassembling, by obfuscating the flow of execution. This is done by overwriting the SEH record on the stack, making it part of the execution flow by forcing an exception to happen.
Assembly code of changing the SEH chain structure, by changing where fs points to.
#Push the current start of the SEH chain to the stack
push dword ptr fs:[0]
#Set the start of the chain FS:[0] to point to a new SEH structure
mov dword ptr fs:[0],esp
Another method is instead of changing to a new SEH structure is also possible to just change the pointer. Change the pointer of edx to the address that is stored inside EAX. Where the new address is where the most recently defined SEH handler stores the address of its SEH handler function.
mov dword ptr ds:[edx], eax
By modifying that pointer, the specimen changed the address of that SEH handler function from the one that exists in ntdll.dll to the one that the malware author created, it will now execute the new code, when the sample hit an exception.
Threat local storage (TLS) callback
Thread Local Storage (TLS) is a mechanism that allows each thread of a process their own value for a variable, as long it’s stored inside TLS. keeping variable value local to the thread.
THE TSL callback function allows a sample to execute code even before it hit the entry point, the intent is too confusing analysts or tools that at not aware of TLS callback. Xdbg will by default stop at TLS callback.\ PEStudio can also be used to identify if a sample will perform TLS callbacks
bypass analysis defense
Decode obfuscated strings statically using FLARE, xorsearch, Balbuzard, etc.
Decode data in a debugger by setting a breakpoint after the decoding function and examining results.
If analyzing shellcode, use scdbg and jmp2it.
Process Environment block (PEB)
The Process Environment Block (PEB) is a data structure in the Windows operating system that contains information about a running process, such as its allocated memory, loaded modules, and runtime environment. The PEB is typically stored in memory and is accessed by the operating system and other processes to retrieve information about the process. The PEB is an important component of the Windows operating system, as it provides the operating system and other processes with the information they need to manage and interact with the process. The PEB is used by the operating system to allocate memory, load modules, and control the process’s execution, among other tasks. It is also used by other processes, such as debuggers and security software, to gain insight into the process’s behavior and to monitor or control its execution.
Malware authors can access the PEB using these operations:
- fs:[30] for 32bit
- GS:[0x60] for 64bit
This allows the malware to get information about the process-loaded modules, their base address, and exported function addresses. They can use this information to calculate the export addresses of APIs and thereby make calls to them. thereby not needing to call GetProcAddress to get the address of the API.
Another thing the PEB can be used for is checking if IsDebugging is enabled.
mov eax, fs[30]
mov eax [eax+2]
test eax,eax
Another is using NtGlobalFlag check to check if the program is launched by a debugger. As it contains the flag 0x70, when the process is launched by a debugger. The NtGlobalFlag is at offset 0x68 from the PEB in a 32-bit process and 0xBC in a 64-bit process.
mov eax, dword ptr fs:[30]
test byte ptr ds:[eax+68], 70
DLL files
Analyzing DLL is similar to PE files, The difference between the two, is instead of having one exported function. DLL can have multiple export functions, where each performs a unique action, one malicious and the other benign. Sandbox can sometimes have a hard time analyzing DLL because each exported function might need to be executed because only one will trigger the malware.
To debug the other function of DLL files, I presume that you have disabled ASLR on the sample.
- The first step is to locate the entry point of the different functions, this can be done using any PE analyzer.
- Now load the DLL file into a xdbg, and run until it hit the dll entry point.
- Now change the EIP so it points to the entry point of the exported function, that was identified in step 1 that you want to debug. (Ensure the base address as in step 1.)