How to obfuscate code with the SDK
How to build and use the SDK
The path to the SDK source code and msvc-project is as follows:
Configuring the SDK is very simple. You can export the VxLang API as follows.
extern "C"
void VxVirtualizationBegin() {
return;
}
extern "C"
void VxVirtualizationEnd() {
return;
}
//
extern "C"
void VxDualModeBegin() {
return;
}
extern "C"
void VxDualModeEnd() {
return;
}
//
extern "C"
void VxObfuscationBegin() {
return;
}
extern "C"
void VxObfuscationEnd() {
return;
}
//
extern "C"
void VxCodeFlatteningBegin() {
return;
}
extern "C"
void VxCodeFlatteningEnd() {
return;
}
These functions are defined by the following macros:
#define VL_OBFUSCATION_BEGIN VxObfuscationBegin()
#define VL_OBFUSCATION_END VxObfuscationEnd()
#define VL_CODE_FLATTENING_BEGIN VxCodeFlatteningBegin()
#define VL_CODE_FLATTENING_END VxCodeFlatteningEnd()
#define VL_VIRTUALIZATION_BEGIN VxVirtualizationBegin()
#define VL_VIRTUALIZATION_END VxVirtualizationEnd()
#define VL_DUAL_MODE_BEGIN VxDualModeBegin()
#define VL_DUAL_MODE_END VxDualModeEnd()
By applying this macro, you can protect your code like this:
void test() {
VL_OBFUSCATION_BEGIN;
printf("Hello Wolrd ! \n");
VL_OBFUSCATION_END;
return;
}
How to check for obfuscation
To check if your code is obfuscated, you can use --disable-core
.
TIP
vxlang.exe ${Your-Binary} –disable-core
When you issue this command, VxLang only performs obfuscation behavior, and does not perform VxLang core estimation and packing behavior.
The changed example is shown below:
Precautions
Code obfuscation tools cannot accurately understand all contexts. Therefore, users must protect their code by being alert to dangerous situations. When using code obfuscation and virtualization, be mindful of the following situations.
Compiler Optimization Considerations
If you’re using the VxLang SDK to obfuscate or virtualize your code, ensure that you check the compiler optimization settings. When compiler optimization is enabled, your code might be merged in ways that differ from how you originally wrote it. If VxLang SDK functions are referenced within this merged code, the Begin/End positions might change, resulting in less obfuscation than intended.
- Here’s how to fix the problem:
- Turn off compiler optimization.
- Disable code optimization for specific functions or pages at the code level.
#pragma optimize("", off) /// turn off optimization .. void ObfuscationTest() { VL_OBFUSCATION_BEGIN; // ... VL_OBFUSCATION_END; return; }
- Use MAP/PDB-based obfuscation/virtualization.
- It is described in the next chapter.
Potential Code Generation
Be aware of the following code pattern that may occur during the obfuscation and virtualization process:
- In this example, the jmp rax instruction causes a jump from the VxLang region back to the original code. This type of code can occur when table-based operations are used in a switch-case statement.
...
call _VXLANG_BEGIN
jmp L1
L0:
jmp EXIT
L1:
lea rax, $L0
jmp rax
EXIT:
call _VXLANG_END
...
Here’s how to get around this:
/*
For switch-case statements, it is very dangerous to obfuscate the entire syntax.
When compiled, it is moved to an unknown location (jmp reg), as shown below, and the obfuscation tool interprets it as it is,
so the code is moved to the original location where it was erased.
*** Therefore, for switch-case statements, it is safe to obfuscate the case internals as shown in the sample.
*/
/*
sub rsp,28
dec ecx
cmp ecx,E
ja tutorial64.vxm.7FF6E9C4133F
;
movsxd rax,ecx
lea rdx,qword ptr ds:[7FF6E9C40000]
mov ecx,dword ptr ds:[rdx+rax*4+3710]
add rcx,rdx
jmp rcx ; ***** Danger code ..
;
lea rdx,qword ptr ds:[<"Case 1"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 2"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 3"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 4"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 5"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 6"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 7"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 8"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 9"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 10"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 11"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 12"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 13"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 14"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Case 15"...>]
jmp tutorial64.vxm.7FF6E9C41346
lea rdx,qword ptr ds:[<"Default case"...>]
mov rcx,qword ptr ds:[<class std::basic_ostream<char, struct std::char_traits<char>> std::cout>]
call <tutorial64.vxm.class std::basic_ostream<char, struct std::char_traits<char>> & __cdecl std::operator<<<struct std::char_traits<char>>(class std::basic_ostream<char, struct std::char_traits<char>> &, char const *)>
lea rdx,qword ptr ds:[<class std::basic_ostream<char, struct std::char_traits<char>> & __cdecl std::endl<char, struct std::char_traits<char>>(class std::basic_ostream<char, struct std::char_traits<char>> &)>]
mov rcx,rax
add rsp,28
jmp qword ptr ds:[<public: class std::basic_ostream<char, struct std::char_traits<char>> & __cdecl std::basic_ostream<char, struct std::char_traits<char>>::operator<<(class std::basic_ostream<char, struct std::char_traits<char>> & (__cdecl *)(class std::basic_ostream<char, >]
*/
#pragma optimize("", off)
void Warning_SwitchCaseTest(int value) {
switch (value) {
case 1:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 1" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 2:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 2" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 3:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 3" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 4:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 4" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 5:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 5" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 6:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 6" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 7:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 7" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 8:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 8" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 9:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 9" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 10:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 10" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 11:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 11" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 12:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 12" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 13:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 13" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 14:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 14" << std::endl;
VL_VIRTUALIZATION_END;
break;
case 15:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Case 15" << std::endl;
VL_VIRTUALIZATION_END;
break;
default:
VL_VIRTUALIZATION_BEGIN;
std::cout << " Default" << std::endl;
VL_VIRTUALIZATION_END;
break;
}
return;
}
#pragma optimize("", off)
void SwitchCaseTest() {
VL_VIRTUALIZATION_BEGIN;
for (int i = 0; i < 16; ++i) {
Warning_SwitchCaseTest(i);
}
VL_VIRTUALIZATION_END;
return;
}
Exception Handling
VxLang currently only supports SEH (Structured Exception Handling). Therefore, be cautious when using it in conjunction with try-catch blocks.
- As of version
2.1.6.1
, MSVC C++ exception handling has been added, but please be aware that the context is unstable.- This is explained in the next chapter.
#pragma optimize("", off)
void ObfuscationSEHTest() {
VL_OBFUSCATION_BEGIN;
__try {
printf("SEH Test \n");
__debugbreak();
}
__except (1) {
printf(" Except \n");
}
VL_OBFUSCATION_END;
return;
}