How Code Injection really works

Discussion in 'CYBER SECURITY awareness!' started by kram7750, Jul 15, 2017.

  1. kram7750

    kram7750 Member Known Member

    Hello everybody.

    This knowledge is useful when dealing with malware analysis since it'll help you to identify DLL injection patterns/log API calls and filter for code injection attacks.

    One of the most common methods of DLL Injection usually evolves around Win32 API functions. Due to this method requiring a dependency, it means that it is not as stealth as code injection which will require none, but often DLL injection will not actually be blocked by most typical AV solutions (based on my personal testing).

    The most common way for executing this technique would be to: start by opening a handle to the target process; allocating memory to store the DLL file path; writing to the newly allocated memory to actually store the DLL file path; creating a remote thread targeting the handle of the target process to call LoadLibraryA (and then for the argument you use the file path allocated within the target process' memory).

    The Win32 API functions which can be used to carry out this task:
    - kernel32.dll!OpenProcess
    - kernel32.dll!GetProcAddress & kernel32.dll!LoadLibraryA/W
    - kernel32.dll!VirtualAllocEx
    - kernel32.dll!WriteProcessMemory
    - kernel32.dll!CreateRemoteThread

    The Native API (NTAPI) equivelent of the functions listed above would be the following:

    - ntdll.dll!NtOpenProcess
    - ntdll.dll!LdrGetProcedureAddress (although I recommend just walking the EAT yourself to get the address and making a custom wrapper for LoadLibraryA too)
    - ntdll.dll!NtAllocateVirtualMemory
    - ntdll.dll!NtWriteVirtualMemory
    - ntdll.dll!RtlCreateUserThread / ntdll.dll!NtCreateThreadEx

    When LoadLibraryA (or the Unicode version which replaces the A for W) is called, it will end up at a function called LdrLoadDll (exported by ntdll.dll). That is the Native API equivelent function for DLL loading, you can use the function by setting up the function structure with a typedef, getting the address of LdrLoadDll via GetProcAddress (or alternatively a custom GetProcAddress to walk the Export Address Table yourself) and then initializing it to the address of the function in memory. Due to LdrLoadDll being the important function for this process, you can actually set a local hook on the function which would allow you to block DLL injection attacks occurring through the LoadLibraryA remote thread injection method - the remote thread that becomes created within your process will call LoadLibraryA which means this is called within the targeted process, therefore a local hook on LdrLoadDll will intercept the attempt of DLL injection for this particular strategy.

    Using the Native API for DLL injection (a code injection attack) does provide benefits which the Win32 API simply cannot provide and one of these is the ability to inject code into a process running on another user account. Even if your process is running as administrator and you've acquired debugging rights (SeDebugPrivilege), you'll find out that you won't be able to create a remote thread with CreateRemoteThread whilst targeting a process running on another user account such as SYSTEM (NT Authority Account) (like lsass.exe, csrss.exe, winlogon.exe, etc.). Alternatively however, using RtlCreateUserThread would allow you to create a remote thread in a process running on another user account. When RtlCreateUserThread is called it will lead back to the NtCreateThreadEx stub within ntdll.dll however it is not required to use NtCreateThreadEx... RtlCreateUserThread is fine and you're bound to run into more work with the latter.

    There is another technique of DLL injection which is a lot more stealth known as "manual map injection", and this evolves around loading a DLL within a target process without it being linked to the Process Environment Blocks' ModulesList (preventing it from being detected simply by enumerating through the modules loaded within the process). To be more stealth using the common method, you could inject shell-code injection and then load the DLL, and even repair any hooks on functions like LdrLoadDll which were set locally prior to loading any DLLs.

    Another technique I have not mentioned here would be APC (Asynchronous Procedure Calls). This works similar in the way that you target a thread of a process and then use it with a function like QueueUserAPC (which will then end up at NtQueueApcThread) to force the target process to make a call to LoadLibraryA/W or just go for LdrLoadDll to start with.

    To sum up this thread:
    - The most common method of DLL injection evolves around forcing a target process to call LoadLibraryA to load the target DLL.
    - A more stealth method of DLL injection is manual mapping which will prevent the DLL from being added to the ModulesList in the PEB (Process Environment Block).
    - You can block simple DLL injection attacks through locally hooking ntdll.dll!LdrLoadDll.

    Thank you for reading.
    daljeet, jasonX, Gabbs and 7 others like this.
  2. Google Adsense

  3. RGiskardR

    RGiskardR Malware Tester Silver Member

    Really very interesting and informative post! thanks!
    daljeet, wwd, guardian and 5 others like this.
  4. revC0de

    revC0de MTAC Moderator Staff Member

    Thanks for analyzing this topic, really interesting! :)
    daljeet, wwd, RGiskardR and 4 others like this.
  5. kram7750

    kram7750 Member Known Member

    Sorry for digging this thread up back from the grave but I stumbled back across it and found a mistake I'd like to address.

    If you wanted to be stealthier you would not need to scan LdrLoadDll for any hooks because genuine manual map injection does not rely on LdrLoadDll, it relies on the internal functions that LdrLoadDll will call for you such as LdrpLoadDll. However, functions like LdrpLoadDll (internally called functions by LdrLoadDll) are not exported by Windows modules like ntdll.dll and thus you'd have to find the address via another method.

    To be stealthier in terms of preventing deployment interception, you could either:
    1. Deploy the manual map injection from kernel-mode (inject shell-code and then have the shell-code actually executed by creating a new thread with ZwCreateThreadEx or an Asynchronous Procedure Call (APC) - the shell-code will be the manual map loader).

    2. Utilise NTAPI system calls (for 32-bit processes running on a 64-bit environment you can use Heavens Gate since Wow64 can be intercepted) from user-mode during the injection deployment (e.g. to bypass user-mode patching techniques).

    daljeet, guardian, wwd and 2 others like this.

Share This Page