{"id":13827158,"url":"https://github.com/amossys/memitm","last_synced_at":"2025-07-09T03:31:02.461Z","repository":{"id":68107564,"uuid":"151551457","full_name":"AMOSSYS/MemITM","owner":"AMOSSYS","description":"Tool to make in memory man in the middle","archived":false,"fork":false,"pushed_at":"2018-10-08T09:52:41.000Z","size":2626,"stargazers_count":124,"open_issues_count":0,"forks_count":28,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-15T12:34:19.820Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AMOSSYS.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-10-04T09:59:48.000Z","updated_at":"2024-08-12T19:42:10.000Z","dependencies_parsed_at":"2023-02-21T23:30:19.583Z","dependency_job_id":null,"html_url":"https://github.com/AMOSSYS/MemITM","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AMOSSYS/MemITM","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AMOSSYS%2FMemITM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AMOSSYS%2FMemITM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AMOSSYS%2FMemITM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AMOSSYS%2FMemITM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AMOSSYS","download_url":"https://codeload.github.com/AMOSSYS/MemITM/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AMOSSYS%2FMemITM/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264386255,"owners_count":23599963,"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":[],"created_at":"2024-08-04T09:01:51.303Z","updated_at":"2025-07-09T03:31:01.081Z","avatar_url":"https://github.com/AMOSSYS.png","language":"Python","funding_links":[],"categories":["\u003ca id=\"42f9e068b6511bcbb47d6b2b273097da\"\u003e\u003c/a\u003e未分类"],"sub_categories":["\u003ca id=\"3bd67ee9f322e2c85854991c85ed6da0\"\u003e\u003c/a\u003e投毒\u0026\u0026Poisoning"],"readme":"## What's the MemITM tool? ##\n\nThe MemITM (Mem In The Middle) tool has been developped in order to easily intercept \"messages\" in Windows processes memory. We developped a lot of custom memory interception tools in order to capture network messages before encryption, or IPC messages, and to be able to inspect them or alter them to do some fuzzing. Each tool was really custom, not generic, implemented in C/ASM and was not easy to use/maintain/adapt.\n\nThe MemITM tool has been developped in order to address these problems, and consists in :\n- an IDA Python script, which generates a \"config\" file, indicating where and how the interception points have to be placed (relative address, how to find the buffer and its size, how to place the hook, etc.) ;\n- the DLL file, which will be injected in the target process and will loads the config and places the hooks ;\n- a DLL file injector, which will load the DLL in the target process ;\n- a python script, which communicates with the injected DLL/hooks, and gets in realtime the intercepted buffers (and can alter them) in really simple callbacks you can modify as you want.\n\nYou may download it (sources + compiled) here: https://github.com/Amossys/MemITM\n\n## How simple is MemITM? (Example1: WriteFile) ##\n\nLet's say you want to intercept file writes (there are simpler ways to do it, but that's for the example) in a specific Windows process. File writes are performed by the `WriteFile` function, which is exported by `kernelbase.dll`, and follows the scheme `BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, ...)`. WriteFile is a `__fastcall` function : `lpBuffer` is pointed by `RDX` and `NumberOfBytesToWrite` by `R8`. Let's intercept these buffers.\n\n1. open your \"kernelbase.dll\" file in IDA Pro, load the `generate.idapython.py` script, and just run `getHook(LocByName(\"WriteFile\"), \"rdx\",\"r8\")` followed by `updateConfig(\"config.bin\")`. \n\n2. run a notepad and then, `python memitm.py notepad.exe config.bin`.\n\nThat's it! A hook has been placed on the `WriteFile` function, and the `memitm.py` \"logger\" and \"fuzzer\" functions will receive a copy of the buffer.\n\n## My buffer is not pointed by a register! (Example2: NtCreateFile) ##\n\nThe `getHook` function allows specifying registers for the buffer and its size, but in some cases it may not be the case. Let's say you want to intercept `NtCreateFile` syscalls and get the filename. `NtCreateFile` follows the scheme `NTSTATUS NtCreateFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, ...)`, and the file name can be obtained by `ObjectAttributes-\u003eObjectName-\u003eBuffer` (size is `ObjectAttributes-\u003eObjectName-\u003eLength * sizeof(WCHAR)`).\n\n`getHook` also allows specifying a shellcode in its `t1customOpcodes` parameter instead of registers. This shellcode must :\n- place the buffer pointer into the `RCX` register;\n- place the buffer's size into the `RDX` register;\n- not mess up with the stack.\n\nIn the `NtCreateFile` case, we can just run this ASM code to do the work:\n\n    mov rax, [r8+0x10]    ; rax is now ObjectAttributes-\u003eObjectName\n    mov rcx, [rax + 0x10] ; rcx is now ObjectAttributes-\u003eObjectName-\u003eBuffer\n    mov rdx, [rax]        ; rdx is now ObjectAttributes-\u003eObjectName-\u003eLength\n    and rdx, 0xFFFF       ; Length is a USHORT \n    shl rdx, 1            ; and must be *2\n\nLet's assemble this (using for instance the online disassembly website) : `498B4010488B4810488B104881E2FFFF000048D1E2`. Our `getHook` call should now be `getHook(LocByName(\"NtCreateFile\", t1customOpcodes=\"498B…E2\")`.\n\n## Can I intercept multiple calls? ##\n\nYes! The config file can embed multiple interception points definitions, and the `getHook` function appends the file. You can set interception points in multiple modules. For instance, you can intercept ALPC and IOCTL messages at the same time.\n\nYou will be able to diffentiate them by their \"message ID\" in the python callbacks.\n\n## Oops, BSOD/system freeze! ##\n\nDon't worry, there is also a simple HTTP server (`logserver.py`) and the `httpNetSend` function which allow you to send your test cases (and modification) to a remote host in realtime.\n\n## How do I fuzz? ##\n\nWell, you can start using the `bufferBitFlip` function in order to... flip several bits. Logging raw messages will allow you to write your own dissector, and to start implementing manually a better fuzzer :). Remember: you can't change the buffer size!\n\nFor instance, in our `WriteFile` example, the following `fuzzer` function will replace \"hello\" by \"world\":\n\n    def fuzzer(data, msgID=None, pid = 0):\n       return data.replace(\"hello\",\"world\")\n\n## Example3: dissecting ALPC messages ##\n\nFor instance, for ALPC messages, you may have the following `memitm.py` setup:\n\n    def logger(data, msgID=None, pid=0):\n        totalLen = 0\n        dataLen = 0\n        typ = 0\n        dataInfoOffset = 0\n        cid = 0\n        tid = 0\n        messageID = 0\n        clientViewSize = 0\n        # skip the PORT_MESSAGE header (0x18 bytes)\n        if len(data) \u003e 0x18:\n            totalLen = struct.unpack(\"\u003cH\",data[:2])[0]\n            dataLen = struct.unpack(\"\u003cH\",data[2:4])[0]\n            typ = struct.unpack(\"\u003cH\",data[4:6])[0]\n            dataInfoOffset = struct.unpack(\"\u003cH\",data[6:8])[0]\n            zeroInit = struct.unpack(\"\u003cL\",data[4:8])[0]\n            cid = struct.unpack(\"\u003cL\",data[8:0xC])[0]\n            tid = struct.unpack(\"\u003cL\",data[0xC:0x10])[0]\n            messageID = struct.unpack(\"\u003cL\",data[0x10:0x14])[0]\n            clientViewSize = struct.unpack(\"\u003cL\",data[0x14:0x18])[0]\n        logMessage = \"ALPC message : %x:%x - %x:%x - %d:%d - %x - %x\" % (totalLen, dataLen, typ, dataInfoOffset, cid, tid, messageID, clientViewSize)\n        print logMessage\n        return\n\n## How does it works internally? ##\n\nThe IDA Pro script embeds a simple \"hooking\" engine, which allows moving several instructions to a specific area (derived from our *DIMCT* tool). It must handle lots of corner cases, such as memory relative instructions, cross references, etc. and will probably display lots of \"can't install hook here\" if you try intercepting really small basic blocks (\u003c5 bytes for x86 binaries, \u003c12 bytes for x64 ones) which contain multiple cross-references. For more information, just read the source code :).\n\nIt generates the following information in the config file:\n- the module name;\n- the relative address of the interception point;\n- the interception hook area (aka \"T1 trampoline\"), which places the buffer/length into `RCX`/`RDX` and calls the DLL log routine;\n- the restoration hook area (aka \"T2 trampoline\"), which restores the context and executes replaced instructions before returning to the interception point;\n- the restoration hook area relocations, for relative instructions which have been converted to absolute ones and must be patched.\n\nThe DLL injector is a simple DLL injector (`CreateRemoteThread`/`LoadLibraryA` stuff).\n\nThe DLL itself initiates a shared memory area and waits for the config data (the shared memory area has 3 generic fields : the shared memory message ID, the message size and the message buffer). Once received, it parses and installs the hooks in memory, and no config update will be allowed after this (you must kill the process if you want to set up new hooks). Any placed hook will land in the `genericHookFunction` DLL function. This function just fills the memory area (protected with critical sections) with the buffer data, its size and the message ID (set in higher bits of the shared memory message ID field). In order to notify the python process, 2 events are used to signal new messages and to wait for the process to patch them (timeout is 2 seconds). If the buffer has been updated, the original buffer is modified too.\n\nThe `memitm.py` script runs the injector process, and waits for the shared memory to be created. It then sends the configuration and waits for the message event. When received, the shared memory is read and the `logger` and `fuzzer` functions are called with the buffer, the process ID and the message ID. If the `logger` function returns a different buffer than the original one and its size is equal, it is written to the shared memory area. The \"ACK\" event is then signaled, and the script waits for a new message.\n\n## That's it! ##\n\nWe hope this tool will be useful, any contrib/reviews will be appreciated! Also, if you're French and like Rennes, we're hiring :)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famossys%2Fmemitm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famossys%2Fmemitm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famossys%2Fmemitm/lists"}