前言:到网上查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. <BR> 不知是不是我孤陋寡闻,总觉得现在关于VB P-CODE的资料少之又少,不管国内国外只能找到各位老大们为数不多的几篇教程(很多也是说“我猜想”之类拿不准的话),《加密与解密》上关于这个也是一笔带过(大概是为续集着想^_^),可能是大虾觉得这个太简单不屑于讲,或是哪里已经有非常系统全面的介绍。总之以前我是一见是VB就害怕,再见是PCODE就投降,这大概就是人家用P-CODE对付破解的原因。这几天终于下决心自己写了几个程序试验,再结合各位老大的文章,总结出一点东西,非常不全面,希望大家多多指教,多多补充。 <BR><BR>“工欲善其事,必先利其器。”那句古话好像是这么说的。我们找的工具有:WKTVBDebug(动态破解用,相当于PCODE里的SOFTICE)EXDEC(静态分析,相当于W32DASM)SmartCheck(可以作辅助用) <BR><BR>要想破解PCODE程序,关键要理解里面的“助记符”(它又不同于汇编语言的“mnemonics”,我不知道该怎么表示了)的作用,PCODE的助记符乍看上去很乱,好像比汇编还难,其实它们都是由几部分组成的。比如CVarStr就是由三部分组成(详见下文)。VB PCODE中常见的“助记符”如下面所示:(只总结出了一点,恳请各位补充) <BR><BR>表示数据类型的: <BR><BR>I2 ---- Integer,占一个字节的整数(汇编里的BYTE) <BR>I4 ---- Integer,占两个字节的整数(汇编里的WORD) <BR>I8 ---- Integer,占四个字节的整数(汇编里的DWORD) <BR>UI4---- Unsigned Integer,无符号整数 <BR>UI8---- Unsigned Integer,无符号整数 <BR>R4 ---- Real,单精度实数(Single) <BR>R8 ---- Real,双精度实数(Double) <BR>Str---- String,字符串类型 <BR>Var---- Variant,变量类型。这就是BASIC特殊的地方,它允许用户在使用变量前不进行声明,这种不声明的变量就用这种类型存储,它可以包括数字、字串等各种类型。我看M$的这个玩意儿没给用户带来方便,只能让一些初学编程的菜鸟思维混乱,让咱们破解时也非常郁闷:(。它的存储方式非常奇怪,比方说你看到一个VARIANT类型的数据被放到内存里了,你跟过去找,结果什么也找不到。看雪书上说应该D *(EAX+8),原来它真正的数据往后挪了8个字节,真不知在搞什么.....BTW:如果是一个数值类型的数据,它的地址向后移8个字节即为真正的数值,如果是一个字符串型的数据,它的地址向后移8个字节即为指向一个UNICODE字串的指针。 <BR><BR>表示堆栈操作的:(PCODE没有寄存器,全部通过堆栈传送数据,因此非常重要) <BR><BR>St ---- Store,把当前栈顶的数据放在内存里 <BR>Ld ---- Load,把内存某处的数据压入堆栈 <BR>Lit---- Literal,把一个“立即数”压入堆栈 <BR><BR>其它重要的: <BR><BR>C ---- Convert,数据转换。如CI4I2即把BYTE扩充为WORD(I2->I4) <BR>Eq ---- Equal,判断是否相等,并把结果(0或1)入栈 <BR>Lt ---- 判断是否小于 <BR>Gt ---- 判断是否大于 <BR>Len---- 得到字串长度 <BR><BR>跳转指令: <BR><BR>Branch ---- 无条件跳转 <BR>BranchT ---- 栈顶数据为真则跳 <BR>BranchF ---- 栈顶数据为假则跳 <BR><BR>一些算术运算: <BR><BR>Add , Sub 等等应该都比较好认吧。 <BR><BR>从一篇介绍PCODE的文章里抄来一些,不知有没有用: <BR><BR>Prefix Control <BR>------------------------------------------------------------------------------------ <BR>cbo Combo box <BR>chk Check box <BR>cmd Command Button <BR>dir Directory box <BR>drv Drive list box <BR>fil File list box <BR>fra Frame <BR>frm Form <BR>grd Grid <BR>hsb Horizontal scrollbar <BR>img Image <BR>lbl Label <BR>lin Line <BR>lst List box <BR>mnu Menu <BR>ole OLE client <BR>opt Option button <BR>pic Picture Box <BR>shp Shape <BR>tmr Timer <BR>txt Text box <BR>vsb Vertical scrollbar <BR>----------------------------------------------------------------------------------------- <BR><BR>还有一些不太清楚的,都是我的猜想,希望大虾解释: <BR><BR>Call ---- 调用过程 <BR>Free ---- 释放内存空间 <BR>Rf ---- 局部变量???? <BR>Pr ---- ???? <BR>Ad ---- 是不是Address?? <BR>HardType--是干什么的? <BR><BR>这些组合在一起就成了多种多样的指令,很有趣吧。 <BR><BR>还有一个要特别强调的是PCODE的堆栈,PCODE几乎所有的指令都要对堆栈进行操作,有许多指令都是针对栈顶的一个或两个数据进行操作,因此在动态调试PCODE时要十分注意右边显示的堆栈区,并经常查看内存,这样才能理解指令的意义。 <BR><BR>下面来实践一下,运行起尘封已久的VB,在FORM上放一个TEXT1,一个BUTTON1,双击Button1,在下面输入: <BR><BR>Private Sub Command1_Click() <BR> st1 = Text1.Text <BR> st2 = "" <BR> m = Len(Text1.Text) <BR> For i = 1 To m <BR> st2 = st2 + Mid$(Text1.Text, m - i + 1, 1) <BR> Next i <BR> MsgBox st2, vbOKOnly, "CRACK" <BR>End Sub <BR><BR>呵呵,很简单是不是。按一下按钮就把TEXT里的文本反过来显示在消息框里。 <BR>下面来“生成工程”,注意一定要在“选项”里选择生成P-CODE文件。然后用Exdec分析一下: <BR><BR>Proc: 401a90 <BR>4019B0: 04 FLdRfVar local_008C ;好像是一个指向TEXT的指针 <BR>4019B3: 21 FLdPrThis ;先给一个下马威,前几句全不太明白! <BR>4019B4: 0f VCallAd text ;用WKTVBDebug过这一句时能看到Form1.text1 <BR>4019B7: 19 FStAdFunc local_0088 ;猜想应该是取得句柄之类的事情 <BR>4019BA: 08 FLdPr local_0088 ; <BR>4019BD: 0d VCallHresult get__ipropTEXTEDIT ;调用,从字面上可以看出是GetText <BR>4019C2: 3e FLdZeroAd local_008C ;好像压入一个指向上面文本的指针,不太清楚,反正上面这个过程很经典啦,几乎从文本框读数都是这样 <BR>4019C5: 46 CVarStr local_00AC ;把上面得到的字串转为Var格式 <BR>4019C8: Lead1/f6 FStVar ;再把这个VAR数据入栈 st1 <BR>4019CC: 1a FFree1Ad local_0088 ;释放前面的空间 <BR>4019CF: 3a LitVarStr: ( local_00CC ) ;压入一个立即数:空字串st2="" <BR>4019D4: Lead2/00 FStVarCopy <BR>4019D8: 04 FLdRfVar local_008C <BR>4019DB: 21 FLdPrThis <BR>4019DC: 0f VCallAd text <BR>4019DF: 19 FStAdFunc local_0088 <BR>4019E2: 08 FLdPr local_0088 <BR>4019E5: 0d VCallHresult get__ipropTEXTEDIT ;和上面相同,得到字串 <BR>4019EA: 6c ILdRf local_008C ;压入字串 <BR>4019ED: 4a FnLenStr ;得到字串的长度m <BR>4019EE: Lead2/69 CVarI4 local_00CC ;转为VAR类型 <BR>4019F2: Lead1/f6 FStVar ;VAR类型的长度入栈 <BR>4019F6: 2f FFree1Str local_008C ;释放内存空间 <BR>4019F9: 1a FFree1Ad local_0088 <BR>4019FC: 28 LitVarI2: ( local_00FC ) 0x1 (1) ;压入一个立即数0x1 <BR>401A01: 04 FLdRfVar local_00EC ;local_00EC是循环变量i <BR>401A04: 04 FLdRfVar local_00DC ;这个是上面得到的长度m <BR>401A07: Lead3/68 ForVar: (when done) 401A67 ;FOR i=1 to m 开始循环 <BR>401A0D: 04 FLdRfVar local_008C <BR>401A10: 21 FLdPrThis <BR>401A11: 0f VCallAd text <BR>401A14: 19 FStAdFunc local_0088 <BR>401A17: 08 FLdPr local_0088 <BR>401A1A: 0d VCallHresult get__ipropTEXTEDIT ;和上面相同的过程,得到字串 <BR>401A1F: 04 FLdRfVar local_00BC ;把local_BC压入,这实际上是st2 <BR>401A22: 28 LitVarI2: ( local_013C ) 0x1 (1) ;压一个0x1,CALL的参数 <BR>401A27: 04 FLdRfVar local_00DC ;字串长度m <BR>401A2A: 04 FLdRfVar local_00EC ;循环变量i <BR>401A2D: Lead0/9c SubVar ;相减 m-i <BR>401A31: 28 LitVarI2: ( local_00CC ) 0x1 (1) ;再压入一个0x1 <BR>401A36: Lead0/94 AddVar local_012C ;再加1, m-i+1,CALL的参数 <BR>401A3A: Lead1/22 CI4Var ;转成整数型 <BR>401A3C: 6c ILdRf local_008C ;压入,作为下面CALL的参数 <BR>401A3F: 0b ImpAdCallI2 ;这是rtcMidCharBStr,源码中的Mid$() <BR>401A44: 46 CVarStr local_014C ;把取得的字符转成Var型 <BR>401A47: Lead0/94 AddVar local_015C ;把新取得的字符和上面的401A1F处的st2连起来 <BR>401A4B: Lead1/f6 FStVar <BR>401A4F: 2f FFree1Str local_008C <BR>401A52: 1a FFree1Ad local_0088 ;释放 <BR>401A55: 36 FFreeVar <BR>401A5E: 04 FLdRfVar local_00EC ;设好循环变量 <BR>401A61: Lead3/7e NextStepVar: (continue) 401A0D ;NEXT i,循环变量+1,直到结束 <BR>401A67: 27 LitVar_Missing ;VB里面那些带[]的可选参数,如果不加设定 <BR>401A6A: 27 LitVar_Missing ;就会变成这种Missing或NULL的形式 <BR>401A6D: 3a LitVarStr: ( local_00CC ) CRACK ;压入字串,MsgBox的标题 <BR>401A72: 4e FStVarCopyObj local_00AC ;把刚压入的字串复制到local_AC <BR>401A75: 04 FLdRfVar local_00AC ;再压进去一次(???) <BR>401A78: f5 LitI4: 0x0 0 (....) ;消息框的样式 vbOKOnly <BR>401A7D: 04 FLdRfVar local_00BC ;这是上面计算得到的反转字串 <BR>401A80: 0a ImpAdCallFPR4: ;这个是rtcMsgBox,共有五个参数 <BR>401A85: 36 FFreeVar <BR>401A8E: 13 ExitProcHresult ;结束过程 <BR><BR>我尽量想把分析写得明白一些,但还是有几句解释不清,希望精通PCODE的大侠解释一下,小弟代表广大菜鸟同胞感激不尽。 |