翻譯|使用教程|編輯:安雯斯|2023-05-23 14:26:24.093|閱讀 329 次
概述:VMProtect是新一代軟件保護實用程序。本文分享VMProtect入門使用教程許可證部分,歡迎查閱~
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
VMProtect是新一代軟件保護實用程序。VMProtect支持德爾菲、Borland C Builder、Visual C/C++、Visual Basic(本機)、Virtual Pascal和XCode編譯器。
同時,VMProtect有一個內置的反匯編程序,可以與Windows和Mac OS X可執行文件一起使用,并且還可以鏈接編譯器創建的MAP文件,以快速選擇要保護的代碼片段。
為了輕松實現應用程序保護任務的自動化,VMProtect實現了內置腳本語言。VMProtect完全支持Windows系列的32/64位操作系統(從Windows 2000開始)和Mac OSX(從版本10.6開始)。重要的是,無論目標平臺如何,VMProtect都支持所有范圍的可執行文件,即Windows版本可以處理Mac OS X版本的文件,反之亦然。有其他問題請咨詢加密解密技術QQ群:766135708
VMProtect 是保護應用程序代碼免遭分析和破解的可靠工具,但只有在正確構建應用程序內保護機制并且沒有可能破壞整個保護的典型錯誤的情況下才能最有效地使用。
“許可制度”包含以下小節:
許可系統功能
許可系統如何運作
管理許可證
如何將系統集成到您的應用程序的示例
自動序列號生成
在下面描述的幾個步驟中,我們將創建一個查詢許可系統的測試應用程序:向其提供序列號,接收序列號的狀態及其內容。第一階段,我們在測試模式下使用許可系統;第二階段我們使用它,因為它會在實際實踐中使用。
許可制度的工作模式
建筑保護總是經歷兩個主要步驟:開發和發布。至于許可,首先您創建一個應用程序,將保護集成到其中,然后添加檢查和功能限制。只有經過全面測試,您才能將產品提供給用戶并開始第二階段。受保護應用程序的測試是一個復雜的過程,因為您需要確保所有檢查和條件跳轉都正確運行。為所有可能的測試用例制作“真實的”序列號是不方便的。這就是許可系統也提供“開發者模式”(又名“測試模式”)的原因。在這種工作模式下,不對應用程序進行保護,系統對提供的序列號的反應在配置文件中進行調整。當應用程序沒有錯誤并且可以正確地與許可系統一起工作時,VMProtect 將“測試”許可模塊替換為執行真實序列號檢查的真實許可模塊。這是在應用程序受到保護時完成的,因此您不能錯誤地避免這一步。
第一階段:測試模式
在測試模式下,許可系統(狀態和它返回的數據)對提供的序列號的所有反應都在配置文件中描述。該文件名為 VMPLicense.ini,應位于應用程序的工作文件夾中。在下面提供的 10 個步驟中,我們將從創建最簡單的應用程序到在具有硬件鎖定和限制免費升級期限的測試模式下使用許可系統的全功能。
第一步是創建一個應用程序。這將是一個簡單的應用程序,沒有任何用戶界面,也沒有重要的功能。我們的目標是將序列號傳遞給許可系統并接收其答案。
#include <windows.h> #include <stdio.h> bool is_registered(const char *serial) { return serial && serial[0] == 'X'; } int main(int argc, char **argv) { char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity if (!is_registered(serial)) { printf("please register!\n"); return 0; } printf("We are registered.\n"); return 0; }
該程序使用一種非常簡單的方法來檢查序列號。is_registered ()函數將序列號的第一個符號與“X”進行比較,如果它們匹配則認為該數字是正確的。對于錯誤的序列號,會顯示一條注冊消息,而如果用戶輸入正確的密鑰,則會顯示“我們已注冊”。改為顯示
下一步是添加代碼以使用 VMProtect 的許可系統檢查序列號
如果您以前沒有這樣做,是時候將 VMProtect SDK 包含到您的項目中了。SDK包含三個文件:頭文件(VMProtectSDK.h)、庫文件(VMProtectSDK32.lib)和帶實現的dll文件(VMProtectSDK32.dll)。對于 64 位系統,庫和 dll 文件有單獨的實現。
將 dll 文件、頭文件和庫文件放入我們應用程序的工作文件夾中,源文件所在的位置,并將頭文件包含到主文件中:
#include <windows.h> #include <stdio.h> #include "VMProtectSDK.h"
構建項目并確保它像以前一樣編譯和運行。許可系統尚未激活。
將序列號發送到許可系統
現在,在序列號行的正下方,我們添加對許可系統的 SDK 函數的調用:
char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); printf("res = 0x%08X\n", res);
如果執行此操作后程序停止并提示缺少所需的 dll 文件,請確保將相應的 DLL 文件放入我們應用程序的工作文件夾中。如果執行成功,您應該會看到以下消息:
2 對應于API 中描述的SERIAL_STATE_FLAG_INVALID 標志。這意味著許可系統認為我們的密鑰不正確,這是真的,因為我們沒有向系統“解釋”哪些密鑰是正確的,哪些不是。
[TestLicense] AcceptedSerialNumber=Xserialnumber
現在,再次運行我們的程序。如果您仍然收到“2”錯誤代碼,請確保 ini 文件位于應用程序的工作文件夾中。這次我們應該收到“0”。這是許可系統接受并批準序列號的標志。現在我們可以從代碼中刪除is_registered()函數——許可系統現在負責檢查序列號:
#include <windows.h> #include <stdio.h> #include "VMProtectSDK.h" int main(int argc, char **argv) { char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); printf("res = 0x%08X\n", res); if (res) { printf("please register!\n"); return 0; } printf("We are registered.\n"); return 0; }
打印標志的便捷功能
首先,我們需要一個方便的函數來將標志的數值轉換為序列號的可理解狀態。下面是這個函數的代碼:
#define PRINT_HELPER(state, flag) if (state & flag) printf("%s ", #flag) void print_state(INT state) { if (state == 0) { printf("state = 0\n"); return; } printf("state = "); PRINT_HELPER(state, SERIAL_STATE_FLAG_CORRUPTED); PRINT_HELPER(state, SERIAL_STATE_FLAG_INVALID); PRINT_HELPER(state, SERIAL_STATE_FLAG_BLACKLISTED); PRINT_HELPER(state, SERIAL_STATE_FLAG_DATE_EXPIRED); PRINT_HELPER(state, SERIAL_STATE_FLAG_RUNNING_TIME_OVER); PRINT_HELPER(state, SERIAL_STATE_FLAG_BAD_HWID); PRINT_HELPER(state, SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED); printf("\n"); }
盡管大小不一,但功能非常簡單——一一檢查所有位標志并打印狀態變量中存在的所有內容。在檢查調用print_state的序列號后替換代碼中的printf,并更改我們傳遞給許可系統的序列號:
char *serial = "Xserialnumber1"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); print_state(res);
現在,如果我們運行這個程序,下面的消息將被打印到控制臺:
state = SERIAL_STATE_FLAG_INVALID please register!
現在,我們通過刪除“1”放回舊密鑰并再次運行程序:
state = 0 We are registered.
現在,我們可以看到序列號的狀態標志,讓我們開始從序列號中檢索標志和數據。
檢索序列號狀態
您可以通過三種方式獲取序列號的狀態:通過調用VMProtectSetSerialNumber()、通過調用VMProtectGetSerialNumberState()或通過調用VMProtectGetSerialNumberData() – 狀態標志被放入結構的字段之一。每種方法都旨在在特定時間使用。第一次檢查序列號是在安裝期間執行的。此時應拒絕錯誤號碼、過期號碼、黑名單號碼等。一些限制,例如,程序的最長運行時間或序列號到期日期也應在運行時檢查。和VMProtectGetSerialNumberState()方法是這里最快和最方便的方法。如果您需要接收有關序列號的完整信息,您可以使用更強大的VMProtectGetSerialNumberData()函數。
讓我們從簡單的事情開始。我們想從序列號中獲取用戶的姓名和電子郵件,以便在“關于”窗口(或其他任何地方)中顯示它們。為此,我們必須在 ini 文件中再添加兩行:
[TestLicense] AcceptedSerialNumber=Xserialnumber UserName=John Doe EMail=john@doe.com
而在程序中,如果注冊成功,我們獲取這些數據并輸出到屏幕:
VMProtectSerialNumberData sd = {0}; VMProtectGetSerialNumberData(&sd, sizeof(sd)); printf("name = %ls,\ne-mail = %ls\n", sd.wUserName, sd.wEMail);
該結構包含 UNICODE 數據,因此printf()使用 %ls 說明符而不是 %s。該程序應在屏幕上打印以下文本:
state = 0 We are registered. name = John Doe, e-mail = john@doe.com
現在按照以下格式在 ini 文件中添加一個新行:ExpDate=YYYYMMDD。例如:
ExpDate=20000101
此行中指定的日期必須已經過去,即最大日期是昨天。當我們運行程序時,我們應該看到以下內容:
state = SERIAL_STATE_FLAG_DATE_EXPIRED please register!
現在讓我們在顯示“請注冊”消息和程序存在之前獲取更多信息:
if (res) { VMProtectSerialNumberData sd = {0}; VMProtectGetSerialNumberData(&sd, sizeof(sd)); printf("exp. date: y = %d, m = %d, d = %d\n", sd.dtExpire.wYear, sd.dtExpire.bMonth, sd.dtExpire.bDay); printf("please register!\n"); return 0; }
該應用程序的第二次運行現在為我們提供了更多詳細信息:
state = SERIAL_STATE_FLAG_DATE_EXPIRED exp. date: y = 2000, m = 1, d = 1 please register!
好的,現在從 ini 文件中刪除 ExpDate=… 行,這樣它就不會影響我們要做的其他事情。
您可以限制程序從啟動的那一刻開始運行。這對于演示目的很有用:您向用戶提供了一個真實的序列號,但該程序的運行時間不超過 5 分鐘。許可系統不會強制關閉此類程序,而只是設置狀態標志。因此,讓我們通過將以下行添加到 ini 文件來將最長工作時間設置為一分鐘:
TimeLimit=1
并修改程序如下:
int main(int argc, char **argv) { char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); print_state(res); if (res) return 0; VMProtectSerialNumberData sd = {0}; VMProtectGetSerialNumberData(&sd, sizeof(sd)); printf("I will run for %d minute(s)\n", sd.bRunningTime); print_state(VMProtectGetSerialNumberState()); Sleep(60 * 1000 * sd.bRunningTime); printf("After %d minute(s):\n", sd.bRunningTime); print_state(VMProtectGetSerialNumberState()); return 0; }
該程序在啟動時打印序列號的狀態,然后計算最長運行時間并等待其到期。然后再次打印序列號狀態。將最大操作時間設置為一分鐘后,我們應該會收到以下結果:
state = 0 I will run for 1 minute(s) state = 0 After 1 minute(s): state = SERIAL_STATE_FLAG_RUNNING_TIME_OVER
受保護程序應定期分析序列號的狀態,并在設置標志時關閉。許可系統不會自動執行此操作,因為程序可能需要釋放內存、將數據保存到文件等。此外,您可能希望程序在操作時間到期后不要停止,而是切換到更受限制的模式。許可系統將此留給開發人員。
怎么運行的
當 VMProtect 保護應用程序時,它會記錄日期。許可系統將此日期視為應用程序的構建日期。您可以將此序列號可以使用的最大構建日期放入序列號中。因此,如果您將當前日期加上一年作為序列號,它將適用于您將在一年內發布的所有程序版本。一年零一天后發布的版本將無法使用此序列號,用戶可以選擇:使用舊版本的程序或購買新密鑰以使用最新版本的程序再使用一年.
讓我們試試
將格式為 MaxBuildDate=YYYYMMDD 的行放入 ini 文件中:
MaxBuildDate=20000101
在測試模式下,許可系統將今天視為構建日期,因此這一行中指定的日期已經過去很重要。也就是說,最大日期是昨天。修改main()函數的代碼,使其看起來像這樣:
int main(int argc, char **argv) { char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); print_state(res); if (res) { VMProtectSerialNumberData sd = {0}; VMProtectGetSerialNumberData(&sd, sizeof(sd)); printf("max. build date: y = %d, m = %d, d = %d\n", sd.dtMaxBuild.wYear, sd.dtMaxBuild.bMonth, sd.dtMaxBuild.bDay); printf("please register!\n"); return 0; } printf("I'm registered\n"); return 0; }
然后,在程序運行時,您應該看到以下內容:
state = SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED max. build date: y = 2000, m = 1, d = 1 please register!
通過將 ini 文件中的日期替換為今天或明天,我們最終得到了“工作”程序:
state = 0 I'm registered
從 ini 文件中刪除 MaxBuildDate=… 行,這樣它就不會影響我們的進一步步驟。
許可系統不應接受在 VMProtect 中標記為“已阻止”的序列號。當您下次重建您的應用程序時,VMProtect 會將黑名單序列號的哈希值添加到受保護的應用程序中。因此,應用程序的許可系統將來會拒絕這些序列號。
首先,讓我們最小化main()函數的內容:
int main(int argc, char **argv) { char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); print_state(res); return 0; }
現在,運行程序并確保許可系統接受我們的序列號:
int main(int argc, char **argv) { char *serial = "Xserialnumber"; // we set the serial number directly in the code, for simplicity int res = VMProtectSetSerialNumber(serial); print_state(res); return 0; }
現在,將這個序列號添加到許可系統的黑名單中。將以下行添加到 ini 文件中:
BlackListedSerialNumber=Xserialnumber
并再次運行程序:
state = SERIAL_STATE_FLAG_BLACKLISTED
我們是否應該通知用戶他或她輸入的序列號被列入黑名單?它是由你決定。您可以簡單地告訴序列號不正確或通知用戶密鑰已泄露。許可系統只是通知程序有關使用黑名單序列號的事實。
接收硬件標識符
在我們鎖定硬件之前,我們必須收到一個硬件標識符。標識符被放入序列號中,當序列號傳遞給許可系統時,它會檢查標識符是否匹配。因此,首先我們需要接收硬件的標識符。讓我們將main()函數減少到最低限度:
int main(int argc, char **argv) { int nSize = VMProtectGetCurrentHWID(NULL, 0); char *buf = new char[nSize]; VMProtectGetCurrentHWID(buf, nSize); printf("HWID: %s\n", buf); delete [] buf; return 0; }
通過運行該程序,我們會收到一個默認的測試硬件標識符:
HWID:myhwid
要更改標識符,請將以下行添加到 ini 文件中:
MyHWID=test
如果我們之后運行程序,我們可以看到系統認為“test”是我們 PC 的硬件標識符:
HWID: test
重要的!只有經過 VMProtect 處理后,程序才會顯示真實的硬件標識符。
硬件鎖定序列號
要將我們的測試序列號鎖定到硬件,我們應該在 ini 文件中再添加一行。這次我們定義“放入”序列號的標識符:
KeyHWID=test
然后我們將main()復雜化一點。現在它將傳遞一個序列號并分析它得到的結果:
int main(int argc, char **argv) { int nSize = VMProtectGetCurrentHWID(NULL, 0); char *buf = new char[nSize]; VMProtectGetCurrentHWID(buf, nSize); printf("HWID: %s\n", buf); delete [] buf; char *serial = "Xserialnumber"; int res = VMProtectSetSerialNumber(serial); print_state(res); return 0; }
運行代碼后我們會看到如下結果:
HWID: test state = 0
許可系統已將當前硬件標識符與序列號中寫入的標識符進行比較。標識符相等,因此VMProtectSetSerialNumber()函數返回 0——序列號匹配。
現在讓我們嘗試在另一個硬件上“運行”我們的程序。我們只需將 ini 文件中 MyHWID 參數的值從“test”更改為“new test”。再次運行程序:
HWID: new test state = SERIAL_STATE_FLAG_BAD_HWID
這次許可系統返回了 SERIAL_STATE_FLAG_BAD_HWID 標志,這意味著真實的硬件標識符與存儲在序列號中的不匹配。我們在屏幕上看到的當前標識符是“new test”,而序列號是“test”。如果我們將 ini 文件中的 KeyHWID 參數更改為“new test”,我們也可以讓我們的序列號在這個“硬件”上工作。
序列號最多可容納 255 個字節的任意數據,許可系統按原樣傳遞給程序。數據可以包含有關銷售的任何其他信息、完整版操作所需的數據或其他內容。讓我們修改我們的main()函數,讓它從序列號中讀取數據并將它們顯示在屏幕上:
int main(int argc, char **argv) { char *serial = "Xserialnumber"; int res = VMProtectSetSerialNumber(serial); print_state(res); if (res) return 0; VMProtectSerialNumberData sd = {0}; VMProtectGetSerialNumberData(&sd, sizeof(sd)); printf("Serial number has %d byte(s) of data\n", sd.nUserDataLength); for (int i = 0; i < sd.nUserDataLength; i++) printf("%02X ", sd.bUserData[i]); printf("\n"); return 0; }
我們還將 Ini 文件縮減為:
[TestLicense] AcceptedSerialNumber=Xserialnumber
現在,我們運行程序并確保我們的序列號正常工作,但不包含任何數據:
state = 0 Serial number has 0 byte(s) of data
要將新的用戶數據添加到序列號中,我們需要在 ini 文件中創建 UserData 變量,并以 HEX 格式為其分配數據。符號必須成對出現,即一行的長度必須是 2 的倍數。像這樣:
UserData=010203A0B0C0D0E0
在這種情況下,如果我們運行該程序,我們將收到以下結果:
state = 0 Serial number has 8 byte(s) of data 01 02 03 A0 B0 C0 D0 E0
第二階段:實模式
在實模式下,VMProtect 許可系統為受保護的應用程序放置了一個特殊的許可模塊。該模塊執行與 SDK 中的測試模塊相同的功能,但使用序列號的內容而不是配置 ini 文件。接下來的五個步驟說明了使用基于 VMProtect 和許可系統的全功能保護來保護簡單應用程序的過程。
在第一階段,我們制作了幾個簡單的應用程序來測試許可系統的 API。現在,在第二階段,我們將只創建一個應用程序。它還將是一個控制臺應用程序,其foo()函數僅在注冊版本中有效。這是我們的測試應用程序的代碼:
#include <windows.h> #include <stdio.h> #include "VMProtectSDK.h" #define PRINT_HELPER(state, flag) if (state & flag) printf("%s ", #flag) void print_state(INT state) { if (state == 0) { printf("state = 0\n"); return; } printf("state = "); PRINT_HELPER(state, SERIAL_STATE_FLAG_CORRUPTED); PRINT_HELPER(state, SERIAL_STATE_FLAG_INVALID); PRINT_HELPER(state, SERIAL_STATE_FLAG_BLACKLISTED); PRINT_HELPER(state, SERIAL_STATE_FLAG_DATE_EXPIRED); PRINT_HELPER(state, SERIAL_STATE_FLAG_RUNNING_TIME_OVER); PRINT_HELPER(state, SERIAL_STATE_FLAG_BAD_HWID); PRINT_HELPER(state, SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED); printf("\n"); } char *read_serial(const char *fname) { FILE *f; if (0 != fopen_s(&f, fname, "rb")) return NULL; fseek(f, 0, SEEK_END); int s = ftell(f); fseek(f, 0, SEEK_SET); char *buf = new char[s + 1]; fread(buf, s, 1, f); buf[s] = 0; fclose(f); return buf; } // The foo() method is very short, but we need it to be an individual function // so we asked the compiler to not compile it inline __declspec(noinline) void foo() { printf("I'm foo!\n"); } int main(int argc, char **argv) { char *serial = read_serial("serial.txt"); int res = VMProtectSetSerialNumber(serial); delete [] serial; if (res) { printf("serial number is bad\n"); print_state(res); return 0; } printf("serial number is correct, calling foo()\n"); foo(); printf("done\n"); return 0; }
在沒有調試信息的情況下編譯程序,但在鏈接器設置中我們啟用了 MAP 文件的創建——我們將需要它與 VMProtect 一起工作。運行程序后,我們應該看到以下文本:
serial number is bad state = SERIAL_STATE_FLAG_INVALID
目前,許可系統仍在測試模式下運行,因為該文件未經過 VMProtect 處理,并且其中不包含許可模塊。在下一步中,我們將創建一個 VMProtect 項目并嘗試保護我們的應用程序。
現在,當我們的測試應用程序準備就緒、編譯并在同一文件夾中分配了一個 MAP 文件時,我們可以運行 VMProtect Ultimate 并打開可執行文件。我們需要向項目添加兩個函數:_main(這是 Visual Studio 重命名我們的 main() 的方式)和 foo()。這兩個函數都可以在 VMProtect 的“函數”部分的函數列表中看到。
許可系統已初始化,讓我們嘗試編譯 VMProtect 項目并運行受保護的文件。從命令行運行它后,我們將收到以下消息:
C:\test>dummy_app.vmp.exe serial number is bad state = SERIAL_STATE_FLAG_INVALID
如果你運行 depends.exe 可以看到我們受保護的可執行文件不再使用 VMProtectSDK.dll。這意味著許可模塊已經內置到程序中。您還可以從 VMProtect 查看已用 DLL 的列表,在“詳細信息 | 進口”部分。
我們的受保護程序從 serial.txt 文件中讀取序列號。由于還沒有這樣的文件,許可模塊收到一個被解釋為不正確的空序列號。現在我們切換到“許可證”部分并生成一個序列號。此處詳細描述了此過程,現在我們僅創建一個簡單的序列號,沒有任何限制。
然后,我們復制序列號(在許可證屬性中選擇“序列號”字段并按下 Ctrl+C),在與受保護應用程序相同的文件夾中創建一個名為 serial.txt 的文件,并將復制的序列號粘貼到那里。現在,如果我們運行我們的應用程序,我們將看到:
C:\test>dummy_app.vmp.exe serial number is correct, calling foo() I'm foo done
許可系統檢查了序列號并發現它是正確的。在下一步中,我們將嘗試應用一些限制并觀察結果。
序列號有效期
讓我們創建另一個具有特定到期日期的序列號。例如,2005。這個日期已經過去了,因此我們的序列號一定是不正確的。切換到“許可證”部分,然后單擊工具欄上的“添加許可證”按鈕。在“Add license”對話框窗口中啟用“Expiration date”選項并指定 2005 年 9 月 30 日。創建序列號,將其復制并粘貼到 serial.txt,然后運行程序:
C:\test>dummy_app.vmp.exe serial number is bad state = SERIAL_STATE_FLAG_DATE_EXPIRED
許可模塊返回“序列號已過期”標志。現在,將工作序列號放回 serial.txt 文件并確保許可模塊完全接受它。
C:\test>dummy_app.vmp.exe serial number is correct, calling foo() I'm foo done
將序列號加入黑名單
讓我們想象一下,我們的“好”序列號已經泄露到 Internet 并且現在已被泄露。我們需要阻止它,以便它在程序的未來版本中不起作用。為此,請在列表中選擇序列號并將主面板中的“已阻止”屬性設置為“是”。目前序列號還沒有被屏蔽,但是當你再次保護文件時,應用程序將不再接受這個序列號。讓我們確保這是真的。如果我們現在運行我們的程序,它應該毫無問題地接受被阻止的序列號,因為這是對被阻止的號碼一無所知的舊版本:
C:\test>dummy_app.vmp.exe serial number is correct, calling foo() I'm foo done
現在我們復制我們的程序并將其命名為“dummy_app1.vmp.exe”,然后打開 VMProtect 并再次保護應用程序。然后運行這個新版本:
C:\test>dummy_app.vmp.exe serial number is bad state = SERIAL_STATE_FLAG_BLACKLISTED
又是舊版本,為了比較:
C:\test>dummy_app1.vmp.exe serial number is correct, calling foo() I'm foo done
舊版本不知道被阻止的序列號并且像以前一樣工作。
在下一步中,我們將嘗試將代碼鎖定到序列號。但在我們繼續之前,取消阻止序列號并在 VMProtect 中重新對應用程序應用保護,使其再次接受該序列號。或者只是創建一個新的許可證。
破解程序最常見的方法之一是定位檢查序列號的地方和緊隨其后的條件跳轉。如果序列號是正確的,程序將以一種方式執行,如果不正確,則以另一種方式執行。黑客找到了這個跳轉,并將其替換為“正確”方式的跳轉。讓我們使用這種技術“破解”我們的測試程序。當然,直接在源代碼中。讓我們“關閉”我們的條件跳轉:
char *serial = read_serial("serial.txt"); int res = VMProtectSetSerialNumber(serial); delete [] serial; if (false && res) {
現在,我們的程序接受任何序列號并正常工作。當然,如果文件被 VMProtect 保護,即使是經驗豐富的黑客也會像我們一樣花費數月的時間來定位和修改條件跳轉。并且考慮到程序會在不同條件下多次檢查序列號,即使是這種簡單的檢查也是相當安全的。但讓我們更進一步。
將代碼鎖定到序列號
重要的!VMProtect 的演示版本對處理函數的數量有限制:只處理一個函數。所以如果你使用演示版,你應該只在項目中包含 foo() 函數,否則 VMProtect 的演示版可以選擇 main() 函數并且鎖定到序列號將不起作用。
VMProtect 的許可系統允許您將一個或多個功能的代碼鎖定到一個序列號,這樣,如果沒有提供正確的序列號,它們將無法工作。函數體被虛擬化,然后加密,只能用正確的序列號解密。這意味著,即使黑客發現并修復了序列號檢查中的條件跳轉,鎖定到序列號的功能仍然無法使用。讓我們試試這個。在“Functions”部分選擇foo()函數,然后在右側面板中將“Lock to Serial Number”選項更改為“Yes”。
然后,保護應用程序。因為,我們已經“破解”了它,將任意文本放入 serial.txt 文件并運行應用程序。控制臺中出現以下文本
C:\test>dummy_app.vmp.exe serial number is correct, calling foo()
這意味著,黑客“修復”了條件跳轉,程序以“正確”的方式運行。但是當調用foo()時,程序會顯示一條消息:
由于我們將foo()函數鎖定到序列號,而黑客并沒有它,因此試圖解密該函數的代碼導致出現故障,無法繼續執行程序。當按下“確定”時,程序關閉并且“完成”消息永遠不會顯示在控制臺中。
什么應該鎖定到一個序列號?
將只應在程序的注冊版本中運行的功能鎖定到序列號是有意義的。由于鎖定需要虛擬化,因此您應該考慮到一些性能損失。例如,如果文本編輯器不允許在演示版本中保存結果,則可以將保存文檔功能鎖定為序列號。如果該函數在其運行期間調用其他函數,則沒有必要也將它們鎖定,因為如果沒有 main 函數,它們將沒有任何用處。
您還應該記住,在沒有序列號的情況下調用鎖定功能會導致程序關閉,并且沒有機會保存工作結果。這就是為什么您應該徹底測試應用程序以確保它不會在試用模式下調用此類功能。在上面的示例中,文本編輯器必須在演示模式下禁用“保存”命令,并且不響應 Ctrl+S 快捷鍵。當然,它也不應該要求在退出時保存文檔。如果您不注意這一點,用戶可能會對您的“錯誤”演示版本感到失望。
鎖定序列號和無效序列號
調用VMProtectSetSerialNumber()函數時,許可模塊會檢查傳遞給該函數的序列號。只有在檢查時序列號絕對正確時,才會執行加密的代碼片段——未列入黑名單、具有正確的硬件標識符、未過期等。在這種情況下,將執行所有加密過程,直到應用程序關閉,或再次調用VMProtectSetSerialNumber() 。
一些限制可能會在程序執行過程中“觸發”:例如,程序的運行時間可能到期或序列號到期日期到來。在這種情況下,許可模塊仍然加密并執行鎖定到序列號的功能。之所以如此,是因為受保護的應用程序很難檢測到這些限制觸發的時刻并相應地更改行為(阻止相應的菜單項等)。如果許可模塊突然停止執行鎖定序列號的代碼片段,則極有可能導致應用程序出現故障。這就是為什么在設置序列號時做出決定,并選擇相應的執行模式。
附加信息
所有位標志、結構格式和函數調用參數的值都可以在該幫助文件的許可系統 API部分找到。使用此部分作為參考,而上面提供的步驟有助于輕松實現典型的即用型保護。
以上便是本篇文章的分享,如果您有任何疑問或者想獲取更多產品試用/授權/價格信息,可以咨詢我們的了解~
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn