https://github.com/lich4/sllvm
Supported Systems and Architectures
-
Host Systems: Planned support for
macOS/Linux; no current plans forWindows. -
Target Systems: Planned support for
macOS/iOS/Android. -
Architectures: Both Host and Target support
X64andARM64. -
Languages: Supports
C,C++,Objective-C,Objective-C++,Swift,Rust,Golang, etc.
Obfuscation Capabilities
- Data Encryption
- CE Constant Encryption (String obfuscation).
- Control Flow Obfuscation
- Function-Level Obfuscation
- Instruction-Level Obfuscation
- 其他
SLLVM Features
SLLVM was developed to address limitations in the OLLVM lineage (Hikari, Hikari-LLVM15, Pluto, Polaris-Obfuscator, goron, Arkari, o-mvll, etc.):
-
Release Compatibility: Obfuscation is not reverted by the compiler during
Releasebuilds. - Anti-Analysis: Resists "Read-Only Data Segment" attacks in IDA Pro; if data segments are marked read-only, obfuscation remains intact.
- Hardening: Removes common OLLVM signatures to resist specialized scripts, symbolic execution (Angr), and AI-based deobfuscation.
-
Stability: Avoids compilation hangs and memory exhaustion often seen in other variants when processing large
header-onlylibraries.
Configuration
In many real-world projects, fully obfuscating the entire codebase is often impractical due to the following reasons:
-
Project Scale and Dependencies: Large projects or those utilizing numerous
header-onlylibraries may end up obfuscating unnecessary code, resulting in excessively large binary sizes. - Compilation Overhead: For massive projects with complex dependencies, applying flattening (or other methods) to unnecessary code can lead to extremely long compilation times or even system hangs.
- Performance Impact: Obfuscating complex algorithms can significantly increase runtime latency; typically, control flow flattening adds over 10% to execution time.
-
Compliance Issues: Excessive obfuscation may violate the submission policies of platforms like the
AppStoreorGooglePlay.
In practice, it is often necessary to apply different levels of obfuscation based on the importance of specific modules or functions. This requires a configuration strategy to specify which targets receive which type of obfuscation. Traditional open-source OLLVM implementations usually manage strategies through the following methods:
-
Command-line Arguments: Specifying parameters for specific modules (e.g.,
-llvm -fla), which is compatible with all compiler front-ends supporting LLVM flags. - Environment Variables: Defining obfuscation parameters via the system environment.
-
Function Annotations: Using attributes like
__attribute((__annotate__(("fla"))))(or the modern syntax[[clang::annotate("fla")]]); however, this is restricted toC/C++and is not supported byObjective-C orother languages. -
Marker Functions: Designating specific functions as markers, as shown below; this method provides support for
Objective-C.
All of the aforementioned methods have their limitations—they either require intrusive code changes, fail to provide control at the function level, or are restricted to specific languages. This project utilizes a configuration file (sllvm.json) to define which functions and modules should be obfuscated, ensuring compatibility with the majority of compiler frontends and programming languages.
Primary/Secondary Fields
-
log_levelGlobal logging level. String type, optional. Defaults to no logging. Possible values:info|debug -
src_rootSource file root path. String type, optional. Defaults to the current directory; typically does not need to be specified. -
policiesA list of strategies, divided into Module-level and Function-level policies. Module-level policies contain module and policy fields but no func field. Function-level policies includemodule,func, andpolicyfields.-
moduleA regular expression used to match module paths. -
funcregular expression used to match function names.C++functions are demangled before matching. -
policyString type; must correspond to a key defined inpolicy_map.
-
-
policy_mapA strategy index referenced bypolicies. Each strategy name maps to a dictionary containing the following fields:-
baseThe name of a base policy to inherit from (module or function level). String type, optional. Value must be a key inpolicy_map. -
dumpTypes of intermediate code to output (saved in thesllvm_dumpdirectory). Applies to module or function levels. String array, defaults to empty. Possible values:ir,mmd,asm. -
enable_stdEnables obfuscation forC++standard library functions. Module-level policy. Boolean type, optional. Disabled by default to minimize compatibility issues.
-
Function-level Policy
-
enable_ceEnablesCEobfuscation.-
ce_size_minMinimum string length. -
ce_size_maxMaximum string length. -
ce_algoEncryption/decryption algorithm. -
ce_mode_stackEnables stack-based string decryption.
-
-
enable_flaEnablesFLAobfuscation.-
fla_probObfuscation probability for BasicBlocks. -
fla_force_regIncreases obfuscation strength. -
fla_use_igvIncreases obfuscation strength. -
fla_use_dynIncreases obfuscation strength. -
fla_use_rcfIncreases obfuscation strength. -
fla_blk_sizeIncreases obfuscation strength. -
fla_invoke_opCompatibility for exception handling.
-
-
enable_bcfEnablesBCFobfuscation.-
bcf_probObfuscation probability for BasicBlocks. -
bcf_complexExpression complexity. -
bcf_use_varUses variables to construct expressions.
-
-
enable_ecfEnablesECFobfuscation.-
ecf_probObfuscation probability for BasicBlocks.
-
-
enable_fwEnablesFWobfuscation.-
fw_loop_minMinimum number of function nesting layers. -
fw_loop_maxMaximum number of function nesting layers. -
fw_excludeSub-functions to exclude.
-
-
enable_fccEnablesFCCobfuscation.-
fcc_numNumber of randomized calling conventions (Module-level). -
fcc_typeType of customized calling convention (Module-level). -
fcc_narg_regMaximum number of parameters passed via registers (Module-level).
-
-
enable_ibrEnablesIBRobfuscation.-
ibr_probObfuscation probability for BasicBlocks. -
ibr_use_igvIncreases obfuscation strength. -
ibr_use_dynIncreases obfuscation strength.
-
-
enable_icallEnablesICALLobfuscation.-
icall_use_igvIncreases obfuscation strength (Enabled by default). -
icall_use_dynIncreases obfuscation strength.
-
-
enable_igvEnablesIGVobfuscation.-
igv_use_dynIncreases obfuscation strength.
-
-
enable_svcEnablesSVCobfuscation.-
svc_usr_irIncreases obfuscation strength.
-
-
enable_splitEnablesSPLITobfuscation.-
split_maxsizeMaximum number of instructions per split segment.
-
-
enable_inlineEnablesINLINEobfuscation. -
enable_secEnablesSECobfuscation.-
sec_ad_probInsertion probability for function anti-debugging logic. -
sec_usr_irIncreases obfuscation strength.
-
A typical SLLVM configuration file sllvm.json is as follows::
{
"log_level": "info",
"policy_map": {
"mod_pol": {
"dump": ["ir"],
},
"func_pol": {
"enable_ce": true,
"ce_size_min": 5,
"ce_size_max": 128,
"ce_algo": 0
}
},
"policies": [
{
"desc": "Module-level policy",
"module": ".*",
"policy": "mod_pol"
},
{
"desc": "Function-level policy",
"module": ".*",
"func": ".*",
"policy": "func_pol"
}
]
}
Supported Obfuscation Methods
String Encryption
Currently, SLLVM's CE supports arm64/arm64e, Objective-C constant strings, and stack-based decryption via ce_mode_stack. Unlike Hikari, which performs module-level obfuscation, SLLVM operates at the function level. This allows users to obfuscate all strings within specific functions, which is particularly useful for handling header-onlylibraries.
Key differences from Hikari:
- Supports encryption algorithms beyond simple XOR.
- Supports stack-based decryption(
ce_mode_stack)
ce_algo
Used to configure the encryption/decryption algorithm. SLLVM currently supports 30 algorithms with complexity ranging from XOR to AES-level. Setting this value to 100 will select a random algorithm.
ce_mode_stack
This setting controls whether strings are decrypted on the stack. If ce_mode_stack is disabled, SLLVM utilizes the same approach as Hikari. The table below compares Hikari's method, SLLVM's S-mode (stack), and C++ template metaprogramming-based string obfuscation:
Hikari |
SLLVMS-Mode |
C++ Template Metaprogramming | |
|---|---|---|---|
| Encryption Location | Static Area | Static Area | Static Area / Immediate |
| Decryption Timing | Function Prologue | Function Prologue | Before Reference |
| Decryption Location | Static Area | Stack Area | Stack Area |
| Requires Source Change | N | N | Y |
| Complex Algorithms | Supported | Supported | Not Suitable |
Notes:
- Decryption Timing:
Hikaridecrypts once at the function prologue. While some otherOLLVM-basedprojects decrypt in an initialization function, that method has the disadvantage of plaintext appearing in the static area immediately after decryption. - Language Support:
C++template methods are limited toC++. While languages likeRusthave third-party libraries for similar results, all such methods require manual changes to the source code.
Important: Most OLLVM variants do not implement stack-based decryption because string constants are static data, making it difficult for the IR layer to determine if a string might "escape." In SLLVM, enabling ce_mode_stack demotes strings from static data to stack data, which requires specific handling.
Showcase
int main(int argc, char** argv) {
printf("hello sllvm\n");
return 0;
}
Control Flow Flattening
Transforms control flow from sequential execution into a switch-case loop structure. Key differences from Hikari include:
-
Signature Weakening: Significantly reduces common
FLApatterns, such as state variables, in-degrees, dispatcher blocks, and single-loop structures. -
Exception Handling: While Hikari cannot handle functions with exception handling,
SLLVMallows you to choose a specific handling method via thefla_invoke_opparameter.
展示
int main(int argc, char** argv) {
if (argc <= 0) {
printf("not possible\n");
} else if (argc == 1) {
printf("no args\n");
} else {
printf("%d args\n", argc - 1);
}
return 0;
}
Bogus Control Flow
Inserts "always-false" conditional branches into the sequential control flow. Key differences from Hikari include:
-
Release Stability: The obfuscation is not reverted or optimized away by the compiler during
Releasebuilds. -
Resilience to Static Analysis: Resists "read-only data segment" attacks; the obfuscation remains intact even if the data segment is set to read-only in
IDA Pro(this feature relies onbcf_use_var).
Showcase
int main(int argc, char** argv) {
if (argc <= 0) {
printf("not possible\n");
} else if (argc == 1) {
printf("no args\n");
} else {
printf("%d args\n", argc - 1);
}
return 0;
}
Novel Control Flow
A brand-new obfuscation approach designed to counter tracing and symbolic execution by tools like Angr.
Function Wrapper
Performs nesting on sub-functions directly called by the designated function. Key difference from Hikari:
- The obfuscation is not reverted or optimized away by the compiler during
Releasebuilds.
Calling Convention Obfuscation
Converts standard C calling conventions into randomized ones, altering the registers used for parameters and return values. Currently, this is partially implemented for ARM64.
-
fcc_numSpecifies the number of randomized calling conventions. -
fcc_typeSpecifies the type of customized calling convention, with the following values:- 0 Uses only registers
X0~X8 - 1 Uses only integer registers.
- 2 Uses only floating-point registers.
- 10 Uses any available registers.
- 0 Uses only registers
-
fcc_narg_regSpecifies the maximum number of parameters passed via registers; remaining parameters are passed via the stack.
Showcase
static int test(int a0, int a1, int a2, int a3, int a4) {
printf("a0=%d\n", a0);
printf("a1=%d\n", a1);
printf("a2=%d\n", a2);
printf("a3=%d\n", a3);
printf("a4=%d\n", a4);
return a0 + a1 + a2 + a3 + a4;
}
int main(int argc, char** argv) {
test(argv[0][0], argv[0][1], argv[0][2], argv[0][3], argv[0][4]);
return 0;
}
Indirect Branch
Key differences from Hikari:
- The obfuscation is not reverted by the compiler when compiling in
Releasemode. -
ibr_use_igvis similar toHikari'sindibran-enc-jump-target. When combined withibr_use_dyn, it further enhances the obfuscation strength.
Showcase
int main(int argc, char** argv) {
if (argc <= 0) {
printf("not possible\n");
} else if (argc == 1) {
printf("no args\n");
} else {
printf("%d args\n", argc - 1);
}
return 0;
}
System Call Obfuscation
Converts standard system call functions into direct SVC (Supervisor Call) instructions.
Showcase
int main(int argc, char** argv) {
int r = access("/tmp/1.txt", 0);
printf("r=%d\n", r);
return 0;
}
Instruction Splitting
Splits a function's instructions and scatters them across random addresses throughout the entire module.
Showcase
void test(int argc) {
if (argc <= 0) {
printf("not possible\n");
} else if (argc == 1) {
printf("no arg\n");
} else {
printf("%d args\n", argc - 1);
}
}
int main(int argc, const char** argv) {
if (argc <= 0) {
printf("not possible\n");
} else if (argc == 1) {
printf("no arg\n");
} else if (argc == 2) {
printf("1 arg\n");
} else {
printf("%d args\n", argc - 1);
}
return 0;
}
Function Inlining
Inlines all sub-functions called by a specified function into the current function's body.
Security Protection
Inserts anti-debugging logic directly into the specified functions.











Top comments (0)