简介
Virbox Protector工具的 SDK 标签静态载入到需要保护的函数当中,生成可执行程序,Virbox Protector 加壳工具解析该程序界面就能够分析出SDK标识的函数,这样就能帮助用户找到核心代码所在的位置。
目前支持的语言:c\c++\c#\oc\swift\java
环境
1.安装 Virbox Protector 工具;
2.包括 SDK 标签所用的静态库和动态库;
windows的路径:C:\Program Files\senseshield\Virbox Protector 3\sdk\windows\x86
Linux的路径:C:\Program Files\senseshield\Virbox Protector 3\sdk\linux,包括arm和x86架构
macOS的路径:C:\Program Files\senseshield\Virbox Protector 3\sdk\darwin,包括arm和x86架构
Android的路径:C:\Program Files\senseshield\Virbox Protector 3\sdk\android,包括arm和x86架构
3.demo示例
demo的路径:C:\Program Files\senseshield\Virbox Protector 3\example\sdk
程序类型
Native程序
Native程序包括Windows、Linux、ARM Linux、Android和macOS系统编译的可执行程序或动态库;
安装目录下包括SDK标签所用的头文件:
C:\Program Files\senseshield\Virbox Protector 3\sdk\inc\virbox.h
标签示例路径:C:\Program Files\senseshield\Virbox Protector 3\example\sdk\C
注意:
1.若一段代码两个标签同时先后顺序写,则在前面的标签不会生效,在后面的标签生效。
2.若只写Begin不写end,加壳工具解析时发现未闭合的标签则默认在保护日志
中进行提示。
函数标签
1.接口:int VB_API_CALL VBProtectBegin(const char *tag);
含义: 常规保护(默认为虚拟化保护),需要和VBProtectEnd
结对使用;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBProtectBegin("add_vb");
ret = x + y;
printf("x+y=%d\n", ret);
VBProtectEnd();
return ret;
}
2.接口:int VB_API_CALL VBVirtualizeBegin(const char *tag);
含义: 代码虚拟化保护,需要和VBProtectEnd
结对使用;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBVirtualizeBegin("add_vb");
ret = x + y;
printf("x+y=%d\n", ret);
VBProtectEnd();
return ret;
}
3.接口:int VB_API_CALL VBMutateBegin(const char *tag);
含义:指代码混淆保护,需要和VBProtectEnd
结对使用;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBMutateBegin("add_vb");
ret = x + y;
printf("x+y=%d\n", ret);
VBProtectEnd();
return ret;
}
4.接口:int VB_API_CALL VBSnippetBegin(const char *tag);
含义:代码碎片化保护,该功能仅支持许可壳,需要和VBProtectEnd
结对使用;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBSnippetBegin("add_vb");
ret = x + y;
printf("x+y=%d\n", ret);
VBProtectEnd();
return ret;
}
5.接口:void VB_API_CALL VBProtectEnd(void);
含义:指定代码范围结束。
6.接口:int VB_API_CALL VBVirtualizeFunction(const char *tag);
含义:虚拟化保护,不需要和end结合使用;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBVirtualizeFunction("add_vb");
ret = x + y;
printf("x+y=%d\n", ret);
return ret;
}
7.接口:int VB_API_CALL VBMutateFunction(const char *tag);
含义:代码混淆保护,不需要和end结合使用;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBMutateFunction("add_vb");
ret = x + y;
printf("x+y=%d\n", ret);
return ret;
}
8.接口:int VB_API_CALL VBProtectVerifyImage(void);
含义:内存校验功能;
代码示例:
int add(int x, int y)
{
int ret = 0;
VBProtectVerifyImage();
ret = x + y;
printf("x+y=%d\n", ret);
return ret;
}
9.注意事项:
1)只能静态加载,不支持动态加载dll(即 LoadLibrary 的方式);
2)VBProtectBegin
、VBVirtualizeBegin
等接口,传入的字符串参数,不能与其他函数共用;
3)传入的字符串参数保证为 ANSII 码的形式,这样显示在界面上的函数名称才正确,否则就会显示为乱码;
4)每个Begin
对应一个End
,并且一个函数里面不要出现多对 Begin+End,Begin/End不能嵌套使用。;
5)如果标签标记的保护方式和ssp配置文件中保存的保护方式冲突了,以ssp配置文件中的保护方式为准;
6)标签Begin和End
之间的代码最好大于3行(若标记的代码正汇编生成的指令小于15个字节,则加壳工具界面上不会显示该标记函数);
7)SDK动态库分32和64位,在使用的时候要开发者根据要编译的程序进行加载对应的库。
字符串加解密
1.接口:void *VB_API_CALL VBProtectDecrypt(void *dst, const void *src, int size);
含义:字符串加密,程序运行时,字符串在调用者内存里,不需要进行释放;
代码示例:
int test_decrypt(){ char buf[1024int test_decrypt()
{
char buf[1024];
#define ENCRYPTED_DATA "this is decrypted data,please...testing passing"
puts(__FUNCTION__);
VBProtectDecrypt(buf, ENCRYPTED_DATA, sizeof(ENCRYPTED_DATA));
puts(buf);
return strcmp(buf, ENCRYPTED_DATA);
}
注意:VBProtectDecrypt被加密的字符串的缓冲区的长度必须是16的倍数;
2.接口:const void* VB_API_CALL VBDecryptData(const void *data, int size);
含义:字符串加密,一般适用于密钥数据加密,但数据和长度也必须是常量,程序运行,字符串会在堆内存里;
可以和VBFreeData配合使用,目的是及时释放堆内存,防止调试工具能在堆内存里搜到字符串;
代码示例:
int test_encrypt_key()
{
static const unsigned char g_key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
unsigned char* key = VBDecryptData(g_key, sizeof(g_key));
for (int i = 0; i != sizeof(g_key); ++i)
{
printf("%02x ", key[i]);
}
printf("\n");
return 0;
}
3.接口:#define VBDecryptStringA(_X_) ((char*)VBDecryptData(_X_, sizeof(_X_)))
含义:指标准的字符串加密,只要是静态变量或全局变量就可以使用且加密的字符串必须是常量,VBDecryptStringA是对VBDecryptData的封装,使用时只需使用VBDecryptStringA即可。
代码示例:
1)直接字符串加密:
VBDecryptStringA("test_string");
Copy
2)局部静态变量:
static char g_string[] = "test_string";
static const char g_string[] = "test_string";
Copy
3)全局变量:
char g_test_string[] = "test_string";
const char g_test_string[] = "test_string";
4)如果程序过于复杂可能会导致解析不出加密的数据,而在加壳时报错,使用 -fpic 或 -fpie 加上 -O2;
编译的32位Linux 程序此类情况比较明显,建议降低代码的复杂度。
5)编译器可能会将相同的常量字符串合并为同一个,如果只加密了其中一个,会导致出错;
代码示例:
const char* a = "test_string";
const char* b = VBDecryptStringA("test_string");
printf("a = %s, b = %s\n", a, b);
这种情况,编译后的程序进行加壳,打印字符串a会是乱码。
4.接口:#define VBDecryptStringW(_X_) ((wchar_t*)VBDecryptData(_X_, sizeof(_X_)))
含义:指宽字符串加密,只要是静态变量或全局变量就可以使用且加密的字符串必须是常量,,VBDecryptStringW是对VBDecryptData的封装,使用时只需使用VBDecryptStringW即可。
代码示例:
VBDecryptStringW(L"test_string");
5.接口:void VB_API_CALL VBFreeData(const void *data);
#define VBFreeString(_X_) VBFreeData(_X_)
含义:VBFreeData只能释放堆内存即VBDecryptData,若不及时释放堆内存,则使用OD\ida等工具动态调试就能看到字符串;
VBFreeString是对VBFreeData的简单封装,使用VBFreeString
宏可以将字符串的指针传递给VBFreeData
函数来释放相应的内存空间,使用时只需使用VBFreeString即可。
static char g_string[] = "test_string";
VBFreeString(g_string);
macOS程序
macOS程序和Native程序所用到的sdk标签是一样的,只是在编译项目时Xcode需要进行一些配置,下面简述OC和swift使用标签时的流程:
1.OC语言
1)将virbox.h放到工程中,使用Xcode打开工程后,将virbox.h添加到工程中;
2)选中TARGETS->Build Settings->Architectures,查看项目的架构是arm64还是x64还是Fat格式的;
3)从Virbox Protector工具的安装目录sdk/darwin下找到对应架构的libvirbox64.dylib,选中TARGETS->Build Phases->Link Binary选项,添加libvirbox64.dylib;
4)在代码中引用virbox.h,并添加标签;
代码参考示例,:
#import "virbox.h"
-(void)readLoad
{
//可以根据自己需求选择其他标签
VBMutateBegin("load_test"); //代码混淆
for (int i=0; i<array.count; i++) {
News * news=[[News alloc]init];
news.content=array[i];
[_newsArray addObject:news];
}
VBProtectEnd();
return NO;
}
- (BOOL)isImageSetFolder:(NSString *)folder
{
//可以根据自己需求选择其他标签
VBVirtualizeFunction("reset_test"); //代码虚拟化
if ([folder hasSuffix:kSuffixImageSet]
|| [folder hasSuffix:kSuffixAppIcon]
|| [folder hasSuffix:kSuffixLaunchImage]) {
return YES;
}
return NO;
}
Copy
2.Swift语言
1)将virbox.h放到工程中,使用Xcode打开工程后,将virbox.h添加到工程中,并将virbox.h设置为桥接文件;
TARGETS->Build Settings->Swift Compiler->Object-C Bridging Header->项目名/virbox.h
2)选中TARGETS->Build Settings->Architectures,查看项目的架构是arm64还是x64还是Fat格式的;
3)从Virbox Protector工具的安装目录sdk/darwin下找到对应架构的libvirbox64.dylib,选中TARGETS->Build Phases->Link Binary选项,添加libvirbox64.dylib;
4)在代码中直接添加标签,且需要给标签添加变量名;
代码参考示例:
struct ContentView: View {
var body: some View {
//可以根据自己需求选择其他标签
var body_begin = VBMutateBegin("body_test")//代码混淆
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())]
var body_end = VBProtectEnd()//代码段结束
}
}
struct RdioApp: App {
var body: some Scene {
//可以根据自己需求选择其他标签
var myva = VBVirtualizeFunction("test") //代码虚拟化
HStack(spacing: 24) {
Image(systemName: "backward.fill")
.font(.title3)
Image(systemName: "play.fill")
.font(.title)
Image(systemName: "forward.fill")
.font(.title3)
}
}
}
3.程序运行
1)编译后的app直接运行需要依赖libvirbox64.dylib
,即将对应app程序架构的libvirbox64.dylib
拷贝到/usr/local/lib/libvirbox64.dylib
,然后再运行编译后的app,即可正常运行;
如果不拷贝则会报以下错误:
注:若是arm架构和FAT格式的libvirbox64.dylib
,需要先对其进行签名后,再进行拷贝。
2)将编译后的app拖入到加壳工具中,函数选项即可显示标记的函数;
对文件进行保护,则保护后的程序即可运行,不会依赖libvirbox64.dylib
。
NET程序
.NET程序支持代码加密、代码混淆和代码虚拟化三种函数保护方式,sdk标签参考微软标准的写法,程序编译成功后,将编译好的程序拖入到加壳工具界面,界面会显示代码中设置的函数保护方式:
标签示例参考:C:\Program Files\senseshield\Virbox Protector 3\example\sdk\dotnet_sdk_demo
代码书写方式:
//命名
[Obfuscation(Feature = "Rename", Exclude = false)]//名称混淆
public class main
{
[Obfuscation(Feature = "Mutate", Exclude = false)]//代码混淆
public static void test1(string[] args)
{
System.Console.WriteLine("hello Virbox.Mutate!");
}
[Obfuscation(Feature = "Encrypt", Exclude = false)]//代码加密
public static void test2(string[] args)
{
System.Console.WriteLine("hello Virbox.Encrypt!");
}
[Obfuscation(Feature = "Virtualization", Exclude = false)]//代码虚拟化
public static void test3(string[] args)
{
System.Console.WriteLine("hello Virbox.Virtualize!");
}
}
Copy
注意事项:
1.可以添加到类名前,但如果方法也设置了sdk标签,那么将会优先显示方法里设置的。
2.函数标签和名称混淆标签不能添加到代码里,只能添加到类和方法名前面。
Java程序
JAVA程序支持代码虚拟化保护方式,代码中设置VBVirtualize注解,并在函数上引用,程序编译成功后,将编译好的程序拖入到加壳工具界面,界面会显示代码中设置的函数保护方式:
注意:文件名必须是VBVirtualize,包名必须是virbox,否则标记的标签加壳工具无法识别到。
1.新建VBVirtualize.java,内容是:
package virbox;
public @interface VBVirtualize{}
2.调用方式:
import virbox.VBVirtualize;
@VBVirtualize //可添加到类上面,所有的方法都会默认保护
public class Main {
public static void main(String[] args) {
System.out.println("hello");
test_vir();
}
@VBVirtualize //可添加到方法上面,只保护该方法
public static void test_vir()
{
System.out.println("test_vir");
}
}
Android程序
适用场景
使用虚拟化标签标记过的类和方法后,编译出的Android APK/AAB应用拖入到Virbox Protector界面进行解析时,默认会显示标记的类和方法。
若使用proguard混淆后,无法判断函数名称,若使用虚拟化标签标记过的类和方法后,在对该应用保护时就不需要去查找对应的类和方法名。
标签示例参考:C:\Program Files\senseshield\Virbox Protector 3\example\sdk\APK
注意:
1)若开启proguard编译的Android APK/AAB应用,有些函数会被优化,导致标记的函数不会解析出来。
2)文件名必须是VBVirtualize,包名必须是virbox,否则标记的标签加壳工具无法识别到。
Copy
操作流程
1.打开Android工程,新建一个目录,命名为virbox;
2.创建Java Class;
3.选择Interface,命名为VBVirtualize;
4.在VBVirtualize.class中添加以下代码;
package virbox;
import androidx.annotation.Keep;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Keep
@Retention(RetentionPolicy.RUNTIME)
public @interface VBVirtualize {
}
5.在其他类上调用VBVirtualize;
@VBVirtualize若放在类上面,则表示该类里的所有方法都进行保留;
import virbox.VBVirtualize;
@VBVirtualize
public class SecondFragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
FragmentSecondBinding inflate = FragmentSecondBinding.inflate(inflater, container, false);
this.binding = inflate;
return inflate.getRoot();
}
public void onDestroyView() {
super.onDestroyView();
this.binding = null;
}
}
@VBVirtualize若放在方法上面,则表示该方法都进行保留,其他方法不会进行保留
import virbox.VBVirtualize;
public class SecondFragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
FragmentSecondBinding inflate = FragmentSecondBinding.inflate(inflater, container, false);
this.binding = inflate;
return inflate.getRoot();
}
@VBVirtualize
public void onDestroyView() {
super.onDestroyView();
this.binding = null;
}
}
问题
1.标签、map、pdb文件谁的优先级高?
答:标签的优先级高且标签和map和pdb文件共存。
2.如果编译出来是arx,sdk标签也支持吗?
答:支持。
3、全局变量是否可以进行保护?
答:使用SDK标签VBDecryptStringA,PE、elf和macho格式的都可以。
4.begin end是只处理当前函数的函数体,如果函数A调用函数B,函数B使用了SDK标签,那么函数A也可以再使用SDK标签,这种情况属于嵌套么?
答:不属于嵌套。
6.c++程序,使用字符串加密sdk标签后,能使用OD调试器可以看到么?
答:使用OD在堆内存里能搜到,可以每次用完调用 VBFreeString(xxx)给释放了,就在堆内存里搜不到了。
7.C#程序,字符串加密,能防止从内存中dump出字符串么?OD调试器可以看到么?
答:不能防止从内存中dump出字符串,OD调试器直接扫是扫不到的,如果找到了真正使用字符串的地方断下来,那是可以看到的。