Cross-platform binary obfuscation, anti-reversing, anti-debugging and av-bypass framework for Windows and Linux
This framework provides a preprocessor that modifies C source code by inserting ASM and C based code snippets for binary obfuscation, anti-reversing, anti-debugging and av-bypass purposes. It uses predefined templates to create variations of code snippets.
The framework provides two types of snippet templates based on the ASM and C codebase. In turn, C templates can include references to ASM templates. Templates are located in the tpl directory. The internal directory win contains Windows-specific code snippets, linux contains Linux-specific code snippets, and cross contains cross-platform code snippets.
The following common templates structure is used for each supported operating system:
- win / linux / cross
- dbgs
- asm
- c
- snds
- asm
- c
- vmds
- asm
- c
- dbgs
The obfuscation ASM snippets are common to all operating systems and are located in the cross subdirectory:
- cross
- obfs
ASM template represents the content of the C language asm volatile() compiler instruction with assembly code. Its code uses framework-based instructions for obfuscation logic.
C template represents the body of a C language function. Its code uses framework-based instructions for obfuscation logic, API calls, and more.
The framework contains four types of snippets:
- Obfuscation snippets (obfs) are used for binary obfuscation. They contain dummy assembly code that does nothing, and their only purpose is to obfuscate the application's logic. These snippets can only contain ASM code templates. To insert such snippets into C code, use the
{{obf:*}}instruction - Anti-Debugging snippets (dbgs) are used to determine whether an application is being debugged. These snippets may contain ASM and C code templates. To insert such snippets into C code, use the
{{dbg:*}}instruction - Sandbox Detection snippets (snds) are used to determine whether the application is running in an antivirus sandbox. These snippets may contain ASM and C code templates. To insert such snippets into C code, use the
{{snd:*}}instruction - Virtual Machine Detection snippets (vmds) are used to detect guest operating systems running under a virtual machine. These snippets may contain ASM and C code templates. To insert such snippets into C code, use the
{{vmd:*}}instruction
In the provided framework-based instructions can be used asterisk {{obf:*}} to insert any snippet, snippet name {{snd:clone}} or a comma separated list with the snippet names {{snd:clone,rdtsc}}. Spaces are not allowed, names may contain letters, numbers and underscores. The snippet name is the same as the template file name without the *.c extension. Snippets with different types and codebases can use the same names.
Warning! Cross-platform code snippets that have the same type, language, and name as OS-specific snippets cause a collision and should be renamed.
The framework contains three common cross-platform C code templates and three corresponding subtemplates for each supported operating system, located in the tpl/code directory:
header.ctemplate contains all the framework logic that should be inserted into the resulting C file. Use the{{header}}instruction to place this code in the C source fileapi.ctemplate contains all the logic related to the API calls (kernel32.dllfor Windows andlibc.sofor Linux) and decryption functions. This code will be placed in the C source file via the header templateshell.ctemplate contains logic related to the shellcode execution. This code will be placed in the C source file via the header template if the shellcode is provided in the command prompt. Shellcode should be called from code using the{{shell-exec}}instruction.
The framework provides two types of instructions for API calls from core libraries. Instructions for calls with {{api-n:<name>}}(...) and without {{api-0:<name>}}() arguments. Each instruction should contain the API function name {{api-n:Sleep}}(1), {{api-0:GetLastError}}(). The function name will be encrypted and added to the source code. Before calling a function, its name will be decrypted and filled with zero bytes after use.
To call API functions, the framework uses a special caller entity that should be initialized once in the project's main function and then passed as a parameter to every function that uses API calls or C code snippets. The following instructions can be used to work with the API caller:
{{caller-init}}initializes the caller variable{{caller-var}}inserts the caller variable. Use it to pass the caller variable to the function as a parameter{{caller-ptr}}inserts the caller pointer. Use it to pass the caller pointer to the function as a parameter{{caller-decl-var}}inserts the caller variable declaration. Use it to declare the caller function parameter as a variable{{caller-decl-ptr}}inserts the caller pointer declaration. Use it to declare the caller function parameter as a pointer{{caller-cast}}declares the caller variable and initializes it by casting from a pointer. Use it in case of API callback functions with arguments of typevoid *{{caller-kernel}}gets thekernel32.dll/libc.socore module handle from the caller variable. Use it to access the core module when needed{{caller-proc}}calls theGetProcAddress/dlsymfunction from the caller variable. It is used to get API functions from modules other than the core one{{caller-exit}}calls theExitProcess()/exit()function. It is useful in case of cross-platform development, when the C snippet is not dependent on the operating system
The framework allows to insert a random constant into the source code using the following instructions:
{{value:byte}}inserts a 8-bit random integer value{{value:uuid}}inserts a random UUID as a string of hexadecimal numbers without delimiters{{value:guid}}inserts a random UUID in the Windows format with braces and delimiters
The framework allows to encrypt string variables used in the source code. To declare and encrypt a string, use the following instruction {{str-alloc:<var>:<value>}}. This instruction allows to declare a string variable using its name and value {{str-alloc:lpName:Text}}. The variable name can contain letters, numbers, and underscores. The characters { and } in the string text must be escaped with \, for example \{ and \}. An already created string variable can be reassigned using the {{str-realloc:<var>:<value>}} instruction with the same parameters. After use, the string should be released using the {{str-free:<var>}} instruction, passing the name of the string variable.
Calling shellcode is an additional feature of this framework. Pass shellcode as a base64 string by using -shell command prompt argument. Use the Metasploit tool msfvenom with the argument -f base64 to generate any type of shellcode.
This type of snippets represents the content of the C language asm volatile() compiler instruction with assembly code. Use the "memory" clobber to add the memory barrier. The ASM snippet provides two types of framework-based instructions:
{{ops}}inserts dummy assembly instructions that obfuscate the binary. The number of instructions varies from 0 to the value of the-opsargument passed on the command prompt. Pass 0 to tell the framework not to generate dummy instructions{{reg:<number>:<size>}}instruction tells the framework to insert a random register to add polymorphism to the fragment. The first argument specifies the register number. The same template will generate different assembly code each time. Up to 10 (numbers from 0 to 9 can be used) general purpose registers can be inserted:rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11. The second argument tells the framework the size of the register: without argument - the preprocessor inserts a QWORD register,d- inserts a DWORD register,w- inserts a WORD register,binserts a BYTE register
The framework generates several variants of ASM snippets and inserts them into the resulting source file as inline C functions with names constructed according to the following pattern {type}_asm_{name}_{number}.
This type of snippets represents the body of a C function that takes a caller parameter. The caller is passed to the function by value for better binary obfuscation. The C snippet allows to use any ASM fragments, call API functions and execute shellcode. The framework generates several variants of C snippets and inserts them into the resulting source file as inline C functions with names constructed according to the following pattern {type}_c_{name}_{number}.
- cross
- obfs
- 00, 01, 02, 03, 04, 05 - six code obfuscation snippets that implement obfuscation techniques such as: Logic Flow Obfuscation, NOP Obfuscation, Anti-Disassembler Code Obfuscation, Trampolines and Instruction Permutations
- snds
- c
- memory - allocation of a huge amount (~200 MB) of memory to force the AV sandbox analysis to end
- c
- vmds
- asm
- cpuid - detecting the presence of a VM by checking some flags returned by
CPUID(1)instruction
- cpuid - detecting the presence of a VM by checking some flags returned by
- asm
- obfs
- linux
- dbgs
- c
- check_tracer_pid - checking
TracerPidfrom the/proc/self/statusfile to see if there is a debugger or tracer attached to this process viaptrace
- check_tracer_pid - checking
- c
- snds
- asm
- clone - creating a copy of the executing process using the
forksyscall
- clone - creating a copy of the executing process using the
- asm
- dbgs
- win
- dbgs
- asm
- debug_flag - direct debugger detection by analyzing the PEB structure
- heap_flags - checking the
FlagsandForceFlagsheap fields, which are affected by the presence of a debugger - nt_global_flag - checking of
NtGlobalFlagby analyzing the PEB structure
- c
- check_remote_debugger_present - calling
CheckRemoteDebuggerPresentWin32 API function - heap_protection - checking the end of the allocated heap block
- is_debugger_present - calling
IsDebuggerPresentWin32 API function - nt_query_information_process - checking information from the process by calling
ntdll!NtQueryInformationProcessWin32 API function
- check_remote_debugger_present - calling
- asm
- snds
- c
- clone - creating a copy of the executing process
- load_library - loading a non-existent dynamic library to cheat the sandbox
- query_performance_counter - detecting the difference between the specified and actual process sleep time using the
QueryPerformanceCounterWin32 API call - rdtsc - detecting the difference between the specified and actual process sleep time using the
rdtscCPU instruction
- c
- vmds
- c
- virtual_box - detecting the presence of the VirtualBox by checking the existence of the pseudo-device
\\.\VBoxMiniRdrDNon the system
- virtual_box - detecting the presence of the VirtualBox by checking the existence of the pseudo-device
- c
- dbgs
The framework provides the following comand prompt arguments:
-helpshows framewor command line help-dirsets the working directory with thetplsfolder. May be useful if the binary is called from another location-tplsets a C file with a template for processing-ossets the operating system for compilation-opssets the maximum number of random operations for the ASM fragment-obfs,-dbgs,-snds,-vmdsset the number of variants for each snippet type-shellsets a Base64 string with shellcode. Usemsfvenom -f base64 ...to generate
This repository contains two sample loader templates for Linux and Windows, demonstrating the framework's capabilities. Use the Makefile from the examples/linux/loader or examples/win/loader directory to create and compile the obfuscated binary. Each Makefile contains the following commands:
install- compile the obfuscation framework using the installed Go compilersource- generate the resulting C file with the obfuscated source codeloader- compile the obfuscated C fileclean- clean compiled executablesall- run theclean+loadercommands
The Linux example uses shellcode to execute the whoami command. The Windows example uses shellcode to run the default calculator. Both shellcodes were generated using the Metasploit framework.
This project is provided for informational purposes only. The author is not responsible for its malicious use.
You can support this project by donating to the following Ethereum wallet:
ethereum:0x0468DcdE81b69b87ea0A546faA6c5aae2F4FE30b
