關於VB P-CODE的一些總結 (10千字)

看雪資料發表於2015-11-15

關於VB P-CODE的一些總結

前言:到網上查PCODE的資料,到了一個講Fight Against Crack的網站上,那個作者講了許多陰毒的招數後,還特別說明,如果是VB程式,最好把它做成P-CODE,這會大大增加破解的難度。事實真的如此嗎?WKT的一個大蝦卻說:We are going to show that protecting a VB application is a very difficult task.The last Microsoft's invention on VB, the 'p-code', it's a delicious bite for reverse engineers. I'll show that a 'p-compiled' application may be easier to crack that a conventional compiled one. 
    不知是不是我孤陋寡聞,總覺得現在關於VB P-CODE的資料少之又少,不管國內國外只能找到各位老大們為數不多的幾篇教程(很多也是說“我猜想”之類拿不準的話),《加密與解密》上關於這個也是一筆帶過(大概是為續集著想^_^),可能是大蝦覺得這個太簡單不屑於講,或是哪裡已經有非常系統全面的介紹。總之以前我是一見是VB就害怕,再見是PCODE就投降,這大概就是人家用P-CODE對付破解的原因。這幾天終於下決心自己寫了幾個程式試驗,再結合各位老大的文章,總結出一點東西,非常不全面,希望大家多多指教,多多補充。

“工欲善其事,必先利其器。”那句古話好像是這麼說的。我們找的工具有:WKTVBDebug(動態破解用,相當於PCODE裡的SOFTICE)EXDEC(靜態分析,相當於W32DASM)SmartCheck(可以作輔助用)

要想破解PCODE程式,關鍵要理解裡面的“助記符”(它又不同於組合語言的“mnemonics”,我不知道該怎麼表示了)的作用,PCODE的助記符乍看上去很亂,好像比彙編還難,其實它們都是由幾部分組成的。比如CVarStr就是由三部分組成(詳見下文)。VB PCODE中常見的“助記符”如下面所示:(只總結出了一點,懇請各位補充)

表示資料型別的:
 
I2 ---- Integer,佔一個位元組的整數(彙編裡的BYTE)
I4 ---- Integer,佔兩個位元組的整數(彙編裡的WORD)
I8 ---- Integer,佔四個位元組的整數(彙編裡的DWORD)
UI4---- Unsigned Integer,無符號整數
UI8---- Unsigned Integer,無符號整數
R4 ---- Real,單精度實數(Single)
R8 ---- Real,雙精度實數(Double)
Str---- String,字串型別
Var---- Variant,變數型別。這就是BASIC特殊的地方,它允許使用者在使用變數前不進行宣告,這種不宣告的變數就用這種型別儲存,它可以包括數字、字串等各種型別。我看M$的這個玩意兒沒給使用者帶來方便,只能讓一些初學程式設計的菜鳥思維混亂,讓我們們破解時也非常鬱悶:(。它的儲存方式非常奇怪,比方說你看到一個VARIANT型別的資料被放到記憶體裡了,你跟過去找,結果什麼也找不到。看雪書上說應該D *(EAX+8),原來它真正的資料往後挪了8個位元組,真不知在搞什麼.....BTW:如果是一個數值型別的資料,它的地址向後移8個位元組即為真正的數值,如果是一個字串型的資料,它的地址向後移8個位元組即為指向一個UNICODE字串的指標。

表示堆疊操作的:(PCODE沒有暫存器,全部透過堆疊傳送資料,因此非常重要)

St ---- Store,把當前棧頂的資料放在記憶體裡
Ld ---- Load,把記憶體某處的資料壓入堆疊
Lit---- Literal,把一個“立即數”壓入堆疊

其它重要的:

C  ---- Convert,資料轉換。如CI4I2即把BYTE擴充為WORD(I2->I4)
Eq ---- Equal,判斷是否相等,並把結果(0或1)入棧
Lt ---- 判斷是否小於
Gt ---- 判斷是否大於
Len---- 得到字串長度

跳轉指令:

Branch  ---- 無條件跳轉
BranchT ---- 棧頂資料為真則跳
BranchF ---- 棧頂資料為假則跳

一些算術運算:

Add , Sub 等等應該都比較好認吧。

從一篇介紹PCODE的文章裡抄來一些,不知有沒有用:

Prefix                                                             Control
------------------------------------------------------------------------------------
cbo                                                                Combo box
chk                                                                Check box
cmd                                                                Command Button
dir                                                                Directory box
drv                                                                Drive list box
fil                                                                File list box
fra                                                                Frame
frm                                                                Form
grd                                                                Grid
hsb                                                                Horizontal scrollbar
img                                                                Image
lbl                                                                Label
lin                                                                Line
lst                                                                List box
mnu                                                                Menu
ole                                                                OLE client
opt                                                                Option button
pic                                                                Picture Box
shp                                                                Shape
tmr                                                                Timer
txt                                                                Text box
vsb                                                                Vertical scrollbar
-----------------------------------------------------------------------------------------

還有一些不太清楚的,都是我的猜想,希望大蝦解釋:

Call ---- 呼叫過程
Free ---- 釋放記憶體空間
Rf   ---- 區域性變數????
Pr   ---- ????
Ad   ---- 是不是Address??
HardType--是幹什麼的?

這些組合在一起就成了多種多樣的指令,很有趣吧。

還有一個要特別強調的是PCODE的堆疊,PCODE幾乎所有的指令都要對堆疊進行操作,有許多指令都是針對棧頂的一個或兩個資料進行操作,因此在動態除錯PCODE時要十分注意右邊顯示的堆疊區,並經常檢視記憶體,這樣才能理解指令的意義。

下面來實踐一下,執行起塵封已久的VB,在FORM上放一個TEXT1,一個BUTTON1,雙擊Button1,在下面輸入:

Private Sub Command1_Click()
    st1 = Text1.Text
    st2 = ""
    m = Len(Text1.Text)
    For i = 1 To m
        st2 = st2 + Mid$(Text1.Text, m - i + 1, 1)
    Next i
    MsgBox st2, vbOKOnly, "CRACK"
End Sub

呵呵,很簡單是不是。按一下按鈕就把TEXT裡的文字反過來顯示在訊息框裡。
下面來“生成工程”,注意一定要在“選項”裡選擇生成P-CODE檔案。然後用Exdec分析一下:

Proc: 401a90
4019B0: 04 FLdRfVar                local_008C          好像是一個指向TEXT的指標
4019B3: 21 FLdPrThis                                   先給一個下馬威,前幾句全不太明白!
4019B4: 0f VCallAd                 text                用WKTVBDebug過這一句時能看到Form1.text1
4019B7: 19 FStAdFunc               local_0088          猜想應該是取得控制程式碼之類的事情
4019BA: 08 FLdPr                   local_0088          
4019BD: 0d VCallHresult            get__ipropTEXTEDIT  呼叫,從字面上可以看出是GetText
4019C2: 3e FLdZeroAd               local_008C          好像壓入一個指向上面文字的指標,不太清楚,反正上面這個過程很經典啦,幾乎從文字框讀數都是這樣
4019C5: 46 CVarStr                 local_00AC          把上面得到的字串轉為Var格式
4019C8: Lead1/f6 FStVar                                再把這個VAR資料入棧 st1
4019CC: 1a FFree1Ad                local_0088          釋放前面的空間
4019CF: 3a LitVarStr:              ( local_00CC )      壓入一個立即數:空字串st2=""
4019D4: Lead2/00 FStVarCopy       
4019D8: 04 FLdRfVar                local_008C
4019DB: 21 FLdPrThis              
4019DC: 0f VCallAd                 text
4019DF: 19 FStAdFunc               local_0088
4019E2: 08 FLdPr                   local_0088
4019E5: 0d VCallHresult            get__ipropTEXTEDIT  和上面相同,得到字串
4019EA: 6c ILdRf                   local_008C          壓入字串
4019ED: 4a FnLenStr                                    得到字串的長度m              
4019EE: Lead2/69 CVarI4            local_00CC          轉為VAR型別
4019F2: Lead1/f6 FStVar                                VAR型別的長度入棧
4019F6: 2f FFree1Str               local_008C          釋放記憶體空間
4019F9: 1a FFree1Ad                local_0088
4019FC: 28 LitVarI2:               ( local_00FC ) 0x1  (1) 壓入一個立即數0x1
401A01: 04 FLdRfVar                local_00EC          local_00EC是迴圈變數i
401A04: 04 FLdRfVar                local_00DC          這個是上面得到的長度m
401A07: Lead3/68 ForVar:           (when done) 401A67  FOR i=1 to m 開始迴圈
401A0D: 04 FLdRfVar                local_008C
401A10: 21 FLdPrThis              
401A11: 0f VCallAd                 text
401A14: 19 FStAdFunc               local_0088
401A17: 08 FLdPr                   local_0088
401A1A: 0d VCallHresult            get__ipropTEXTEDIT  和上面相同的過程,得到字串
401A1F: 04 FLdRfVar                local_00BC          把local_BC壓入,這實際上是st2
401A22: 28 LitVarI2:               ( local_013C ) 0x1  (1) 壓一個0x1,CALL的引數
401A27: 04 FLdRfVar                local_00DC          字串長度m
401A2A: 04 FLdRfVar                local_00EC          迴圈變數i
401A2D: Lead0/9c SubVar                                相減 m-i
401A31: 28 LitVarI2:               ( local_00CC ) 0x1  (1) 再壓入一個0x1
401A36: Lead0/94 AddVar            local_012C          再加1, m-i+1,CALL的引數
401A3A: Lead1/22 CI4Var                                轉成整數型
401A3C: 6c ILdRf                   local_008C          壓入,作為下面CALL的引數
401A3F: 0b ImpAdCallI2                                 這是rtcMidCharBStr,原始碼中的Mid$()
401A44: 46 CVarStr                 local_014C          把取得的字元轉成Var型
401A47: Lead0/94 AddVar            local_015C          把新取得的字元和上面的401A1F處的st2連起來
401A4B: Lead1/f6 FStVar           
401A4F: 2f FFree1Str               local_008C
401A52: 1a FFree1Ad                local_0088          釋放
401A55: 36 FFreeVar
401A5E: 04 FLdRfVar                local_00EC          設好迴圈變數
401A61: Lead3/7e NextStepVar:      (continue) 401A0D   NEXT i,迴圈變數+1,直到結束
401A67: 27 LitVar_Missing                               VB裡面那些帶[]的可選引數,如果不加設定
401A6A: 27 LitVar_Missing                               就會變成這種Missing或NULL的形式
401A6D: 3a LitVarStr:              ( local_00CC ) CRACK 壓入字串,MsgBox的標題
401A72: 4e FStVarCopyObj           local_00AC           把剛壓入的字串複製到local_AC
401A75: 04 FLdRfVar                local_00AC           再壓進去一次(???)
401A78: f5 LitI4:                  0x0  0  (....)       訊息框的樣式 vbOKOnly
401A7D: 04 FLdRfVar                local_00BC           這是上面計算得到的反轉字串
401A80: 0a ImpAdCallFPR4:                               這個是rtcMsgBox,共有五個引數      
401A85: 36 FFreeVar
401A8E: 13 ExitProcHresult                              結束過程

我儘量想把分析寫得明白一些,但還是有幾句解釋不清,希望精通PCODE的大俠解釋一下,小弟代表廣大菜鳥同胞感激不盡。

相關文章