日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

160个Crackme038之P-Code初窥门径

發布時間:2025/3/21 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 160个Crackme038之P-Code初窥门径 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • VB的變量類型
    • 實戰分析
      • 第一部分 基礎校驗
      • 第二部分 根據用戶名計算結果
      • 第三部分 除以圓周率
      • 第四部分 干擾代碼
      • 第五部分 關鍵比較
    • 寫出注冊機
    • 總結
    • 附上分析過程

【軟件名稱】:CyberBlade.2.exe

【軟件大小】:61.0 KB

【下載地址】:https://github.com/TonyChen56/160-Crackme

【加殼方式】:未加殼

【保護方式】:Name/Serial

【編譯語言】:VB P-Code

【調試環境】:W10 x64

【使用工具】 OD,VBExplorer,VB Decompiler

【破解日期】:2019-5-1

【破解目的】:學習分析P-Code類型的程序,理解P-Code虛擬機的解釋過程

【目標程序】:

這一次的目標程序的這個Crackme,是160個Crackme里面的第38個。運行時需要 Visual Basic 5.0 運行庫支持。這個Crackme的分析,用到了三個工具,每個工具都有各自的用途:

  • OD:用于跟蹤P-Code偽指令的具體細節及在靜態分析過程中無法查看的數據
  • VBExplorer 用于查看P-Code偽指令及注釋
  • VB Decompiler 用于靜態查看反匯編的偽代碼,減少OD跟蹤偽指令的工作量

VB的變量類型

想要分析這個Crackme,首先需要了解VB變量類型在內存中的存儲方式。

VB中的variant類型屬于一種結構體,該結構體的前兩個字節表示變量的類型,后面有3個WORD是保留的,接下來才是其真正的值,如下圖:

也就是說VB變量中真正的數據是存儲在首地址+8的位置處,下圖顯示了VB的所有的變量類型及含義

實戰分析

首先用 VB Decompiler反編譯目標程序,找到Check按鈕的點擊事件,分析整個點擊事件的校驗過程

這個程序的校驗過程分為五個部分,下面講解每一個部分的校驗過程

第一部分 基礎校驗

首先根據靜態分析的結果可以看到,該程序首先會校驗用戶名和序列號是否為空,然后判斷序列號長度是否小于5個字節,否則提示錯誤。即使看不懂反匯編后的VB代碼,也可以通過字符串知道整個過程。

第二部分 根據用戶名計算結果

第二部分的校驗過程看的就不那么清晰了,需要利用OD動態跟蹤每一個偽指令的具體操作流程。

用VBExplorer對目標程序進行反編譯,一直往下拉,根據字符串直接忽略第一部分的基礎校驗,來到0040E380的位置

我們可以看到第一個被執行的偽指令是0040E380處的0D,接下來將程序載入OD,數據窗口跟隨->0040E380

然后給第一個字節0D下內存訪問斷點,F9運行

然后隨便輸入一個用戶名和序列號,點擊Check

程序首先會讀取一個字節的操作碼到AL,

來看下VB Explorer中顯示的操作碼,后面的注釋提示這是在調用一個函數,0D后面的是操作碼的參數,接著esi自增1,指向操作碼的參數

接著通過一個jmp跳轉去執行操作碼,0x741BED94是地址跳轉表的首地址,eax保存下一條指令的操作碼,由于每一個跳轉地址是一個DWORD,所以用eax乘以4的值加上跳轉表的基地址來索引下一條偽指令的解釋單元,我們跟隨這個jmp

首先把[ebp-0x4C]賦值給eax,然后將eax壓棧。我們需要知道eax的含義。數據窗口跟隨之后,發現是一個指針,再次選中前四個字節,數據窗口跟隨DWORD,然后將數據顯示方式切換為長型->地址

這其實是一個函數的跳轉表,再接著把esi的內容A0賦值給edi,而esi始終執行的是操作碼

可以看到這一步實際上是在取操作碼的參數A0了

接著取出eax的內容,然后將eax加上edi,eax實際是跳轉表的首地址,那么參數A0,就是跳轉表的偏移

接著call eax,我們不需要跟進這個函數,只需要關注棧中的第二個參數0x19F134即可。直接步過這個函數

可以看到棧中的參數顯示出了我們剛才輸入的用戶名,再接著單步到xor eax,eax的地址

這里把eax的值清零了。也就是說第一條偽指令已經執行完成了。

這個就是P-Code虛擬機的解釋過程,其中esi始終指向要解釋的偽指令,eax保存的是將要解釋的偽指令。

接著看下一條偽指令,6C是操作碼,64FF是參數。后面的注釋告訴我們這條偽指令是在將某個DWORD值入棧。繼續跟蹤

首先取出操作碼6C,然后esi+5執向偽指令參數,接著跳轉執行這條偽指令,直接跟進jmp

首先取出參數FF64放到eax中,FF64是一個負數,

也就是十進制的9C,這也是為什么VBExplorer里會顯示LOCAL_009C的原因,這個9C代表[ebp-9C],是個局部變量

接著將[eax+ebp]入棧,壓棧的是剛剛輸入的用戶名[eax]的值是-98,這里其實是將[ebp-98]局部變量壓入棧,然后eax清零,表示這條偽指令結束。

繼續看下一條,偽代碼解釋的求長度

首先取出偽指令,然后直接跟進jmp

這里調用vbaLenBster,參數是之前輸入的用戶名,接著將用戶名長度入棧后,eax清零,接著看下一條FD6934EF

這條偽指令后面并沒有給出解釋,但是沒有關系,我們可以根據偽指令的執行過程猜測指令含義,這里其實是一個雙操作碼的指令,

首先取出操作碼FD,直接跟進jmp,什么都沒有做,直接將eax清零。之后再次取出操作碼69,繼續跟進jmp

這里將bx賦值為0x3,然后跳轉,繼續跟進

接著取出參數FF34,FF34也是個負數,然后再將FF34加上ebp,代表這是一個局部變量

接下來將ecx賦值給[eax+0x8]的地址處,我們數據窗口跟隨eax,然后將bx賦值給eax,賦值完成后eax值如下:

還記得VB的變量類型嗎?前兩個字節是變量類型,03代表是Long,中間是6個字節的保留位,首地址+8的位置才是真正的數值。

這個07是之前通過vbaLenBstr獲取到的用戶名長度,在這里轉成了變量,并將變量首地址壓棧。

現在我們就能通過實際的跟蹤結果來得出這條指令的含義了。就是將int值轉成變量類型。由于整個跟蹤過程實在是復雜,我這里只貼出算法的關鍵部分

在40E3C1處截取用戶名的第一個字符串

接著在40E3CD處將截取的用戶名每一位轉成ASCII值

接著將用戶名每一位的ASCII值轉為十進制后進行字符串拼接

整個過程循環,循環次數為用戶名的長度,可以直接在這個地方下斷點看到最后的結果

即拼接用戶名的ASCII十進制字符串,第二部分的算法就完成

在VB偽代碼中,var_94就是最后拼接的結果

第三部分 除以圓周率

接下來是第三部分,直接來看VB Decompiler中的偽代碼

這一部分的邏輯也很清晰,如果用戶名拼接的字符串長度大于9的話,就將這個結果轉為浮點數除以圓周率,一直除到結果的長度小于9。這個部分我也用OD詳細跟過每一條偽指令,確實和靜態反匯編的邏輯是一樣的

但是在loc_40E449的位置,將var_94和一個值進行了異或,并且還減去了另外一個值。這兩個數值我們無從得知,只能跟蹤OD

根據偽指令的助記符XorVar和SubVar快速定位到這兩個地址,下內存訪問斷點,很快就能找到這兩個值

可以看到這里實際上是將0x30F85678和用戶名的結果進行異或

然后減去0xD8B3,找到了這兩個數,第三部分也就結束了

第四部分 干擾代碼

這個是最有意思的,你會發現代碼初始化了10次循環,循環將一個變量和Key值進行比較,但問題在于Then分支沒有任何代碼。這也就是說不管這個循環中的比較成立與否 都對我們沒有任何影響

第五部分 關鍵比較

最后一部分,比較序列號減去var_94是否等于用戶名長度,也就是說用戶名計算的結果再加上用戶名的長度就是真正的序列號。最后對這個程序的校驗過程做一個總結

總結:

  • 檢測用戶名和序列號是否為空,序列號長度是否小于5
  • 將用戶名各個字符的ASCII碼的十進制轉換成字符串連接起來得到一個數result
  • 判斷result長度是否大于9 如果大于則將result轉為浮點數之后除以圓周率 直到resultI的長度小于或等于9
  • 將result和0x30F85678進行異或并減去0xD8B3,再加上用戶名長度就是正確的序列號
  • 寫出注冊機

    接著我們根據已經分析的算法寫出這個程序的注冊機,這個注冊機用C++寫還是太費勁了 直接用python快

    Name = "GuiShou" s = int(''.join([str(ord(i)) for i in Name]))while len(str(s))>9:s = int(s // 3.141592654)Serial = (s ^ 0x30F85678) - 55475 + len(Name) print(Serial)

    總結

    P-Code的程序并非如傳言一樣不可戰勝,只要你有足夠的耐心,配合VB Decompiler+VBExplorer+OD的黃金組合,剩下的就是純體力活了。

    P-Code類的程序用OD跟蹤偽指令雖然能看到每一處實現細節,但是畢竟還是太費力了。這個時候如果能總結出一套相對比較完整的P-Code的偽指令及每個參數的具體含義再配合WKTVBDebugger,調試P-Code就顯得游刃有余了。如果有大佬總結出來了還請發我一份 哈哈。

    附上分析過程

    最后附上分析過程和相關文件

    :0040E380 0DA0000300 VCallHresult ;Call ptr_0040342C 獲取輸入的用戶名 :0040E385 6C64FF ILdRf ;Push DWORD [LOCAL_009C] 將用戶名壓入堆棧 :0040E388 4A FnLenStr ;vbaLenBstr 求用戶名長度 :0040E389 FD6934FF CVarI4 ; 將用戶名長度轉為變量 :0040E38D 2F64FF FFree1Str ;SysFreeString [LOCAL_009C]; [LOCAL_009C]=0 釋放用戶名內存 :0040E390 1A68FF FFree1Ad ;Push [LOCAL_0098]; Call [[[LOCAL_0098]]+8]; [[LOCAL_0098]]=0 釋放局部變量 :0040E393 FE68B4FE8901 ForVar ; 初始化循環次數 開始循環 :0040E399 0464FF FLdRfVar ;Push LOCAL_009C 將局部變量0壓入堆棧 :0040E39C 21 FLdPrThis ;[SR]=[stack2] :0040E39D 0F0003 VCallAd ;Return the control index 02 :0040E3A0 1968FF FStAdFunc ; :0040E3A3 0868FF FLdPr ;[SR]=[LOCAL_0098] ***********Reference To:[propget]TextBox.Text| :0040E3A6 0DA0000300 VCallHresult ;Call ptr_0040342C 獲取輸入的用戶名 :0040E3AB 046CFF FLdRfVar ;Push LOCAL_0094 將局部變量0壓入堆棧 :0040E3AE 2824FF0100 LitVarI2 ;PushVarInteger 0001 將局部變量1壓入堆棧 :0040E3B3 04D4FE FLdRfVar ;Push LOCAL_012C 將局部變量0壓入堆棧 :0040E3B6 FC22 CI4Var ;vbaI4Var 將變量1轉為數字 :0040E3B8 3E64FF FLdZeroAd ;Push DWORD [LOCAL_009C]; [LOCAL_009C]=0 將用戶名壓入堆棧 :0040E3BB 4644FF CVarStr ; 將用戶名字符串轉為變量 :0040E3BE 0404FF FLdRfVar ;Push LOCAL_00FC 將局部變量0壓入堆棧 **********Reference To->msvbvm50.rtcMidCharVar | :0040E3C1 0A0A001000 ImpAdCallFPR4 ;Call ptr_00401006; check stack 0010; Push EAX 截取用戶名的第一個字符 :0040E3C6 0404FF FLdRfVar ;Push LOCAL_00FC 將用戶名的第一個字符壓入堆棧 :0040E3C9 FDFEB0FE CStrVarVal ; 將用戶名的第一個字符從變量轉為字符串 **********Reference To->msvbvm50.rtcAnsiValueBstr| :0040E3CD 0B0B000400 ImpAdCallI2 ;Call ptr_0040100C; check stack 0004; Push EAX 將用戶名的第一個字符轉為ASCII值 :0040E3D2 4434FF CVarI2 ; 將用戶名的第一個字符的ASCII值轉為變量 :0040E3D5 FBEFE4FE ConcatVar ; 將ASCII值的十進制進行字符串拼接 :0040E3D9 FCF66CFF FStVar ; 將拼接的字符串轉為變量 :0040E3DD 2FB0FE FFree1Str ;SysFreeString [LOCAL_0150]; [LOCAL_0150]=0 釋放字符串 :0040E3E0 1A68FF FFree1Ad ;Push [LOCAL_0098]; Call [[[LOCAL_0098]]+8]; [[LOCAL_0098]]=0 :0040E3E3 36060044FF24FF04 FFreeVar ;Free 0006/2 variants 釋放變量 :0040E3EC 04D4FE FLdRfVar ;Push LOCAL_012C :0040E3EF FE7EB4FE2D01 NextStepVar ; 開始下一輪循環 :0040E3F5 046CFF FLdRfVar ;Push LOCAL_0094 將用戶名拼接的字符串壓入堆棧——7111710583104111117(0x13) :0040E3F8 FBEB44FF FnLenVar ;vbaLenVar 求用戶名拼接的字符串長度 :0040E3FC 2854FF0900 LitVarI2 ;PushVarInteger 0009 將整形變量9壓入堆棧 :0040E401 5D HardType ; 修改變量9的類型 :0040E402 FB74 GtVarBool ;Push (Pop1 >= Pop2) 比較長度是否大于9 :0040E404 1CB901 BranchF ;If Pop=0 then ESI=0040E425 如果不大于9則跳轉到0040E425 :0040E407 046CFF FLdRfVar ;Push LOCAL_0094 將用戶名拼接的字符串壓入堆棧 :0040E40A FEC454FF50455254 LitVarR8 ; 將參數一(圓周率)的類型修改為浮點數 :0040E416 FBBC44FF DivVar ; 將用戶名拼接的字符串除以圓周率 :0040E41A FBE124FF FnFixVar ; 相當于字符串拷貝 :0040E41E FCF66CFF FStVar ; 將用戶名拼接的字符串轉為浮點數 :0040E422 1E8901 Branch ;ESI=0040E3F5 如果長度大于9則跳轉至0040E3F5 :0040E425 046CFF FLdRfVar ;Push LOCAL_0094 將浮點數結果壓入堆棧 :0040E428 FEC154FF7856F830 LitVarI4 ; 將浮點數轉為整形 :0040E430 FB1744FF XorVar ; 將結果和30F85678進行異或 :0040E434 FCF66CFF FStVar ; 保存結果 :0040E438 046CFF FLdRfVar ;Push LOCAL_0094 將異或后的結果壓棧 :0040E43B 080800 FLdPr ;[SR]=[STACK_0008] :0040E43E 8A4C00 MemLdStr ;Push DWORD [[SR]+004C] 將0xDBD3壓棧 :0040E441 FD6954FF CVarI4 ; 將0xDBD3轉為變量 :0040E445 FB9C44FF SubVar ; 用異或后的結果減去0xDBD3 :0040E449 FCF66CFF FStVar ; 保存結果

    需要分析記錄和相關文件可以到我的Github下載:https://github.com/TonyChen56/160-Crackme

    總結

    以上是生活随笔為你收集整理的160个Crackme038之P-Code初窥门径的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。