use std::ffi::CString;
use std::sync::Once;
type VmpBeginFn = unsafe extern "C" fn(marker_name: *const i8);
type VmpBeginVirtualizationFn = unsafe extern "C" fn(marker_name: *const i8);
type VmpBeginMutationFn = unsafe extern "C" fn(marker_name: *const i8);
type VmpBeginUltraFn = unsafe extern "C" fn(marker_name: *const i8);
type VmpEndFn = unsafe extern "C" fn();
type VmpIsDebuggerPresentFn = unsafe extern "C" fn(check_kernel: i32) -> i32;
type VmpIsVirtualMachinePresentFn = unsafe extern "C" fn() -> i32;
type VmpDecryptStringAFn = unsafe extern "C" fn(value: *const i8) -> *const i8;
type VmpDecryptStringWFn = unsafe extern "C" fn(value: *const u16) -> *const u16;
type VmpFreeStringFn = unsafe extern "C" fn(value: *const std::ffi::c_void);
struct VmpSdk {
begin: Option<VmpBeginFn>,
begin_virtualization: Option<VmpBeginVirtualizationFn>,
begin_mutation: Option<VmpBeginMutationFn>,
begin_ultra: Option<VmpBeginUltraFn>,
end: Option<VmpEndFn>,
is_debugger_present: Option<VmpIsDebuggerPresentFn>,
is_virtual_machine_present: Option<VmpIsVirtualMachinePresentFn>,
decrypt_string_a: Option<VmpDecryptStringAFn>,
decrypt_string_w: Option<VmpDecryptStringWFn>,
free_string: Option<VmpFreeStringFn>,
loaded: bool,
}
static INIT: Once = Once::new();
static mut SDK: VmpSdk = VmpSdk {
begin: None,
begin_virtualization: None,
begin_mutation: None,
begin_ultra: None,
end: None,
is_debugger_present: None,
is_virtual_machine_present: None,
decrypt_string_a: None,
decrypt_string_w: None,
free_string: None,
loaded: false,
};
fn load_sdk() {
#[inline(always)]
fn sym(s: &str) -> Vec<u8> {
let mut v = s.as_bytes().to_vec();
v.push(0);
v
}
INIT.call_once(|| {
#[cfg(target_os = "windows")]
let dll_name = obfstr::obfstr!("VMProtectSDK64.dll").to_string();
#[cfg(target_os = "linux")]
let dll_name = obfstr::obfstr!("libVMProtectSDK.so").to_string();
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
let dll_name = obfstr::obfstr!("libVMProtectSDK.so").to_string();
let lib = match unsafe { libloading::Library::new(&dll_name) } {
Ok(lib) => lib,
Err(_) => {
return;
}
};
unsafe {
SDK.begin = lib
.get::<VmpBeginFn>(&sym(obfstr::obfstr!("VMProtectBegin")))
.ok()
.map(|f| *f);
SDK.begin_virtualization = lib
.get::<VmpBeginVirtualizationFn>(&sym(obfstr::obfstr!("VMProtectBeginVirtualization")))
.ok()
.map(|f| *f);
SDK.begin_mutation = lib
.get::<VmpBeginMutationFn>(&sym(obfstr::obfstr!("VMProtectBeginMutation")))
.ok()
.map(|f| *f);
SDK.begin_ultra = lib
.get::<VmpBeginUltraFn>(&sym(obfstr::obfstr!("VMProtectBeginUltra")))
.ok()
.map(|f| *f);
SDK.end = lib
.get::<VmpEndFn>(&sym(obfstr::obfstr!("VMProtectEnd")))
.ok()
.map(|f| *f);
SDK.is_debugger_present = lib
.get::<VmpIsDebuggerPresentFn>(&sym(obfstr::obfstr!("VMProtectIsDebuggerPresent")))
.ok()
.map(|f| *f);
SDK.is_virtual_machine_present = lib
.get::<VmpIsVirtualMachinePresentFn>(&sym(obfstr::obfstr!("VMProtectIsVirtualMachinePresent")))
.ok()
.map(|f| *f);
SDK.decrypt_string_a = lib
.get::<VmpDecryptStringAFn>(&sym(obfstr::obfstr!("VMProtectDecryptStringA")))
.ok()
.map(|f| *f);
SDK.decrypt_string_w = lib
.get::<VmpDecryptStringWFn>(&sym(obfstr::obfstr!("VMProtectDecryptStringW")))
.ok()
.map(|f| *f);
SDK.free_string = lib
.get::<VmpFreeStringFn>(&sym(obfstr::obfstr!("VMProtectFreeString")))
.ok()
.map(|f| *f);
SDK.loaded = true;
std::mem::forget(lib);
}
});
}
pub fn is_sdk_loaded() -> bool {
load_sdk();
unsafe { SDK.loaded }
}
pub fn vmp_begin(marker: &str) {
load_sdk();
if let Some(f) = unsafe { SDK.begin } {
if let Ok(name) = CString::new(marker) {
unsafe { f(name.as_ptr()) };
}
}
}
pub fn vmp_begin_virtualization(marker: &str) {
load_sdk();
if let Some(f) = unsafe { SDK.begin_virtualization } {
if let Ok(name) = CString::new(marker) {
unsafe { f(name.as_ptr()) };
}
}
}
pub fn vmp_begin_mutation(marker: &str) {
load_sdk();
if let Some(f) = unsafe { SDK.begin_mutation } {
if let Ok(name) = CString::new(marker) {
unsafe { f(name.as_ptr()) };
}
}
}
pub fn vmp_begin_ultra(marker: &str) {
load_sdk();
if let Some(f) = unsafe { SDK.begin_ultra } {
if let Ok(name) = CString::new(marker) {
unsafe { f(name.as_ptr()) };
}
}
}
pub fn vmp_end() {
load_sdk();
if let Some(f) = unsafe { SDK.end } {
unsafe { f() };
}
}
pub fn is_debugger_present(check_kernel: bool) -> bool {
load_sdk();
if let Some(f) = unsafe { SDK.is_debugger_present } {
unsafe { f(if check_kernel { 1 } else { 0 }) != 0 }
} else {
false
}
}
pub fn is_virtual_machine() -> bool {
load_sdk();
if let Some(f) = unsafe { SDK.is_virtual_machine_present } {
unsafe { f() != 0 }
} else {
false
}
}
pub fn decrypt_string(value: &str) -> String {
load_sdk();
if let Some(f) = unsafe { SDK.decrypt_string_a } {
if let Ok(cstr) = CString::new(value) {
let decrypted = unsafe { f(cstr.as_ptr()) };
if !decrypted.is_null() {
let result = unsafe { std::ffi::CStr::from_ptr(decrypted) }
.to_string_lossy()
.into_owned();
if let Some(free_fn) = unsafe { SDK.free_string } {
unsafe { free_fn(decrypted as *const std::ffi::c_void) };
}
return result;
}
}
}
value.to_string()
}
pub fn anti_debug_check<F: FnOnce()>(check_kernel: bool, on_detected: F) {
if is_debugger_present(check_kernel) {
on_detected();
}
}
pub fn anti_vm_check<F: FnOnce()>(on_detected: F) {
if is_virtual_machine() {
on_detected();
}
}
#[macro_export]
macro_rules! vmp_protect {
($marker:expr, Ultra, $body:block) => {{
$crate::utils::protection::vmp_begin_ultra($marker);
let __vmp_result = (|| $body)();
$crate::utils::protection::vmp_end();
__vmp_result
}};
($marker:expr, Mutation, $body:block) => {{
$crate::utils::protection::vmp_begin_mutation($marker);
let __vmp_result = (|| $body)();
$crate::utils::protection::vmp_end();
__vmp_result
}};
($marker:expr, Virtualization, $body:block) => {{
$crate::utils::protection::vmp_begin_virtualization($marker);
let __vmp_result = (|| $body)();
$crate::utils::protection::vmp_end();
__vmp_result
}};
}