Malware Analysis Code injection identification [Malware Analysis]

Discussion in 'Malware Analysis' started by Opcode, Sep 20, 2017.

  1. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    #21 Opcode, Sep 30, 2017
    Last edited: Sep 30, 2017
    I'd like to add some small brief additional information.

    There are two kernel-mode only undocumented functions called KeInitializeApc and KeInsertQueueApc. They can be used to perform APC injection from kernel-mode. You can setup IOCTL support to receive a Process Id and then enumerate through its threads (or receive a thread ID and open a handle to the thread with that ID), or alternatively register a kernel-mode callback called PsSetCreateThreadNotifyRoutine and based on filtering, apply the thread for APC injection. In fact, if you use the kernel-mode callback to perform APC injection within its callback routine, you'll be able to automatically inject into new processes as they start up via APC (because every new process gets a main thread and your callback would be invoked for this, allowing you to apply APC injection for the main thread of the new process... You can do checks to make sure its a new process though). If that is too tricky or for whatever reason you dislike that, hook NtResumeThread in user-mode and connect to the driver so it will perform the injection (NtResumeThread can be used with filtering to detect a new process start-up since the main thread will need to be resumed for the new process to execute its code).

    In kernel-mode you have access to ZwAllocateVirtualMemory, however you won't have access to ZwWriteVirtualMemory by default (ZwWriteVirtualMemory is not exported by ntoskrnl.exe). However, you can still find the address of ZwWriteVirtualMemory from the System Service Dispatch Table (both on x86 and x64 systems). If you don't want to bother finding the address of ZwWriteVirtualMemory you can use wcscpy instead (which is what the Carberp device driver did for APC injection). You could use RtlCopyMemory (which is the same as memcpy just renamed, check its definition) instead if you prefer, however if you want to use wcscpy or memcpy you'll need to call KeStackAttachProcess first so you can get into the context of the target process... Remember to detach with KeUnstackDetachProcess though!

    KeInitializeApc - can be accessed after using extern for its definition:
    Code:
    // thanks Google.. hehe
    extern NTSTATUS NTAPI KeInitializeApc(
        struct _KAPC *Apc,
        PKTHREAD thread,
        unsigned char state_index,
        PKKERNEL_ROUTINE ker_routine,
        PKRUNDOWN_ROUTINE rd_routine,
        PKNORMAL_ROUTINE nor_routine,
        unsigned char mode,
        void *context
    );
    
    KeInsertQueueApc - can be accessed the same way as KeInitializeApc:
    Code:
    extern NTSTATUS NTAPI KeInsertQueueApc(
        struct _KAPC *APC,
        void *SysArg1,
        void *SysArg2,
        unsigned char arg4
    );
    
    The following will be needed:
    Code:
    // (I didn't write the structures myself, I found them online)
    typedef VOID(NTAPI *PKNORMAL_ROUTINE)(
        PVOID NormalContext,
        PVOID SystemArgument1,
        PVOID SystemArgument2
        );
    
    typedef VOID KKERNEL_ROUTINE(
        PRKAPC Apc,
        PKNORMAL_ROUTINE *NormalRoutine,
        PVOID *NormalContext,
        PVOID *SystemArgument1,
        PVOID *SystemArgument2
    );
    
    typedef KKERNEL_ROUTINE(NTAPI *PKKERNEL_ROUTINE);
    
    typedef VOID(NTAPI *PKRUNDOWN_ROUTINE)(
        PRKAPC Apc
        );
    
    You can allocate space for and write shell-code to the target process via ZwAllocateVirtualMemory and NtWriteVirtualMemory/wcscpy/memcpy which will be for a manual map loader/LdrLoadDll, or get the address to kernel32.dll!LoadLibraryA/W and go with the normal routine but from kernel-mode via APC. You can write a GetModuleHandle replacement in kernel-mode to get the address of a module imported by a target process (ZwQueryInformationProcess may be useful here) and then an export scanner to get the address of the target function exported by the found module to get the address of kernel32.dll!LoadLibraryA/W or even simpler, just have the address sent to your driver via IOCTL when it is started because the address for LoadLibraryA/W will be the same for all running programs that import kernel32.dll (all will import except native programs like smss.exe) due to how the Windows loader works.

    If you need access to ZwQueryInformationProcess... Here is some example code I've just written to post here:
    Code:
    typedef NTSTATUS(NTAPI *pZwQueryInformationProcess)(
        HANDLE ProcessHandle,
        PROCESSINFOCLASS ProcessInformationClass,
        PVOID ProcessInformation,
        ULONG ProcessInformationLength,
        PULONG ReturnLength
        );
    
    pZwQueryInformationProcess fZwQueryInformationProcess;
    
    NTSTATUS NtSetupMemory()
    {
        NTSTATUS NtStatus = STATUS_SUCCESS;
        PVOID pvFunctionAddress = 0;
        UNICODE_STRING usFunctionName;
    
        if (!fZwQueryInformationProcess)
        {
            RtlInitUnicodeString(&usFunctionName, L"ZwQueryInformationProcess");
    
            pvFunctionAddress = MmGetSystemRoutineAddress(&usFunctionName);
    
            if (!pvFunctionAddress)
            {
                return STATUS_UNSUCCESSFUL;
            }
    
            fZwQueryInformationProcess = (pZwQueryInformationProcess)pvFunctionAddress;
        }
    
        return NtStatus;
    }
    
    I wrote the above function the way I did so you can keep using RtlInitUnicodeString to change the function name and then setup other functions also.

    Thanks. :)
     
  2. Andy Ful

    Andy Ful Level 21

    Dec 23, 2014
    1,086
    4,645
    business
    Poland
    Windows 10
    Microsoft
    There are so many different ways of doing something in Windows. Actually cannot decide, if I should be happy or maybe hackers should.:alien:
     
  3. tim one

    tim one Level 18
    Trusted AV Tester

    Jul 31, 2014
    889
    8,998
    Europe
    Windows 10
    Emsisoft
    Good point and it depends on how skill there is in the game.
    For example: malcoders want to obfuscate/hide strings in their malware to make reversing more difficult and usually the more common approach is to encrypt them somehow and put them inside binary buffers instead of plain ASCII strings.
    One downside of this naive approach is of course, once decompiled, the access to these binary buffers will easily be noticed by a good reverser, he would assume some sort of obfuscation/encryption/whatever and start reversing the algorithm to unobfuscate the strings in a matter of minutes.
     
  4. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    Update to details.

    Bad approach, apologies. I'll explain a better approach which is more realistic and useful.

    Since you would already be in kernel-mode, register a callback to PsSetLoadImageNotifyRoutine. When you detect a new module from within the callback routine, store the address if it is a module you are interested in (e.g. ntdll.dll, kernel32.dll, etc.). You can use the base address of the module to retrieve the address of exported functions within that targeted module, and then you can store the address for future reference (e.g. when APC injection really needs to occur).

    I have not checked what approach various security software take for automatic injection, however my guess would be that most probably make use of their PsSetCreateThreadNotifyRoutine callback as an advantage to deploy APC injection which relies on shell-code. Process start-up interception is typically handled via kernel-mode callback to PsSetCreateProcessNotifyRoutine/Ex these days, however you should not try to attempt injection from the callback routine of this function because it is not safe to perform virtual memory operations with the target at this point... However, with the callback routine to PsSetCreateThreadNotifyRoutine, it is for a thread notification belonging to a process which already fully exists and therefore virtual memory operations are much more reliable and stable.

    You don't have to rely on APC injection from kernel-mode, you can create a remote thread instead however it is a lot more work to do this. ZwCreateThreadEx is not accessible by default, so you'd have to find the address yourself by accessing the System Service Dispatch Table.

    Generally speaking though, injecting code into running processes comes with many potential problems. Some security software may do this for whatever reason (e.g. Sandboxie does it), but if possible it is best to avoid doing this - rely on kernel-mode callbacks and other documented methods as much as you can, it will save you a lot of trouble during security software development. It can ruin performance and stability a lot if it is not handled properly by someone with experience with the topic.

    You'll have to do extensive testing if you are injecting code for purposes such as API hooking in-case of unexpected errors...

    Microsoft do offer an API hooking library called MS Detours - there is a free version labelled as Express which only supports 32-bit processes whereas the paid version provides 64-bit process support (but is not freely purchasable by anyone and I would expect it costs a lot).

    There are open-source API hooking libraries but as far as I know they can not be used for commercially paid software. I am not sure, I've never really looked at them. Could be beneficial for a free security product looking to be expanded for more capabilities though.

    Kernel-Mode callbacks are much more secure than user-mode hooking if implemented correctly anyway usually, it depends on the circumstances really (e.g. recently a team managed to find a vulnerability in the PsSetLoadImageNotifyRoutine callback to block security software from identifying modules for scanning properly, whereas a local hook on NtCreateSection would not have been affected... At the same time, a local hook on NtCreateSection could have been bypassed by a manual DLL loader which relied on system calls - rare but still possible).

    Edit: Edited the SPOILER contents by expanding on some notes here and there.
     
    venustus, frogboy and tim one like this.
  5. Andy Ful

    Andy Ful Level 21

    Dec 23, 2014
    1,086
    4,645
    business
    Poland
    Windows 10
    Microsoft
    I like this fragment very much. :)
     
    frogboy and Opcode like this.
  6. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    Me too. Using undocumented functions can cause future instability when things are changed in Windows and injecting code to detour APIs and what-not comes with its own set of problems... Case-by-case basis for what problems may be encountered.

    If someone has access to the professional (paid) version of MS Detours (no idea how much it costs because I've never had the opportunity to purchase a license even if I could afford the price myself), then using this with code injection will probably reduce a lot of potential problems as long as callbacks are handled properly, considering Microsoft know what they are doing and they aren't going to mess up with a library for doing something like that.

    Even then, the developer must understand how it works to use it correctly. You don't do API hooking without knowing how it works or have at least some experience, you'll eventually run into a problem and you'll require knowledge and/or experience to resolve it.

    Generally speaking though, using undocumented functions or anything similar in that sense can cause huge instability with compatibility for future updates so it is crucial the developer is aware for the future. I still remember the problems many security products had with the Windows 7 to Windows 8 transition, and also Windows 8 to Windows 10... :oops:
     
    frogboy and Andy Ful like this.
  7. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    #27 Opcode, Sep 30, 2017
    Last edited: Sep 30, 2017
    I guess at the end of the day, security software typically utilises various undocumented functions/methods to gain more control over the system and work-around limitations put in place, or perform actions which are rare to see/the OS does internally.

    For example:
    1. NtSuspendProcess is an undocumented function but might be used to suspend a process so its not running its code temporarily (AFAIK Windows will internally enumerate the threads and suspend them, because you cannot suspend a process... The threads get suspended = process suspended state).
    2. Undocumented kernel-mode only functions to provide more usability when performing specific operations (e.g. NtSetInformationProcess is not documented by Microsoft, other functions like NtSetSystemInformation aren't either, but can be very useful in some cases, etc.).
    3. Undocumented kernel-mode callbacks. There are quite a few. Some of them can be used for boot-time scanning of newly loaded device drivers and what-not. (I believe some were made specifically for Windows Defender but just were not documented for others originally, but still exported by ntoskrnl.exe so can be used without having to manually find addresses, etc.).
    4. Hyper-Visor stuff. Of course there is Intel/AMD documentation as well though.
    Many different things can be brain stormed to apply for examples.

    Generally speaking though, stick to documented as much as you can as the likelihood is things staying positive in terms of stability and compatibility.
     
    Andy Ful and frogboy like this.
  8. Andy Ful

    Andy Ful Level 21

    Dec 23, 2014
    1,086
    4,645
    business
    Poland
    Windows 10
    Microsoft
    A good examples of such an approach are Excubits programs (Bouncer, Memprotect, etc.).
     
    Sunshine-boy, frogboy and Opcode like this.
  9. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    I've never tested either but I've heard of Memprotect. The developer is over on Wilders Security or am I thinking of someone else? I assume it uses kernel-mode callbacks.
     
  10. Andy Ful

    Andy Ful Level 21

    Dec 23, 2014
    1,086
    4,645
    business
    Poland
    Windows 10
    Microsoft
    I did not see the developer on WildersSecurity, but @WildByDesign and @WindowsSecurity are in contact with him.
     
    Sunshine-boy, vemn and Opcode like this.
  11. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    My mistake, 100% I was thinking about something else (cannot even remember what developer/app I was thinking of then). :unsure:
     
    Sunshine-boy, vemn, frogboy and 2 others like this.
  12. vemn

    vemn Level 6
    AV Tester

    Feb 11, 2017
    269
    1,238
    IT SYSADMIN
    Singapore
    Thanks for the knowledge!
    And thanks @Umbra and @Opcode for sharing what can be protected.

    Just curious if anyone can elaborate if any of the enterprise AVs' features can protect against these too (pardon my limited knowledge):
    - i.e. Trend Micro OfficeScan's ATSE or its machine learning?
    - SEP 14's new features?
    - Cylance's Pre-execution machine learning able to? Hearing alot of it on how strong it is.
    - PAN Traps' way of exploit kit identification (before v4.0) able to do it?
    - McAfee ENS (Dynamic Application Containment and ML)?
    - CrowdStrike?
    - Kaspersky?

    For my understanding so that I can handle my ops requirement/recommendation at times. =)
    Hope someone can advise me on this.

    regards
     
    Sunshine-boy, frogboy and Opcode like this.
  13. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    I cannot comment on the products based on testing with the exception of Kaspersky. I haven't tested the end-point security solutions I'm afraid.

    Kaspersky have a feature in their Internet Security product called Application Control. It is a restriction-based feature which will prevent a program from performing specific operations depending on the configuration tied to different layers of Trust Levels. If a program is untrusted and the configuration states that the program should be unable to write to the memory of another process, then it won't be able to (as an example). I cannot comment on any end-point/business solutions, but I would imagine they would offer such a feature in their end-point protection or at least a replacement.

    Regarding machine learning/Ai detection for static scanning (before the sample executes any malicious code), there is no doubt that it can be beneficial. However, typically speaking, it is far from full-proof just like everything else in this world. Pre-execution isn't going to block code injection attacks dynamically based on behavior. Why? The reason being is because the block occurs before the sample has a chance to attempt to inject code... Therefore it isn't dynamic behavioural prevention.
     
  14. tim one

    tim one Level 18
    Trusted AV Tester

    Jul 31, 2014
    889
    8,998
    Europe
    Windows 10
    Emsisoft
    Fully agree, an "intelligent algorithm" for static analysis should have the ability to find concrete indicators of impairment just by analyzing imports and functions of the malicious code.
    As @Opcode says, it is far enough to be always reliable.
    As I already said, it does not exist (at least today) an algorithm that is absolutely capable of deciding whether a file is malware or not.
     
    harlan4096, frogboy, Opcode and 2 others like this.
  15. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    The only thing is that with shell-code you must be null-free with the bytes.

    I've made a demonstration on shell-code execution/injection here: Tutorial - Shellcode execution (C++ & ASM)
     
  16. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    Update.

    Avast are getting much better with preventing code-injection attacks via their Behavior Shield component; I did some testing the other day and it managed to block DLL injection via NTAPI functions (not system-calls or any NTDLL tricks - just normal via remote thread creation). I am yet to test code-cave/shell-code though. Nevertheless, it caused a block and showed an on-screen alert in the centre with a nice slick UI to alert me of the blocked attack - it was 100% not signature-based but dynamic based.

    I did some quick tests and they are injecting into monitored processes and detouring API functions to control execution flow as well now, but I am sure they are using kernel-mode for functionality as well in the Behavior Shield (maybe even for the DLL injection prevention).

    Nice to see them improving with dynamic, I recall both Avast and AVG (now owned by Avast) were awful with blocking code injection entirely awhile ago. Maybe AVG has improved with it too since they now work with Avast.
     
  17. Opcode

    Opcode Level 18
    Content Creator

    Aug 17, 2017
    890
    6,293
    Caille
    Windows 10
    Update.

    You can prevent global system-wide hooks (e.g. SetWindowsHookEx) from affecting GUI applications by starting them up on another desktop environment. There are several documented Win32 API functions for creating a new desktop and starting a process under the new Desktop. You'll want to look into functions such as CreateDesktop and CreateProcessAsUser. SetWindowsHookEx will only affect programs which are the same architecture as the program deploying the injection attack, and have user32.dll loaded. The real functionality is all handled in win32k.sys.

    It is not unknown that LdrpLoadDll (NTDLL) is not exported, therefore there is no "official" way to locate the address. It is possible to scan the bytes of the LdrLoadDll function to retrieve the function address, but this is not reliable because the function prologue (the stub) for LdrLoadDll may change at any given time with a patch update.

    If you wish to prevent manual map DLL injection, your best bet is to prevent thread and APC injection (generally speaking). Manual map injection rarely makes use of LdrpLoadDll anyway (since it is not exported), therefore the attacker would manually load the DLL into memory by allocating enough space and copying the sections from the DLL into the allocated memory, handling operations such as address/import resolving and eventually calling the DllMain function (the entry point of the DLL).

    There is a function, KiUserApcDispatcher. It is invoked within your own process when APC is performed, targeting a thread from within your own process. You can locally detour it and block it from working depending on the conditions based on checks from parameters. It must be handled correctly otherwise you will potentially break functionality of your software. Even if APC is performed from kernel-mode via KeInitializeApc and KeInsertApcQueue, this function will still be invoked, allowing you to block the injection. However, forget about blocking kernel-mode attacks from user-mode like this anyway considering nothing would stop the attacker from "unhooking" the function from kernel-mode via attaching to the address space of your process and cleaning up the hook, or just using a work-around technique.

    There is another function, LdrInitializeThunk. It is invoked within your own process for every single thread creation operation... You can locally hook it to block thread creation depending on the conditions. Be just as cautious fiddling around with local hooks for this function than with KiUserApcDispatcher, because if it isn't handled correctly you will cause problems within your own program. If you block every single call to it, then you'll block genuine thread creation within your program which needs to occur to execute your own genuine code.

    -------------
    Universal Windows Programs (UWP) are more secure against remote code execution by default due to their sandboxing (AppContainer) mechanism. This explains why Microsoft Edge is more secure than other web-browsers; even if Google Chrome is enabled for AppContainer, I am pretty sure it still won't be as safe as Microsoft Edge with this (Google Chrome does a good job with preventing code injection though - they just need to verify loaded DLLs at start-up and unload unwanted ones if any are found, for example).

    With this being said, I can confirm that manual mapping will allow you to inject any DLL into UWP applications. This can include the Windows Store application, the UWP Calculator (calc.exe -> Windows 8 and on-wards is Metro-based) application, or even Microsoft Edge. The explanation for why manual mapping still works is because the RCE (Remote Code Execution) mitigation is only valid for DLL injection through common methods such as trying to create a remote thread with a start address of LoadLibraryA. The task of actually creating a thread in itself is not blocked. Therefore, as long as you can open a handle to the UWP target process with the necessary access rights required to perform virtual memory operations and thread creation, you'll be able to perform shell-code/code-cave injection which can proceed to manual map a DLL.

    In-case anyone needs to inject code into a UWP process (e.g. for security software development purposes, whatever the reason), remember to use manual mapping for the UWP applications otherwise it simply won't work. You can always try injecting shell-code which will get the address of LdrLoadDll from the Export Address Table and then try using that to load the DLL normally, however this ensures more internal checks are performed about your DLL and chances are it would be blocked. In fact, the "Shim" hooks I was talking about in the past on this very same thread I believe in the comments, the engine setup routines for it is actually initialised for dynamically loaded DLLs via functions like LdrLoadDll and LdrpLoadDll. Therefore, manual mapping a DLL should also remove such "Shim" hook restrictions... This is not always a good thing though, since Shims are used for compatibility reasons as well. Be cautious of it all, none of these things are officially supported.

    Thanks for reading the update. :)
     
    harlan4096, Andy Ful, tim one and 3 others like this.
  18. Sunshine-boy

    Sunshine-boy Level 22

    Apr 1, 2017
    1,169
    5,185
    IRAN
    Windows 10
    ESET
    Opcode pls test Eset Hips in interactive mode, Sophos home premium and Q360 for having more fun:giggle:will you do that?
     
  19. Andy Ful

    Andy Ful Level 21

    Dec 23, 2014
    1,086
    4,645
    business
    Poland
    Windows 10
    Microsoft
    Thanks.:)
    What do you mean by manual map DLL? It looks like reflective DLL injection, but some things have to be done manually? If so, then would it be possible to make a reflective DLL loader to inject into a UWP process?
     
  20. tim one

    tim one Level 18
    Trusted AV Tester

    Jul 31, 2014
    889
    8,998
    Europe
    Windows 10
    Emsisoft
    @Opcode, I'm just reading my old stuff :)

    So yes, using raw system calls instead of normal high level API is obviously unusual and unnecessarily uncomfortable, except if you want to evade some monitoring/hooking software and obtain a basic code obfuscation.
    The problem here is on every Windows version too syscall numbers may vary and it seems there is no official way to obtain the correct values for the system your application is running.
    By “official ways” I mean we can always extract those values from the ntdll library we can find during runtime.
    But if we can load it with a simple LoadLibrary, obtain a pointer to the API we are interested ( let’s say NtCreateFile ) with GetProcAddress and examine the first opcodes in it, can we could achieve the same result without loading the library but manually inspecting its content as a portable executable file?

    Correct me, can we easily open ntdll.dll, inspect its export directory, obtain the RVA and raw offset of NtCreateFile and then check the first bytes of opcodes?

    Thank you :)
     
    XhenEd, frogboy, Opcode and 1 other person like this.
Loading...