-
The pass logic is implemented in X86RetModPass.cpp
-
Include X86RetModPass.h in llvm-project/llvm/lib/Target/X86/X86TargetMachine.cpp.
-
In X86TargetMachine.cpp, there is a function - void X86PassConfig::addPreEmitPass(), add below code at the end of the function.
// LLVM will run our pass in PreEmit phase addPass(new X86RetModPass());
-
Now we have successfully registered our custom backend pass.
-
Here we will modify assembly printer component (X86AsmPrinter class) of LLVM.
-
We will add a new private member EpilogueStubSymbol in X86AsmPrinter class definition.
llvm-project/llvm/lib/Target/X86/X86AsmPrinter.h -
Following the addition of new class member, we need to modify two X86AsmPrinter class methods as mentioned below:
- void X86AsmPrinter::emitFunctionBodyStart()
- void X86AsmPrinter::emitFunctionBodyEnd()
-
You will find the X86AsmPrinter class implementation in the file mentioned below.
llvm-project/llvm/lib/Target/X86/X86AsmPrinter.cpp -
Just replace the contents in emitFunctionBodyStart() and emitFunctionBodyEnd() with code in X86AsmPrinter.cpp provided in this repo. Dont forget to add the EpilogueStubSymbol in X86AsmPrinter.h
- Now we are ready to build LLVM from scratch with our modification baked into it.
- Make sure CCACHE is installed among other dependencies for the installation.
--------------------------------------------------------------------------------------------------------------
Configure CCACHE (optional)
--------------------------------------------------------------------------------------------------------------
ccache --max-size=10G
--------------------------------------------------------------------------------------------------------------
llvm building
--------------------------------------------------------------------------------------------------------------
git clone https://github.com/llvm/llvm-project.git
cmake -G Ninja ../llvm-project/llvm -DLLVM_ENABLE_PROJECTS="clang;lld" -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_ENABLE_ASSERTIONS=ON -DCMAKE_INSTALL_PREFIX=../llvm-install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
ninja install
--------------------------------------------------------------------------------------------------------------
Compile and linking test sample test.cpp
--------------------------------------------------------------------------------------------------------------
llvm-install/bin/clang++ -static -static-libstdc++ -static-libgcc -target x86_64-w64-windows-gnu -fuse-ld=lld -O0 test.cpp -o out.exe -Wl,--strip-all -Wl,--gc-sections -v
--------------------------------------------------------------------------------------------------------------
Inject custom entrypoint stub
--------------------------------------------------------------------------------------------------------------
python3 modifyEP.py out.exe final.exe
- LLVM backward compatibility is very poor, therefore future LLVM updates may (or may not) break compilation.
-
Do not try to run the binary prior to injecting custom entry point stub, as it will lead to a crash.
-
Test code test.cpp contents are shown below.
// test.cpp #include <windows.h> #include <iostream> #include <stdint.h> struct MyStruct { void* func; uint32_t len; }; // Place xor key here, POC uses single byte keys. eg 0x00000008 __attribute__((section(".funcmeta"))) uint32_t myfuncsec_key = 0x12345678; // Macro to register a function #define REGISTER_FUNCTION(fn) \ __attribute__((section(".funcmeta"))) \ struct MyStruct fn##_entry = { (void*)fn, 0xDEADBEEF }; void REG_foo2() { std::cout << "\nhello from foo2"; } int REG_foo(int a, int b, int c, int d, int e) { int i = 0; int x = a + b + c + d + e; std::cout << "\n" << x; MessageBoxA(NULL, "Hello from foo", "Test", MB_OK); if (i) { return 0; } else{ i++; } return x; } //Register your functions here REGISTER_FUNCTION(REG_foo) REGISTER_FUNCTION(REG_foo2) int main() { bool p = VirtualProtect(0,0,0,0); std::cout << "MAIN here"; int a = REG_foo(1,2,3,4,5); std::cout << "\n Ret val :" << a; REG_foo2(); return 0; }