{"id":28434824,"url":"https://github.com/alsch092/llvm-obfuscatedynamicimports","last_synced_at":"2025-06-30T11:32:36.098Z","repository":{"id":296462528,"uuid":"993467095","full_name":"AlSch092/LLVM-ObfuscateDynamicImports","owner":"AlSch092","description":"LLVM Pass Plugin for obfuscating imported/resolved functions using `GetProcAddress` (x64)","archived":false,"fork":false,"pushed_at":"2025-05-31T04:07:06.000Z","size":41,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-05-31T08:05:48.660Z","etag":null,"topics":["llvm","llvm-clang","llvm-ir","llvm-pass","obfuscation","obfuscation-tool"],"latest_commit_sha":null,"homepage":"","language":"C++","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/AlSch092.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-30T20:59:51.000Z","updated_at":"2025-05-31T04:44:33.000Z","dependencies_parsed_at":"2025-05-31T08:10:51.778Z","dependency_job_id":"a8572030-4370-4c4d-aa12-f2b78508dbdf","html_url":"https://github.com/AlSch092/LLVM-ObfuscateDynamicImports","commit_stats":null,"previous_names":["alsch092/llvm-obfuscatedynamicimports"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AlSch092/LLVM-ObfuscateDynamicImports","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlSch092%2FLLVM-ObfuscateDynamicImports","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlSch092%2FLLVM-ObfuscateDynamicImports/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlSch092%2FLLVM-ObfuscateDynamicImports/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlSch092%2FLLVM-ObfuscateDynamicImports/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlSch092","download_url":"https://codeload.github.com/AlSch092/LLVM-ObfuscateDynamicImports/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlSch092%2FLLVM-ObfuscateDynamicImports/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262765668,"owners_count":23360954,"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":["llvm","llvm-clang","llvm-ir","llvm-pass","obfuscation","obfuscation-tool"],"created_at":"2025-06-05T19:30:42.899Z","updated_at":"2025-06-30T11:32:36.086Z","avatar_url":"https://github.com/AlSch092.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DynamicImports Obfuscation Transformative Pass - By AlSch092 @ Github\nThis project implements an LLVM transformative obfuscation module pass to protect dynamically resolved Windows system calls. It was developed using the new pass manager, and not tested in optimizations besides -O0. It's highly recommended you use the format seen in Example.cpp when using this pass, otherwise I can't guarantee it will work for you.   \n\nThe project was built \u0026 tested using CMake with Visual Studio 2022 Build Tools, and was tested with LLVM/Clang version 20.1.3 on Windows 10 x64.  \n\nRight now the pass looks for hardcoded function and variable names (using \"contains\" when possible) along with attributes, however you'll need to custom-build LLVM to add in these attributes in order for LLVM to properly work with them.  \n\n## Folder Structure  \n\n/DynamicImportsObfuscatorPass/  \n├── Example.cpp  \n├── DynamicImportObfuscatorPass.cpp  \n├── CMakeLists.txt  \n├── BuildAndRun.bat  \n├── README.md  \n└── /build/ (pass project files \u0026 built pass .dll)  \n\n## Building the DynamicImportObfuscation Pass (\"obf-dynimports\"):  \n\nThe file `BuildAndRun.bat` is the recommended build method, and contains a full pipeline for building the pass - creating the IR from Example.cpp, transforming the IR, compiling and linking the transformed IR into an executable, along with running the final executable.  \n\nA manual build process can be done by following these steps:  \n\n1. `mkdir build`  \n2. `cd build`  \n3. `cmake -G \"Visual Studio 17 2022\" -A x64 ..`  \n4. `cmake --build . --config Release --target DynamicImportObfuscatorPass`  \n\n ** If you get an error about.. \"LINK : fatal error LNK1181: cannot open input file 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\DIA SDK\\lib\\amd64\\diaguids.lib' (See [LLVM GitHub Issue #86250](https://github.com/llvm/llvm-project/issues/86250))  \na) Open `build/DynamicImportObfuscatorPass.sln` in Visual Studio  \nb) Select \"Release\" under current build configuration  \nc) Right click on the DynamicImportObfuscatorPass project -\u003e Click Properties -\u003e Linker -\u003e Input  \nd) Under \"Additional Dependencies\", remove any references/lines to lib/amd64/diaguids.lib  \ne) Close Visual Studio (save changes when prompted) and run the command at 4. again, or build the pass from within Visual Studio  \n\n## Running the pass  \n\nAssuming you're still in the 'build' directory:  \n\n`opt -load-pass-plugin=\"./Release/DynamicImportObfuscatorPass.dll\" -passes=\"obf-dynimports\" ../Example.ll -S -o ../Example_out.ll`    \n\nThis should give you a version of Example.ll with added obfuscation (Example_out.ll), which follows the three obfuscation requirements outlined in the original task.  \n\nYou can then compile and link Example_out.ll:  \n\n`clang -O0 ../Example_out.ll -o ../Example.exe`  \n\n## Output  \n\nWhen running the pass on Example.ll, the following text should be displayed in the console:  \n```\nXOR'd string: NtQueryInformationProcess =\u003e ╤(æ╤rπE6@ºA¢ycÜùç╬╣╥┴±oÆ┐\nXOR'd string: NtQuerySystemInformation =\u003e ╤(æ╤rπE,W▓ZîyKÇÿç╥ä┴┌√eÅ\nXOR'd string: NtQueryVirtualMemory =\u003e ╤(æ╤rπE)G│Z£unú¢à╧¢┘\nXOR'd string: NtQueryInformationThread =\u003e ╤(æ╤rπE6@ºA¢ycÜùç╬╜╚▄≈kà\nXOR'd string: NtQueryVolumeInformationFile =\u003e ╤(æ╤rπE)A¡[äqKÇÿç╥ä┴┌√eÅè±£╥\nXOR'd string: NtCreateSection =\u003e ╤(â╓r≡H→}ñM¥}mÇ\nXOR'd string: NtCreateThread =\u003e ╤(â╓r≡H→z⌐\\îuf\nXOR'd string: NtCreateProcessEx =\u003e ╤(â╓r≡H→~│Aèqq¥╗É\nXOR'd string: NtQueueApcThread =\u003e ╤(æ╤rΣY\u003e^ózüfgÅÜ\nXOR'd string: NtWriteVirtualMemory =\u003e ╤(ù╓~σY)G│Z£unú¢à╧¢┘\nXOR'd string: NtReadVirtualMemory =\u003e ╤(Æ┴v⌡j▬\\╡[êxOïôç╥É\nAóKÜg string: NtOpenProcess =\u003e ╤(Å╘r l\nTransformed import names!\nFound g_ImportAddresses\nInjected decryption logic for importName\nFound a store to g_ImportAddresses!\nTransformed the store instruction to import table to be XOR'd with the key: 9900550227890101106, which is stored in g_ImportAddresses!\nTransformed GetImportAddresses function!\nModified index to i32 5565448 in call to GetImportAddress\nGenerated junk byte string: .byte 0xAB,0x0D,0xE0,0xFD,0xBF,0xB8,0x89,0x3F,0xEB,0x3F,0x0C,0x27,0xBE,0x92,0x68,0xEF,0x27,0xBE,0x8F,0x24,0x81,0xC2,0x35,0x6E,0x1C,0x6F,0x52,0x52,0xBD,0x9C,0x1C,0xD8,0x1D,0xBD,0x89,0xB6,0x65,0x89,0xEC,0x23,0x67,0xA0,0xED,0xE0,0x9F,0x96,0x7F,0xE6,0xE3,0x1A,0xEA,0xFA,0xF9,0xA0,0x67,0x28,0x0F,0x76,0xBD,0x05,0x9A,0x80,0x33,0x15,0x43,0x8A,0x76,0xA1,0x2C,0xD7,0x20,0xFC,0x02,0xA3,0xF6,0x76,0xF5,0xBC,0x91,0x53,0x23,0x8F,0xC1,0xF4,0xB3,0x5E,0xC2,0xB6,0x5E,0x41,0xE5,0x2D,0x89,0x7F,0x21,0xC9,0x9F,0xF3,0x7F,0x86\nTransformed GetImportAddress function!\nRewrote store using result of GetImportAddress\nTransformed CallImportFunction routine!\nPass ran successfully and made atleast one code transformation!\n```\n\nObfuscation keys are randomized on each pass, so these values will likely be different each time the pass is run.    \n\n## Impact of Transformations  \n\nSeveral changes can be seen in the binary \u0026 assembler code produced by compiling \u0026 linking the transformed `Example_out.ll` file:  \n\n1. No references to \"NtQuery...\" can be seen when viewing string references in a disassembler/debugger - their values are encrypted while running our pass, and decrypted versions only appear on the stack, local to the function they're used in  \n\n2. A string decryption loop (random key for each character of the string) is added before the call to `GetProcAddress` in `GetImportAddresses` using manual unfolding  \n\n3. Before storing import addresses into `g_ImportAddresses` in the `GetImportAddresses` function, the address is XORed with a random key (the full key is split into 2 hi32/low32 to make it a tiny bit less obvious). This will cause values being stored in `g_ImportAddresses` to be obfuscated. Decryption never occurs in-place (only onto a stack-allocated location), so the encrypted values are never changed or seen as plaintext after being set.  \n\n4. Calls to any dynamic imports are not direct - after the encrypted address is fetched from `GetImportAddress`, it's XORed with a key and called from [RSP+0xB8]  \n\n5. The function `GetImportAddress` has the `index` parameter randomly transformed to make mappings less obvious. All calls to `GetImportAddress` have their `index` parameter transformed (index = index * rng() + rng())  \n\n6. An opaque predicate with 100 randomized junk bytes are added to `GetImportAddress` to help obfuscate the return value of the function  \n\n7. No explicit XOR instructions are used with full keys - they are either split into multiple instructions (OR + AND) or into high32 + low32 bits (XOR + XOR)  \n\n## Result\nThis pass makes it harder for attackers to:  \n\n1. Identify what system calls are being made when performing static analysis/reversing  \n2. See resolved addresses in memory  \n3. Trace dynamic imports back to API functions  \n\nNo fragments are left in program sections (.rdata, .data), all decrypted copies of the stored encrypted values are done on the stack.  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falsch092%2Fllvm-obfuscatedynamicimports","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falsch092%2Fllvm-obfuscatedynamicimports","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falsch092%2Fllvm-obfuscatedynamicimports/lists"}