├── .gitignore ├── README.md ├── VMProtect ├── VMProtect.go ├── VMProtectSDK.h └── go.mod ├── example ├── VMProtectLicense.ini ├── VMProtectSDK64.dll ├── build-mac-x64-Mach-O.sh ├── build-win-x64-PE-cross.sh ├── build-win-x64-PE-llvm-mingw.bat ├── go.mod ├── libVMProtectSDK.dylib ├── script.lua └── vm.go └── pic.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VMProtectSDK-Golang 2 | ``UnOfficial VMProtectSDK for Golang`` 3 | 4 | ```` 5 | Due to the particularity of Golang, VMP is not compatible with it. 6 | 7 | For example, there is no 0 at the end of the Golang string, 8 | 9 | and ABI is non-standard(GO x64 ABI: RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11) 10 | 11 | If use CGO, VMP does not recognize Marker and the string need to encrypt, 12 | 13 | After a while of research, I solved the compatibility of some VMProtect with Golang. 14 | 15 | Now VMProtect can recognize the address of VMProtectBegin and VMProtectEnd. 16 | 17 | MarkerName and the string need to encrypt all can be detected(work well on mac and win). 18 | 19 | Because of I didn't have Web License Manager, so the correlation function has not been implemented. 20 | 21 | Most functions are Bind. 22 | ```` 23 | ```` 24 | * Test work well on window 11 and Mac OS 12.3.1 (Go 1.18.1) 25 | 26 | * But please note that due to the use of some unconventional methods, it may be unsafe. 27 | ```` 28 | ```` 29 | Guide: 30 | 31 | Copy "VMProtect" and "example" folder to "/Users/YourName/go/src/" 32 | 33 | Please install the c compiler if not. 34 | 35 | Mac: xcode-select --install 36 | Mac Cross build PE: sudo port install x86_64-w64-mingw32-gcc 37 | Windows: download llvm-mingw 38 | 39 | Modify the build script,set the CC 40 | 41 | run the script to build 42 | 43 | ```` 44 | ```` 45 | Tip: 46 | 47 | 1.\x00 or \000 must be added after string ,like VMProtect.BeginUltra("Marker\x00"). 48 | 49 | 2.Don't use the -gcflags "-N -l" command to compile , Otherwise VMP cannot recognize the Marker. 50 | 51 | 3.Must use -ldflags "-s -w" to strip the Symbol,Otherwise VMP cannot recognize the file. 52 | 53 | 4.Refer to the files in the example folder and modify the GoPath. 54 | 55 | 5.If necessary, add scripts in script.lua for additional protection. 56 | 57 | 6.Linux has not been tested. Maybe, but some of the code needs to be modified. 58 | 59 | 7.You must use VMProtect.GoString to convert func DecryptString's char to string,not C.GoString. 60 | 61 | 8.32-bit systems are not supported.. 62 | 63 | 9.If you import other projects from github, please set GO111MODULE=on and modify go.mod, 64 | replace VMProtect => /Users/YourName/go/src/VMProtect. 65 | ```` 66 | -------------------------------------------------------------------------------- /VMProtect/VMProtect.go: -------------------------------------------------------------------------------- 1 | package VMProtect 2 | 3 | /* 4 | #include 5 | #include "VMProtectSDK.h" 6 | */ 7 | import "C" 8 | 9 | import ( 10 | "runtime" 11 | "strconv" 12 | "unsafe" 13 | ) 14 | 15 | const ( 16 | SERIAL_STATE_SUCCESS = 0 17 | SERIAL_STATE_FLAG_CORRUPTED = 0x00000001 18 | SERIAL_STATE_FLAG_INVALID = 0x00000002 19 | SERIAL_STATE_FLAG_BLACKLISTED = 0x00000004 20 | SERIAL_STATE_FLAG_DATE_EXPIRED = 0x00000008 21 | SERIAL_STATE_FLAG_RUNNING_TIME_OVER = 0x00000010 22 | SERIAL_STATE_FLAG_BAD_HWID = 0x00000020 23 | SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED = 0x00000040 24 | ) 25 | 26 | //go:linkname vmprotectBegin VMProtectBegin 27 | //go:noescape 28 | func vmprotectBegin(*string) unsafe.Pointer 29 | 30 | //go:linkname vmprotectBeginVirtualization VMProtectBeginVirtualization 31 | //go:noescape 32 | func vmprotectBeginVirtualization(*string) unsafe.Pointer 33 | 34 | //go:linkname vmprotectBeginMutation VMProtectBeginMutation 35 | //go:noescape 36 | func vmprotectBeginMutation(*string) unsafe.Pointer 37 | 38 | //go:linkname vmprotectBeginUltra VMProtectBeginUltra 39 | //go:noescape 40 | func vmprotectBeginUltra(*string) unsafe.Pointer 41 | 42 | //go:linkname vmprotectBeginUltraLockByKey VMProtectBeginUltraLockByKey 43 | //go:noescape 44 | func vmprotectBeginUltraLockByKey(*string) unsafe.Pointer 45 | 46 | //go:linkname vmprotectBeginVirtualizationLockByKey VMProtectBeginVirtualizationLockByKey 47 | //go:noescape 48 | func vmprotectBeginVirtualizationLockByKey(*string) unsafe.Pointer 49 | 50 | //go:linkname End VMProtectEnd 51 | //go:noescape 52 | func End() 53 | 54 | //go:linkname vmprotectBeginD VMProtectBegin 55 | //go:noescape 56 | func vmprotectBeginD(string, *string, string) unsafe.Pointer 57 | 58 | //go:linkname vmprotectBeginVirtualizationD VMProtectBeginVirtualization 59 | //go:noescape 60 | func vmprotectBeginVirtualizationD(string, *string, string) unsafe.Pointer 61 | 62 | //go:linkname vmprotectBeginMutationD VMProtectBeginMutation 63 | //go:noescape 64 | func vmprotectBeginMutationD(string, *string, string) unsafe.Pointer 65 | 66 | //go:linkname vmprotectBeginUltraD VMProtectBeginUltra 67 | //go:noescape 68 | func vmprotectBeginUltraD(string, *string, string) unsafe.Pointer 69 | 70 | //go:linkname vmprotectBeginUltraLockByKeyD VMProtectBeginUltraLockByKey 71 | //go:noescape 72 | func vmprotectBeginUltraLockByKeyD(string, *string, string) unsafe.Pointer 73 | 74 | //go:linkname vmprotectBeginVirtualizationLockByKeyD VMProtectBeginVirtualizationLockByKey 75 | //go:noescape 76 | func vmprotectBeginVirtualizationLockByKeyD(string, *string, string) unsafe.Pointer 77 | 78 | //go:linkname vmprotectDecryptStringA VMProtectDecryptStringA 79 | //go:noescape 80 | func vmprotectDecryptStringA(*string) *C.char 81 | 82 | //go:linkname vmprotectDecryptStringAD VMProtectDecryptStringA 83 | //go:noescape 84 | func vmprotectDecryptStringAD(string, *string, string) *C.char 85 | 86 | //go:linkname call runtime.asmcgocall 87 | //go:noescape 88 | func call(fn, arg unsafe.Pointer) int32 89 | 90 | //go:linkname callbool runtime.asmcgocall 91 | //go:noescape 92 | func callbool(fn, arg unsafe.Pointer) bool 93 | 94 | func GoString(cchar *C.char) string { 95 | return C.GoString(cchar) 96 | } 97 | 98 | func GetCurrentHWID() (hwid string) { 99 | nSize := C.VMProtectGetCurrentHWID(nil, 0) 100 | b := make([]byte, nSize) 101 | hw := (*C.char)(unsafe.Pointer(&b)) 102 | C.VMProtectGetCurrentHWID(hw, nSize) 103 | hwid = C.GoStringN(hw, nSize) 104 | return hwid 105 | } 106 | 107 | func SetSerialNumber(serial string) int { 108 | b := []byte(serial) 109 | cserial := (*C.char)(unsafe.Pointer(&b[0])) 110 | return int(call(C.VMProtectSetSerialNumber, unsafe.Pointer(cserial))) 111 | } 112 | 113 | func GetSerialNumberState() int { 114 | return int(call(C.VMProtectGetSerialNumberState, unsafe.Pointer(nil))) 115 | } 116 | 117 | func GetUser() (user string) { 118 | var sd C.VMProtectSerialNumberData 119 | if C.VMProtectGetSerialNumberData(&sd, C.sizeof_VMProtectSerialNumberData) { 120 | for _, v := range sd.wUserName { 121 | if v != 0 { 122 | user += string(v) 123 | } 124 | } 125 | return user 126 | } 127 | return 128 | } 129 | 130 | func GetEmail() (email string) { 131 | var sd C.VMProtectSerialNumberData 132 | if C.VMProtectGetSerialNumberData(&sd, C.sizeof_VMProtectSerialNumberData) { 133 | for _, v := range sd.wEMail { 134 | if v != 0 { 135 | email += string(v) 136 | } 137 | } 138 | return email 139 | } 140 | return 141 | } 142 | 143 | func GetExpireDate() (date string) { 144 | var sd C.VMProtectSerialNumberData 145 | if C.VMProtectGetSerialNumberData(&sd, C.sizeof_VMProtectSerialNumberData) { 146 | year := *(*uint16)(unsafe.Pointer(&sd.dtExpire.wYear)) 147 | month := *(*uint8)(unsafe.Pointer(&sd.dtExpire.bMonth)) 148 | day := *(*uint8)(unsafe.Pointer(&sd.dtExpire.bDay)) 149 | date := strconv.Itoa(int(year)) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(int(day)) 150 | return date 151 | } 152 | return 153 | } 154 | 155 | func GetMaxBuild() (date string) { 156 | var sd C.VMProtectSerialNumberData 157 | if C.VMProtectGetSerialNumberData(&sd, C.sizeof_VMProtectSerialNumberData) { 158 | year := *(*uint16)(unsafe.Pointer(&sd.dtMaxBuild.wYear)) 159 | month := *(*uint8)(unsafe.Pointer(&sd.dtMaxBuild.bMonth)) 160 | day := *(*uint8)(unsafe.Pointer(&sd.dtMaxBuild.bDay)) 161 | date := strconv.Itoa(int(year)) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(int(day)) 162 | return date 163 | } 164 | return 165 | } 166 | 167 | func Begin(MarkerName string) unsafe.Pointer { 168 | if runtime.GOOS == "windows" { 169 | return vmprotectBegin(&MarkerName) 170 | } else { 171 | return vmprotectBeginD("", nil, MarkerName) 172 | } 173 | } 174 | 175 | func BeginVirtualization(MarkerName string) unsafe.Pointer { 176 | if runtime.GOOS == "windows" { 177 | return vmprotectBeginVirtualization(&MarkerName) 178 | } else { 179 | return vmprotectBeginVirtualizationD("", nil, MarkerName) 180 | } 181 | } 182 | 183 | func BeginMutation(MarkerName string) unsafe.Pointer { 184 | if runtime.GOOS == "windows" { 185 | return vmprotectBeginMutation(&MarkerName) 186 | } else { 187 | return vmprotectBeginMutationD("", nil, MarkerName) 188 | } 189 | } 190 | 191 | func BeginUltra(MarkerName string) unsafe.Pointer { 192 | if runtime.GOOS == "windows" { 193 | return vmprotectBeginUltra(&MarkerName) 194 | } else { 195 | return vmprotectBeginUltraD("", nil, MarkerName) 196 | } 197 | } 198 | 199 | func BeginUltraLockByKey(MarkerName string) unsafe.Pointer { 200 | if runtime.GOOS == "windows" { 201 | return vmprotectBeginUltraLockByKey(&MarkerName) 202 | } else { 203 | return vmprotectBeginUltraLockByKeyD("", nil, MarkerName) 204 | } 205 | } 206 | 207 | func BeginVirtualizationLockByKey(MarkerName string) unsafe.Pointer { 208 | if runtime.GOOS == "windows" { 209 | return vmprotectBeginVirtualizationLockByKey(&MarkerName) 210 | } else { 211 | return vmprotectBeginVirtualizationLockByKeyD("", nil, MarkerName) 212 | } 213 | } 214 | 215 | func DecryptStringA(EncryptStr string) (DecryptStr *C.char) { 216 | if runtime.GOOS == "windows" { 217 | return vmprotectDecryptStringA(&EncryptStr) 218 | } else { 219 | return vmprotectDecryptStringAD("", nil, EncryptStr) 220 | } 221 | } 222 | 223 | func IsDebuggerPresent(CheckKernelMode bool) bool { 224 | return bool(callbool(C.VMProtectIsDebuggerPresent, unsafe.Pointer(&CheckKernelMode))) 225 | } 226 | 227 | func IsVirtualMachinePresent() bool { 228 | return bool(callbool(C.VMProtectIsVirtualMachinePresent, unsafe.Pointer(nil))) 229 | } 230 | 231 | func IsProtected() bool { 232 | return bool(callbool(C.VMProtectIsProtected, unsafe.Pointer(nil))) 233 | } 234 | 235 | func IsValidImageCRC() bool { 236 | return bool(callbool(C.VMProtectIsValidImageCRC, unsafe.Pointer(nil))) 237 | } 238 | -------------------------------------------------------------------------------- /VMProtect/VMProtectSDK.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__APPLE__) || defined(__unix__) 4 | #define VMP_IMPORT 5 | #define VMP_API 6 | #define VMP_WCHAR unsigned short 7 | #else 8 | #define VMP_IMPORT __declspec(dllimport) 9 | #define VMP_API __stdcall 10 | #define VMP_WCHAR wchar_t 11 | #ifdef _WIN64 12 | #pragma comment(lib, "VMProtectSDK64.lib") 13 | #else 14 | #pragma comment(lib, "VMProtectSDK32.lib") 15 | #endif // _WIN64 16 | #endif // __APPLE__ || __unix__ 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | // protection 23 | VMP_IMPORT void VMP_API VMProtectBegin(const char *); 24 | VMP_IMPORT void VMP_API VMProtectBeginVirtualization(const char *); 25 | VMP_IMPORT void VMP_API VMProtectBeginMutation(const char *); 26 | VMP_IMPORT void VMP_API VMProtectBeginUltra(const char *); 27 | VMP_IMPORT void VMP_API VMProtectBeginVirtualizationLockByKey(const char *); 28 | VMP_IMPORT void VMP_API VMProtectBeginUltraLockByKey(const char *); 29 | VMP_IMPORT void VMP_API VMProtectEnd(void); 30 | 31 | // utils 32 | VMP_IMPORT bool VMP_API VMProtectIsProtected(); 33 | VMP_IMPORT bool VMP_API VMProtectIsDebuggerPresent(bool); 34 | VMP_IMPORT bool VMP_API VMProtectIsVirtualMachinePresent(void); 35 | VMP_IMPORT bool VMP_API VMProtectIsValidImageCRC(void); 36 | VMP_IMPORT const char * VMP_API VMProtectDecryptStringA(const char *value); 37 | VMP_IMPORT const VMP_WCHAR * VMP_API VMProtectDecryptStringW(const VMP_WCHAR *value); 38 | VMP_IMPORT bool VMP_API VMProtectFreeString(const void *value); 39 | 40 | // licensing 41 | enum VMProtectSerialStateFlags 42 | { 43 | SERIAL_STATE_SUCCESS = 0, 44 | SERIAL_STATE_FLAG_CORRUPTED = 0x00000001, 45 | SERIAL_STATE_FLAG_INVALID = 0x00000002, 46 | SERIAL_STATE_FLAG_BLACKLISTED = 0x00000004, 47 | SERIAL_STATE_FLAG_DATE_EXPIRED = 0x00000008, 48 | SERIAL_STATE_FLAG_RUNNING_TIME_OVER = 0x00000010, 49 | SERIAL_STATE_FLAG_BAD_HWID = 0x00000020, 50 | SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED = 0x00000040, 51 | }; 52 | 53 | #pragma pack(push, 1) 54 | typedef struct 55 | { 56 | unsigned short wYear; 57 | unsigned char bMonth; 58 | unsigned char bDay; 59 | } VMProtectDate; 60 | 61 | typedef struct 62 | { 63 | int nState; // VMProtectSerialStateFlags 64 | VMP_WCHAR wUserName[256]; // user name 65 | VMP_WCHAR wEMail[256]; // email 66 | VMProtectDate dtExpire; // date of serial number expiration 67 | VMProtectDate dtMaxBuild; // max date of build, that will accept this key 68 | int bRunningTime; // running time in minutes 69 | unsigned char nUserDataLength; // length of user data in bUserData 70 | unsigned char bUserData[255]; // up to 255 bytes of user data 71 | } VMProtectSerialNumberData; 72 | #pragma pack(pop) 73 | 74 | VMP_IMPORT int VMP_API VMProtectSetSerialNumber(const char *serial); 75 | VMP_IMPORT int VMP_API VMProtectGetSerialNumberState(); 76 | VMP_IMPORT bool VMP_API VMProtectGetSerialNumberData(VMProtectSerialNumberData *data, int size); 77 | VMP_IMPORT int VMP_API VMProtectGetCurrentHWID(char *hwid, int size); 78 | 79 | // activation 80 | enum VMProtectActivationFlags 81 | { 82 | ACTIVATION_OK = 0, 83 | ACTIVATION_SMALL_BUFFER, 84 | ACTIVATION_NO_CONNECTION, 85 | ACTIVATION_BAD_REPLY, 86 | ACTIVATION_BANNED, 87 | ACTIVATION_CORRUPTED, 88 | ACTIVATION_BAD_CODE, 89 | ACTIVATION_ALREADY_USED, 90 | ACTIVATION_SERIAL_UNKNOWN, 91 | ACTIVATION_EXPIRED, 92 | ACTIVATION_NOT_AVAILABLE 93 | }; 94 | 95 | VMP_IMPORT int VMP_API VMProtectActivateLicense(const char *code, char *serial, int size); 96 | VMP_IMPORT int VMP_API VMProtectDeactivateLicense(const char *serial); 97 | VMP_IMPORT int VMP_API VMProtectGetOfflineActivationString(const char *code, char *buf, int size); 98 | VMP_IMPORT int VMP_API VMProtectGetOfflineDeactivationString(const char *serial, char *buf, int size); 99 | 100 | #ifdef __cplusplus 101 | } 102 | #endif 103 | -------------------------------------------------------------------------------- /VMProtect/go.mod: -------------------------------------------------------------------------------- 1 | module VMProtect 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /example/VMProtectLicense.ini: -------------------------------------------------------------------------------- 1 | [TestLicense] 2 | MyHWID=KLZw2YVsykm/7iHtcr+VRoL/fqmGtg3Pr= 3 | AcceptedSerialNumber=SerialNumber 4 | UserName=andy koko 5 | EMail=koko@mail.com 6 | ExpDate=20250101 7 | MaxBuildDate=20250101 -------------------------------------------------------------------------------- /example/VMProtectSDK64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevmn/VMProtectSDK-Golang/a8880b767b23b3daa6d91dc79dd76d677c1090ee/example/VMProtectSDK64.dll -------------------------------------------------------------------------------- /example/build-mac-x64-Mach-O.sh: -------------------------------------------------------------------------------- 1 | cd $(dirname "$0") 2 | GO111MODULE=off CGO_LDFLAGS="-L. -lVMProtectSDK -Wl,-map,vm.map -w -s" CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \ 3 | go build -a -ldflags "-s -w" -o vm 4 | -------------------------------------------------------------------------------- /example/build-win-x64-PE-cross.sh: -------------------------------------------------------------------------------- 1 | cd $(dirname "$0") 2 | GO111MODULE=off CGO_LDFLAGS="-L. -lVMProtectSDK64 -Wl,-Map,vm.map" CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 \ 3 | go build -a -ldflags "-s -w" -o vm.exe 4 | -------------------------------------------------------------------------------- /example/build-win-x64-PE-llvm-mingw.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set GO111MODULE=off 3 | set CC=E:\llvm-mingw\bin\clang.exe 4 | set CGO_LDFLAGS=-L. -lVMProtectSDK64 -Wl,-pdb=vm.pdb 5 | set CGO_ENABLED=1 6 | set GOOS=windows 7 | set GOARCH=amd64 8 | go build -a -ldflags "-s -w" -o vm.exe 9 | pause 10 | -------------------------------------------------------------------------------- /example/go.mod: -------------------------------------------------------------------------------- 1 | module vm 2 | 3 | go 1.18 4 | 5 | replace VMProtect => /Users/yourname/go/src/VMProtect 6 | 7 | require VMProtect v0.0.0 8 | -------------------------------------------------------------------------------- /example/libVMProtectSDK.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevmn/VMProtectSDK-Golang/a8880b767b23b3daa6d91dc79dd76d677c1090ee/example/libVMProtectSDK.dylib -------------------------------------------------------------------------------- /example/script.lua: -------------------------------------------------------------------------------- 1 | function OnBeforeCompilation() 2 | print("----------------------- Attached Protection -----------------------\n") 3 | local file = vmprotect.core():outputArchitecture() 4 | for i = 1, file:mapFunctions():count() do 5 | local fn = file:mapFunctions():item(i) 6 | local addType 7 | if fn:name():find("Cfunc_VMProtect") then 8 | addType = CompilationType.Ultra 9 | file:functions():addByAddress(fn:address(), addType) 10 | print(fn:name() .. " : " .. tostring(fn:address()) .. " => Add To Protection\n") 11 | end 12 | end 13 | print("----------------------- Attached Protection -----------------------\n") 14 | end 15 | 16 | function GetRandomName() 17 | local res = "" 18 | for i = 1, 8 do 19 | res = res .. string.char(32 + math.random(string.byte("z") - 32)) 20 | end 21 | return res 22 | end 23 | 24 | function OnAfterSaveFile() 25 | local file = vmprotect.core():outputArchitecture() 26 | if vmprotect.core():inputFile():format() == "PE" then 27 | print("----------------------- Obfuscate Segment Name -----------------------\n") 28 | for i = 1, file:segments():count() do 29 | segment = file:segments():item(i) 30 | name = GetRandomName() 31 | segment:setName(name) 32 | print(string.format("Segment \"%s\" is renamed to \"%s\"", segment:name(), name)) 33 | end 34 | print("----------------------- Obfuscate Segment Name -----------------------\n") 35 | end 36 | end -------------------------------------------------------------------------------- /example/vm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "VMProtect" 5 | ) 6 | 7 | func main() { 8 | VMProtect.BeginUltra("Marker\x00") 9 | str := VMProtect.GoString(VMProtect.DecryptStringA("This is a decrypted string\x00")) 10 | serial := "SerialNumber\x00" 11 | 12 | println(str) 13 | println("HWID: ", VMProtect.GetCurrentHWID()) 14 | println("IsProtected: ", VMProtect.IsProtected()) 15 | println("IsDebuggerPresent: ", VMProtect.IsDebuggerPresent(true)) 16 | println("IsVirtualMachinePresent: ", VMProtect.IsVirtualMachinePresent()) 17 | println("IsValidImageCRC: ", VMProtect.IsValidImageCRC()) 18 | println("SetSerialNumber: ", VMProtect.SetSerialNumber(serial)) 19 | if VMProtect.GetSerialNumberState() == VMProtect.SERIAL_STATE_SUCCESS { 20 | println("-- Registered --") 21 | } 22 | println("User: ", VMProtect.GetUser()) 23 | println("Email: ", VMProtect.GetEmail()) 24 | println("ExpireDate: ", VMProtect.GetExpireDate()) 25 | println("MaxBuildDate: ", VMProtect.GetMaxBuild()) 26 | VMProtect.End() 27 | } 28 | -------------------------------------------------------------------------------- /pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevmn/VMProtectSDK-Golang/a8880b767b23b3daa6d91dc79dd76d677c1090ee/pic.png --------------------------------------------------------------------------------