在十幾年前確實破解軟體僅改JMP就可以破解軟體,不過現今的軟體大部分都會將驗證程序隱藏得很好,抑或是將驗證程序移到其他的DLL檔案,並且使用一些Anti-Debugger的方法來抵擋駭客,因此現今想要破解軟體,很有可能連驗證的JMP都找不到,一定要相當程度的駭客經驗,才能對軟體驗證駕輕就熟。
那麼此篇章就比較一下以前跟現在軟體驗證的差異。
十幾年前的軟體驗證:
在十幾年前要找到CheckLicense這個驗證函式,其實非常容易,因為只要透過輸出的錯誤字串(Show Error Messages),再搜尋這些字串找到相對應的函式,就能輕易地修改驗證的JMP。
int main()
{
if(CheckLicense(licence))
{
Show Error Messages
return error;
}
………………….}
現今的軟體驗證:
很多人為什麼會找不到驗證程序,因為它將驗證程序移到其他的DLL檔(xx.dll),因此在主程式分析是找不到它的。
目前蠻多的軟體會在驗證程序的前後塞垃圾的程式碼(Garbage Code),已防被找到驗證函式,然後還會將錯誤訊息進行重組(MakeUpString),因此搜尋驗證字串也是找不到的(還沒組合字串前很有可能是無意義的字串)。
有些軟體還會使用多階層驗證序號,因此修改一個JMP是不夠的,甚至要分析驗證的結構(struct),這樣才能更準確的判斷哪些條件式才是跟驗證序號有關的。
xx.dll
int License(license)
{
………………….
Garbage Code
if(CheckLicense(license))
{
MakeUpString(ErrorString);
Show Error Messages
return error;
}
Garbage Code
………………….}
CheckLicense2(license2);
………………….
CheckLicense3(license3);
更進階的驗證方法是一開始的驗證CheckLicense只是做一個hash,下個驗證(CheckLicense2)僅獲取CheckLicense2的函示指標到FunctionTable,然後執行一段時間才會從FunctionTable獲取函示指標到Check3Function,並且運行它,那麼這裡會有一個問題,如果以為前面就是主要驗證的函式,後面程序可能就會崩潰,因此需要花更多時間將所有跟驗證值有關的條件式完全修改才能突破驗證。
int AdvCheckLicense(license)
{
………………….
Check3Function = FunctionTable[0];
Check3Function(license3);
int AdvCheckLicense(license)
{
………………….
if(CheckLicense(license))
{
license2 = sha1(license)
………………….
}
if(CheckLicense2(license2))
{
FunctionTable[0] = Check3FunctionAddr;
………………….
}
………………….}
………………….
Check3Function = FunctionTable[0];
Check3Function(license3);
目前都還沒談到Anti-Debugger的手法,看起來就那麼難破解,軟體驗證只會越來越複雜,唯有提升自我的技術和增加實戰經驗,才能攻破軟體的驗證。