{"id":13840489,"url":"https://github.com/wbenny/injdrv","last_synced_at":"2025-05-16T03:05:18.339Z","repository":{"id":41203269,"uuid":"107860962","full_name":"wbenny/injdrv","owner":"wbenny","description":"proof-of-concept Windows Driver for injecting DLL into user-mode processes using APC","archived":false,"fork":false,"pushed_at":"2024-05-01T13:30:40.000Z","size":411,"stargazers_count":1209,"open_issues_count":16,"forks_count":288,"subscribers_count":49,"default_branch":"master","last_synced_at":"2025-04-08T13:11:56.756Z","etag":null,"topics":["apc","hooking","injection","windows-driver"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wbenny.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-22T11:45:29.000Z","updated_at":"2025-04-08T07:49:49.000Z","dependencies_parsed_at":"2024-09-30T17:00:40.941Z","dependency_job_id":"c7619cf6-54a3-49a8-8492-abc802a8923c","html_url":"https://github.com/wbenny/injdrv","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbenny%2Finjdrv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbenny%2Finjdrv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbenny%2Finjdrv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbenny%2Finjdrv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wbenny","download_url":"https://codeload.github.com/wbenny/injdrv/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254459088,"owners_count":22074605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apc","hooking","injection","windows-driver"],"created_at":"2024-08-04T17:00:49.482Z","updated_at":"2025-05-16T03:05:13.330Z","avatar_url":"https://github.com/wbenny.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"# injdrv\n\ninjdrv is a proof-of-concept Windows Driver for injecting DLL into user-mode processes using APC.\n\n### Motivation\n\nEven though [APCs][apc] are [undocumented to decent extent][inside-apc], the technique of using them to inject a DLL\ninto a user-mode process is not new and has been talked through many times. Such APC can be queued from regular\nuser-mode process (seen in [Cuckoo][apc-cuckoo]) as well as from kernel-mode driver (seen in [Blackbone][apc-blackbone]).\n\nDespite its popularity, finding small, easy-to-understand and actually working projects demonstrating usage of this\ntechnique isn't very easy. This project tries to fill this gap.\n\n### Features\n\n- Support for **Windows 7** up to **Windows 10**\n- Support for **x86**, **x64**, **ARM32** \u0026 **ARM64** architectures\n- Ability to inject **Wow64** processes\n    - With DLL of the same architecture as the **injected process** (e.g. **x86 DLL into x86 Wow64 process**)\n    - With DLL of the same architecture as the **OS** (e.g. **x64 DLL into Wow64 process on Windows x64**)\n- DLL is injected in very early process initialization stage\n    - Injection is performed from the `PsSetLoadImageNotifyRoutine` callback\n    - Native processes (e.g. x86 on Windows x86, x64 on Windows x64, ...) are injected on next load of DLL after `ntdll.dll`\n    - Wow64 processes are injected on next load of DLL after the Wow64-DLLs are loaded\n- Because of that, injected DLL must depend only on `ntdll.dll`\n- Demonstrative DLL performs hooking of few `ntdll.dll` functions\n    - Achieved using [DetoursNT][DetoursNT]\n- Detoured functions use `ETW` to trace hooked function calls\n\n### Compilation\n\nBecause [DetoursNT][DetoursNT] project is attached as a git submodule, which itself carries the [Detours][Detours]\ngit submodule, you must not forget to fetch them:\n\n`git clone --recurse-submodules git@github.com:wbenny/injdrv.git`\n\nAfter that, compile this project using Visual Studio 2017. Solution file is included. The only required dependency\nis [WDK][wdk].\n\n### Implementation\n\nWhen the driver is loaded, it'll register two callbacks:\n- For process create/exit notification ([`PsSetCreateProcessNotifyRoutineEx`][MSDN-CreateProcessNotify])\n- For image load notification ([`PsSetLoadImageNotifyRoutine`][MSDN-LoadImageNotify])\n\nWhen a new process is created, the driver allocates small structure, which will hold information relevant to the process\ninjection, such as:\n- Which DLLs are already loaded in the process\n- Addresses of important functions (such as `LdrLoadDll` in `ntdll.dll`)\n\nStart of a new Windows process is followed by mapping `ntdll.dll` into its address space and then ongoing load of DLLs\nfrom the process's import table. In case of **Wow64** processes on Windows x64, the following libraries are loaded\nimmediately after native `ntdll.dll`: `wow64.dll`, `wow64cpu.dll`, `wow64win.dll` and second (Wow64) `ntdll.dll`.\nThe driver is notified about load of these DLLs and marks down this information.\n\nWhen these DLLs are loaded, it is safe for the driver to queue the user-mode APC to the process, which will load our\nDLL into the process.\n\n### Challenges\n\nAlthough such project might seem trivial to implement, there are some obstacles you might be facing along the way.\nHere I will try to summarize some of them:\n\n#### \"Thunk\"-method\n\n_This method injects DLL of the same architecture as the process. This method is available on all architectures._\n\nInjection of DLL requires a small allocation inside of the user-mode address space. This allocation\nholds path to the DLL to be injected and a small thunk (shellcode), which basically calls `LdrLoadDll` with the DLL path as\na parameter. It is obvious that this memory requires `PAGE_EXECUTE_READ` protection, but the driver has to fill this\nmemory somehow - and `PAGE_EXECUTE_READWRITE` is unacceptable security concern.\n\nIt might be tempting to use `ZwAllocateVirtualMemory` and `ZwProtectVirtualMemory` but unfortunatelly, the second\nfunction is exported only since Windows 8.1.\n\nThe solution used in this driver is to create section ([`ZwCreateSection`][MSDN-CreateSection]), map it\n([`ZwMapViewOfSection`][MSDN-MapViewOfSection]) with `PAGE_READWRITE` protection, write the data, unmap it\n([`ZwUnmapViewOfSection`][MSDN-UnmapViewOfSection]) and then map it again with `PAGE_EXECUTE_READ` protection.\n\nWith usage of sections another problem arises. Since this driver performs injection from the image load notification\ncallback - which is often called from the `NtMapViewOfSection` function - we'd be calling `MapViewOfSection`\nrecursively. This wouldn't be a problem, if mapping of the section wouldn't lock the `EPROCESS-\u003eAddressCreationLock`.\nBecause of that, we would end up in deadlock.\n\nThe solution used in this driver is to inject **kernel-mode APC** first, from which the `ZwMapViewOfSection` is called.\nThis kernel-mode APC is triggered right before the kernel-to-user-mode transition, so the internal `NtMapViewOfSection`\ncall won't be on the callstack anymore (and therefore, `AddressCreationLock` will be unlocked).\n\nInjection of our DLL is triggered on first load of DLL which happens after all important system DLLs (mentioned above)\nare already loaded.\n\nIn case of native processes, the codeflow is following:\n- `process.exe` is created (process create notification)\n- `process.exe` is loaded (image load notification)\n- `ntdll.dll` is loaded (image load notification)\n- `kernel32.dll` is loaded (image load notification + **injection happens here**)\n\nIn case of Wow64 processes, the codeflow is following:\n- `process.exe` is created (process create notification)\n- `process.exe` is loaded (image load notification)\n- `ntdll.dll` is loaded (image load notification)\n- `wow64.dll` is loaded (image load notification)\n- `wow64cpu.dll` is loaded (image load notification)\n- `wow64win.dll` is loaded (image load notification)\n- `ntdll.dll` is loaded (image load notification - note, this is 32-bit ntdll.dll)\n- `kernel32.dll` is loaded (image load notification + **injection happens here**)\n\n\u003cbr\u003e\n\n\u003e **NOTE:** Load of the `kernel32.dll` was used as an example. In fact, load of any DLL will trigger the injection.\n\u003e But in practice, `kernel32.dll` is loaded into **every Windows process**, even if:\n\u003e - it has no import table\n\u003e - it doesn't depend on `kernel32.dll`\n\u003e - it does depend only on `ntdll.dll` (covered in previous point, I just wanted to make that crystal-clear)\n\u003e - it is a console application\n\u003e\n\u003e Also note that the order of loaded DLLs mentioned above might not reflect the exact order the OS is performing.\n\u003e\n\u003e The only processes that won't be injected by this method are:\n\u003e - native processes (such as `csrss.exe`)\n\u003e - pico processes (such as applications running inside [Windows Subsystem for Linux][WSL])\n\u003e \n\u003e Injection of these processes is not in the scope of this project.\n\u003e\n\u003e **NOTE:** On Windows 7, the Wow64 loads `kernel32.dll` and `user32.dll` (both native and Wow64) into the process.\n\u003e Unfortunatelly, this load is performed in the initialization of Wow64 (by `wow64!ProcessInit`), therefore on Windows 7\n\u003e we have to wait until these DLLs are loaded as well before injecting a Wow64 process.\n\nThe injected user-mode APC is then force-delivered by calling `KeTestAlertThread(UserMode)`. This call internally\nchecks if any user-mode APCs are queued and if so, sets the `Thread-\u003eApcState.UserApcPending` variable to `TRUE`.\nBecause of this, the kernel immediately delivers this user-mode APC (by `KiDeliverApc`) on next transition from\nkernel-mode to user-mode.\n\n\u003e If we happened to not force the delivery of the APC, the APC would've been delivered when the thread would be in\n\u003e the alertable state. (There are two alertable states per each thread, one for kernel-mode, one for user-mode;\n\u003e this paragraph is talking about `Thread-\u003eAlerted[UserMode] == TRUE`.) Luckily, this happens when the Windows loader\n\u003e in the `ntdll.dll` finishes its job and gives control to the application - particularly by calling `NtAlertThread`\n\u003e in the `LdrpInitialize` (or `_LdrpInitialize`) function.  So even if we happened to not force the APC, our DLL would\n\u003e still be injected before the main execution would take place.\n\u003e\n\u003e **NOTE:** This means that if we wouldn't force delivery of the APC on our own, the APC would be delivered **BEFORE**\n\u003e the `main`/`WinMain` is executed, but **AFTER** all [TLS callbacks][TLS-callbacks] are executed. This is because\n\u003e TLS callbacks are executed also in the early process initialization stage, within the `LdrpInitialize` function.\n\u003e\n\u003e This behavior is configurable in this project by the `ForceUserApc` variable (by default it's `TRUE`).\n\u003e\n\u003e **NOTE:** Some badly written drivers try to inject DLL into processes by queuing APC at wrong time. For example:\n\u003e - Queuing an APC for injecting DLL that **doesn't depend only on ntdll.dll** right when **ntdll.dll** is mapped\n\u003e - Queuing an APC for injecting DLL that **depends on kernel32.dll** right when **kernel32.dll** is mapped (but not loaded!)\n\u003e\n\u003e Such injection will actually work as long as someone won't try to forcefully deliver user-mode APCs. Because this\n\u003e driver triggers immediate deliver of user-mode APCs (**all of them**, you can't pick which should be delivered),\n\u003e it might happen that APC of other driver will be triggered. If such APC consisted, let's say, of calling\n\u003e `LoadLibraryA` from `kernel32.dll` and the `kernel32.dll` won't be fully loaded (just mapped), such APC would fail.\n\u003e And because this injection happens in early process initialization stage, this error would be considered critical\n\u003e and the process start would fail. Also because basically every process is being injected, if start of every process\n\u003e would fail, it would make the system very unusable.\n\nThe reason why our DLL is not injected immediately from the `ntdll.dll` image load callback is simple: the image\nload callback is called when the DLL is mapped into the process - and at this stage, the DLL is not fully initialized.\nThe initialization takes place **after** this callback (in user-mode, obviously). If we would happen to inject\n`LdrLoadDll` call before `ntdll.dll` is initialized, the call would fail somewhere in that function, because some\nvariable it relies on would not be initialized.\n\nInjection of **Wow64** processes is handled via `PsWrapApcWow64Thread(\u0026NormalContext, \u0026NormalRoutine)` call. This\nfunction essentially alters provided arguments in a way (not covered here) that `KiUserApcDispatcher` in native\n`ntdll.dll` is able to recognize and handle such APCs differently. Handling of such APCs is internally resolved\nby calling `Wow64ApcRoutine` (from `wow64.dll`). This function then emulates queuing of \"32-bit APC\" and resumes\nits execution in `KiUserApcDispatcher` in the Wow64 `ntdll.dll`.\n\n#### \"Thunkless\"-method\n\n_This method injects x64 DLL into both x64 (native) and x86 (Wow64) processes. This method is available only on Windows x64._\n\nInjection of **x64 DLL** into **Wow64** processes is tricky on its own, and **SentinelOne** wrote an excellent 3-part\nblogpost series on how to achieve that:\n- https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-1\n- https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-2\n- https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-3\n\nIn short, if you try to use the same approach as with \"thunk\"-method for injecting x64 DLL into Wow64 process,\nyou will run into problems with [Control Flow Guard][MSDN-CFG] on Windows 10.\n- On x64 system, CFG maintains 2 bitmaps for Wow64 processes\n  - One for \"x86 address space\" (used when checking execution of \u003c 4GB memory)\n  - One for \"x64 address space\" (used when checking execution of \u003e= 4 GB memory)\n- You cannot allocate memory in \u003e 4GB range (even from the kernel-mode), because of [VAD][VAD] that reserves this\n  memory range\n  - You can theoretically unlink such VAD from `EPROCESS-\u003eVadRoot` and decrement `EPROCESS-\u003eVadCount`, but that's\n    highly unrecommended\n- That means, when you allocate memory inside of Wow64 process (even from the kernel-mode) or change its protection,\n  the _x86 CFG bitmap_ is used.\n- x64 `ntdll.dll` is mapped **above** 4GB, therefore, the `KiUserApcDispatcher` function is also located in \u003e 4GB\n  address.\n- Before `KiUserApcDispatcher` calls (indirectly) the `NormalRoutine` provided to the `KeInitializeApc` function,\n  it checks whether `NormalRoutine` can be executed via CFG\n- Because `KiUserApcDispatcher` is called from \u003e 4GB address, this CFG check is performed on _x64 CFG bitmap_, but\n  this check will fail, because the allocated memory of ours is in \u003c 4GB memory\n  - You can theoreticaly work around this by disabling the CFG with various hacks, but that's also highly\n    unrecommended\n- `ZwProtectVirtualMemory` and even `ZwSetInformationVirtualMemory` won't help you, because these APIs will operate\n  on _x86 CFG bitmap_ as well, if you feed them with \u003c 4GB address\n\nThe solution outlined in the **SentinelOne** blogpost rests in calling `LdrLoadDll` of x64 `ntdll.dll` directly\nfrom the user APC dispatcher - effectively, making `NormalRoutine` point to the address of the `LdrLoadDll`.\nThe issue here is that `PKNORMAL_ROUTINE` takes only 3 parameters, while `LdrLoadDll` takes 4.\n\n```c\ntypedef\nVOID\n(NTAPI *PKNORMAL_ROUTINE) (\n  _In_ PVOID NormalContext,\n  _In_ PVOID SystemArgument1,\n  _In_ PVOID SystemArgument2\n  );\n\nNTSTATUS\nNTAPI\nLdrLoadDll (\n  _In_opt_ PWSTR SearchPath,\n  _In_opt_ PULONG DllCharacteristics,\n  _In_ PUNICODE_STRING DllName,\n  _Out_ PVOID *BaseAddress\n  );\n```\n\n  Note that 4th parameter of the `LdrLoadDll` **must** point to some valid address, where the `BaseAddress` will\n  be stored. The devil is always in the details - the solution takes advance of \"couple of lucky coincidences\":\n- `KiUserApcDispatcher` is a function expecting `RSP` to point to the `CONTEXT` structure\n- From this structure, values `P1Home` ... `P4Home` are fetched:\n  - `P1Home` (moved to `RCX`) represent `NormalContext`\n  - `P2Home` (moved to `RDX`) represent `SystemArgument1`\n  - `P3Home` (moved to `R8`) represent `SystemArgument2`\n  - `P4Home` (moved to `RAX`) represent `NormalRoutine`\n  - Also, `R9` is set to point to the `RSP` (the `CONTEXT` structure)\n  - Note that `RCX`, `RDX`, `R8` and `R9` are used as first four function parameters in [Microsoft x64 calling convention][x64-abi]\n\n  \u003cp align=\"center\"\u003e\n    \u003cimg src=\"img/KiUserApcDispatcher.png\" /\u003e\n  \u003c/p\u003e\n\n- `KiUserApcDispatcher` calls `KiUserCallForwarder`\n  - `KiUserCallForwarder` checks whether `RAX` points to valid execution target (in _x64 CFG bitmap_)\n  - `KiUserCallForwarder` calls function pointed by `RAX` with parameters `RCX`, `RDX`, `R8` and `R9`\n  - This is basically equivalent of calling APC's `PKNORMAL_ROUTINE`\n    - `NormalRoutine(NormalContext, SystemArgument1, SystemArgument2)`\n  - ...except that, because `R9` is set, it is in fact called like this:\n    - `NormalRoutine(NormalContext, SystemArgument1, SystemArgument2, ContinueContext)`\n\n  \u003cp align=\"center\"\u003e\n    \u003cimg src=\"img/KiUserCallForwarder.png\" /\u003e\n  \u003c/p\u003e\n\n- Therefore, if we queue the user-mode APC like this:\n  - `NormalRoutine` = address of `LdrLoadDll` in 64-bit `ntdll.dll`\n  - `NormalContext` = `NULL` (translates to 1st param. of `LdrLoadDll` (`SearchPath`))\n  - `SystemArgument1` = `NULL` (translates to 2nd param. of `LdrLoadDll` (`DllCharacteristics`))\n  - `SystemArgument2` = pointer to `UNICODE_STRING DllName` (translates to 3rd param. of `LdrLoadDll` (`DllName`))\n  - (as mentioned above, the 4th parameter (`BaseAddress`) will be provided automatically by the `KiUserApcDispatcher`)\n- ...it will effectively result in the following call: `LdrLoadDll(NULL, 0, \u0026DllName, \u0026ContinueContext)`\n- `LdrLoadDll` overwrites first 8 bytes of the `CONTEXT` structure, which happens to be its `P1Home` field\n- It doesn't break anything, because this field has been already used (when fetching `NormalContext`) and is\n  no longer accessed (not even by `ZwContinue`)\n\n\u003e **NOTE:** Not all function calls from x86 NTDLL end up in x64 NTDLL. This is because some functions are fully\n\u003e implemented on its own in both x86 and x64 NTDLL. This applies mainly on functions that does not require any\n\u003e syscall - i.e. `Rtl*` functions. For example, if you wanted to hook `RtlDecompressBuffer` in Wow64 process,\n\u003e hooking that function in x64 NTDLL wouldn't have any effect and such hooked function would be never called.\n\u003e\n\u003e **NOTE:** Because of differences in APC-dispatching mechanism, this method is not possible to use on **x86** or\n\u003e **ARM64** Windows.\n\n#### \"wow64log.dll reparse\"-method\n\n_This method injects native DLL into all processes. This method is available on all architectures._\n\nWhen **Wow64** process is starting, the `wow64.dll` tries to load `wow64log.dll`. This DLL is never present in\nregular Windows installation (it's probably used internally by Microsoft for debugging of the Wow64 subsystem).\nTherefore, load of this DLL will normally fail. This isn't problem, though, because no critical functionality of\nthe Wow64 subsystem depends on it. If the load actually succeeds, it tries to find following exported functions\nin the DLL:\n- `Wow64LogInitialize`\n- `Wow64LogMessageArgList`\n- `Wow64LogSystemService`\n- `Wow64LogTerminate`\n\nIf one of these functions is not exported by the DLL, the DLL is immediately unloaded.\n\nIf we drop custom `wow64log.dll` (which exports functions mentioned above) into the `%SystemRoot%\\System32` directory,\nit gets loaded into every **Wow64 process**.\n\nFor more details, this method is greatly described by [Walied Assar][wow64log]\n\nThe actual injection of **Wow64** processes by **injdrv** is handled via redirection of `wow64log.dll` path to the\npath of our native DLL. This redirection is solved via filter driver, which registers `IRP_MJ_CREATE` pre-callback.\nWhen this pre-callback detects that the `wow64log.dll` file is being opened, it replaces the path in the `FILE_OBJECT`\nby using [`IoReplaceFileObjectName`][MSDN-IoReplaceFileObjectName] function and returning `STATUS_REPARSE` in the\n`IO_STATUS_BLOCK`. The code of the filter driver is entirely based on [SimRep][SimRep] example found in Microsoft's\nWDK examples.\n\n\u003e **NOTE:** Because native processes do not load `wow64.dll`, **injdrv** injects them using **\"thunk\"-method** when\n\u003e **\"wow64log.dll reparse\"-method** is selected.\n\u003e\n\u003e **NOTE:** Because `wow64.dll` itself is compiled for native architecture, the `wow64log.dll` must be also native.\n\n#### Protected processes\n\nInjection of protected processes is simply skipped, as it triggers code-integrity errors. Such processes are detected\nby the `PsIsProtectedProcess` function. If you're curious about workaround of this issue (by temporarily unprotecting\nthese processes), you can peek into [Blackbone source code][blackbone-unprotect-process]. Keep in mind that\nunprotecting protected processes requires manipulation with undocumented structures, which change dramatically\nbetween Windows versions.\n\n#### ETW logging\n\nFinally, as mentioned in the beginning, the injected DLL performs logging of hooked functions with ETW.\nBecause functions such as `EventRegister`, `EventWriteString`, ... are located in the `advapi32.dll`, we can't\nuse them from our NTDLL-only dependent DLL. Luckily, ETW support is hardwired in the `ntdll.dll` too. In fact,\nmost of the `Event*` functions in the `advapi32.dll` are simply redirected to the `EtwEvent*` functions in `ntdll.dll`\nwithout any change to the arguments! Therefore, we can simply mock the `Event*` functions and just include the\n`\u003cevntprov.h\u003e` header:\n\n```c\n//\n// Include support for ETW logging.\n// Note that following functions are mocked, because they're\n// located in advapi32.dll.  Fortunatelly, advapi32.dll simply\n// redirects calls to these functions to the ntdll.dll.\n//\n\n#define EventActivityIdControl  EtwEventActivityIdControl\n#define EventEnabled            EtwEventEnabled\n#define EventProviderEnabled    EtwEventProviderEnabled\n#define EventRegister           EtwEventRegister\n#define EventSetInformation     EtwEventSetInformation\n#define EventUnregister         EtwEventUnregister\n#define EventWrite              EtwEventWrite\n#define EventWriteEndScenario   EtwEventWriteEndScenario\n#define EventWriteEx            EtwEventWriteEx\n#define EventWriteStartScenario EtwEventWriteStartScenario\n#define EventWriteString        EtwEventWriteString\n#define EventWriteTransfer      EtwEventWriteTransfer\n\n#include \u003cevntprov.h\u003e\n```\n\n...easy, wasn't it?\n\n### Usage\n\n\u003e Following example is performed on **Windows 10 x64**\n\nEnable [Test-Signing][test-signing] boot configuration option (note that you'll need administrative privileges to use\n`bcdedit`) and reboot the machine:\n\n```\nbcdedit /set testsigning on\nshutdown /r /t 0\n```\n\nNow open administrator command line and run following command:\n\n`injldr -i`\n\nThe `-i` option installs the driver. After the driver is installed, it waits for newly created processes.\nWhen a new process is created, it is hooked. Prepare some **x86** application, for example, [PuTTY][putty] and run it.\nWith **Process Explorer** we can check that indeed, our x64 DLL is injected in this x86 application.\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"img/injection.png\" /\u003e\n\u003c/p\u003e\n\nAlso, immediately after `injldr` is started, it starts an ETW tracing session and prints out information\nabout called hooked functions:\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"img/injldr.png\" /\u003e\n\u003c/p\u003e\n\nYou can exit `injldr` by pressing `Ctrl+C`. Now you can run `injldr` without any parameters to just start\nthe tracing session. If you wish to uninstall the driver, run `injldr -u`.\n\n\u003e This driver by default uses following injection methods:\n\u003e - `InjMethodThunk` on Windows x86\n\u003e - `InjMethodThunkless` on Windows x64\n\u003e - `InjMethodWow64LogReparse` on Windows ARM64\n\u003e\n\u003e Therefore, it always tries to inject native DLL into all processes, including Wow64 processes. If you wish to\n\u003e change this behavior and e.g. inject x86 DLL into x86 Wow64 process, set injection method to `InjMethodThunk`.\n\u003e Also, do not forget to compile `injdll` for the corresponding architectures and place it in the same directory\n\u003e as `injldr.exe`.\n\n### License\n\nThis software is open-source under the MIT license. See the LICENSE.txt file in this repository.\n\nDependencies are licensed by their own licenses.\n\nIf you find this project interesting, you can buy me a coffee\n\n```\n  BTC 3GwZMNGvLCZMi7mjL8K6iyj6qGbhkVMNMF\n  LTC MQn5YC7bZd4KSsaj8snSg4TetmdKDkeCYk\n```\n\n  [apc]: \u003chttps://docs.microsoft.com/en-us/windows/desktop/sync/asynchronous-procedure-calls\u003e\n  [inside-apc]: \u003chttp://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590\u003e\n  [apc-cuckoo]: \u003chttps://github.com/cuckoosandbox/monitor/blob/7c5854fae12e1f01f56eab2db4008148c790cc7a/bin/inject.c#L375\u003e\n  [apc-blackbone]: \u003chttps://github.com/DarthTon/Blackbone/blob/master/src/BlackBoneDrv/Loader.c#L638\u003e\n  [Detours]: \u003chttps://github.com/Microsoft/Detours\u003e\n  [DetoursNT]: \u003chttps://github.com/wbenny/DetoursNT\u003e\n  [MSDN-CreateProcessNotify]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-pssetcreateprocessnotifyroutineex\u003e\n  [MSDN-LoadImageNotify]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-pssetloadimagenotifyroutine\u003e\n  [MSDN-CreateSection]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-zwcreatesection\u003e\n  [MSDN-MapViewOfSection]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-zwmapviewofsection\u003e\n  [MSDN-UnmapViewOfSection]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-zwunmapviewofsection\u003e\n  [MSDN-CFG]: \u003chttps://docs.microsoft.com/en-us/windows/desktop/secbp/control-flow-guard\u003e\n  [VAD]: \u003chttps://resources.infosecinstitute.com/finding-enumerating-processes-within-memory-part-2/\u003e\n  [x64-abi]: \u003chttps://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention\u003e\n  [blackbone-unprotect-process]: \u003chttps://github.com/DarthTon/Blackbone/blob/43bc59f68dc1e86347a76192ef3eadc0bf21af67/src/BlackBoneDrv/Inject.c#L144\u003e\n  [WSL]: \u003chttps://docs.microsoft.com/en-us/windows/wsl/faq\u003e\n  [TLS-callbacks]: \u003chttp://www.hexblog.com/?p=9\u003e\n  [test-signing]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/install/the-testsigning-boot-configuration-option\u003e\n  [PuTTY]: \u003chttps://www.chiark.greenend.org.uk/~sgtatham/putty/\u003e\n  [wdk]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk\u003e\n  [MSDN-IoReplaceFileObjectName]: \u003chttps://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ioreplacefileobjectname\u003e\n  [SimRep]: \u003chttps://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/miniFilter/simrep/simrep.c\u003e\n  [wow64log]: \u003chttp://waleedassar.blogspot.com/2013/01/wow64logdll.html\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwbenny%2Finjdrv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwbenny%2Finjdrv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwbenny%2Finjdrv/lists"}