solly 发表于 2019-4-29 15:25

160 个CrackMe之 124 - noos.3 (VB5 p-code)算法分析和5秒爆破算码注册机

本帖最后由 solly 于 2019-6-23 22:31 编辑

160 个 CrackMe 之 124 -- noos.3 是一个VB5编写的CrackMe程序,运行时需要 Visual Basic 5.0 运行库支持(MSVBVM50.DLL等),同时这个程序是编译成p-code的程序,需要跟踪p-code中间码,虽然我从QB4.5,VB4到VB6都用过,后面的VB.NET没用过,对Basic语法非常了解(show years old?),但对p-code没怎么了解过,以前也没有跟踪过,所以跟踪这个CM花了不少时间,了解了p-code的跟踪,虽然VB p-code现在用不到了,但我们的目标不是在于用不用这个语言,而是在于理解更多的知识,老知识是新知识的基础,就象现在的虚拟机加壳,是不是与p-code码有点相似???

这个CM的追踪,用到了几个工具,包括 WKTVBDebugger,VBExplorer和OllyICE,三个工具各有各的作用。
1、WKTVBDebugger不是用来调试和跟踪,而是用来查询MSVBVM50.DLL中对p-code的中间码进行解释执行的各个函数。
2、VBExplorer用来反编译CM程序,找到各个控件的执行代码,不过还是p-code代码。(试过其它几种VB反编译器,这个CM用VBExplorer效果完美,其它几个都不行,包括 VB Decompiler Pro都不行)
3、OllyICE则是用来一步一步跟踪调试程序,当然跟踪的都是 MSVBVM50.DLL 中的代码,因为 VB p-code代码都是由MSVBVM50.DLL来执行,没有原生二进制代码。至于为什么用OllyICE,而不用 WKTVBDebugger,那就是WKTVBDebugger 是 p-code 代码级跟踪,但一个p-code包括多个微操作,只有 OllyICE跟踪MSVBVM50.DLL才看得到,而 WKTVBDebugger 却不行,特别是一些数据保存,用WKTVBDebugger跟踪,不晓得存到哪里去了,还有那个栈的显示方式也不方便。

另外,为了后期算法效果验证,装了一个win98的虚拟机和一套Visual Basic 5,把p-code代码改编成 basic代码,直接将这个 CM reverse成一个basic源码CM了。

VB p-code 代码中,有下面几个特点:
1、MSVBVM50.DLL 将CPU的寄存器 ESI 是作为p-code指令寄存器使用的,MSVBVM50.DLL内部通过ESI中的地址来读取指令,并解释执行。
2、使用 EBP 寄存器作为变量的地址基址寄存器,所有变量在 p-code 的指令中都是保存为一个相对偏移量,在访问变量前需加上EBP的值,作为其真正的数据地址来读取数据。
3、函数调用的参数、参与计算的变量的值,或者字符串数据的地址,都是通过栈来管理的,数据使用前,均push到栈,指令执行时再pop出来,运算或调用的结果再重新push到栈,这个有点类似CPU的浮点指令的处理方式
4、每个p-code指令执行完成后,接着会通过ESI的指示取出下一条指令到EAX中,再通过一条类似 JMP DWORD PTR的指令跳转到该指令的解释程序,对刚取出的指令进行处理,依次类推。。。

VB中的变量,可以不定义类型,默认是一个 Variant 类型,这类种类型对于跟踪来説,也非常麻烦,占用16个字节不説,也不直观,数字还好,字符串还是一个引用地址,比较麻烦。
Variant中VS98的头文件中有定义,如下:
structtagVARIANT
    {
    union
      {
      struct__tagVARIANT
            {
            VARTYPE vt;
            WORD wReserved1;
            WORD wReserved2;
            WORD wReserved3;
            union
                {
                LONG lVal;
                BYTE bVal;
                SHORT iVal;
                FLOAT fltVal;
                DOUBLE dblVal;
                VARIANT_BOOL boolVal;
                _VARIANT_BOOL bool;
                SCODE scode;
                CY cyVal;
                DATE date;
                BSTR bstrVal;
                IUnknown __RPC_FAR *punkVal;
                IDispatch __RPC_FAR *pdispVal;
                SAFEARRAY __RPC_FAR *parray;
                BYTE __RPC_FAR *pbVal;
                SHORT __RPC_FAR *piVal;
                LONG __RPC_FAR *plVal;
                FLOAT __RPC_FAR *pfltVal;
                DOUBLE __RPC_FAR *pdblVal;
                VARIANT_BOOL __RPC_FAR *pboolVal;
                _VARIANT_BOOL __RPC_FAR *pbool;
                SCODE __RPC_FAR *pscode;
                CY __RPC_FAR *pcyVal;
                DATE __RPC_FAR *pdate;
                BSTR __RPC_FAR *pbstrVal;
                IUnknown __RPC_FAR *__RPC_FAR *ppunkVal;
                IDispatch __RPC_FAR *__RPC_FAR *ppdispVal;
                SAFEARRAY __RPC_FAR *__RPC_FAR *pparray;
                VARIANT __RPC_FAR *pvarVal;
                PVOID byref;
                CHAR cVal;
                USHORT uiVal;
                ULONG ulVal;
                INT intVal;
                UINT uintVal;
                DECIMAL __RPC_FAR *pdecVal;
                CHAR __RPC_FAR *pcVal;
                USHORT __RPC_FAR *puiVal;
                ULONG __RPC_FAR *pulVal;
                INT __RPC_FAR *pintVal;
                UINT __RPC_FAR *puintVal;
                struct__tagBRECORD
                  {
                  PVOID pvRecord;
                  IRecordInfo __RPC_FAR *pRecInfo;
                  } __VARIANT_NAME_4;
                } __VARIANT_NAME_3;
            } __VARIANT_NAME_2;
      DECIMAL decVal;
      } __VARIANT_NAME_1;
    };
其中的成员 VARTYPE vt; 指明当前 Variable 变量中保存的实际数据类型,有以下取值:
enum VARENUM{
    VT_EMPTY    = 0,
    VT_NULL   = 1,
    VT_I2       = 2,
    VT_I4       = 3,
    VT_R4       = 4,
    VT_R8       = 5,
    VT_CY       = 6,
    VT_DATE   = 7,
    VT_BSTR   = 8,
    VT_DISPATCH = 9,
    VT_ERROR    = 10,
    VT_BOOL   = 11,
    VT_VARIANT= 12,
    VT_UNKNOWN= 13,
    VT_DECIMAL= 14,
    VT_I1       = 16,
    VT_UI1      = 17,
    VT_UI2      = 18,
    VT_UI4      = 19,
    VT_I8       = 20,
    VT_UI8      = 21,
    VT_INT      = 22,
    VT_UINT   = 23,
    VT_VOID   = 24,
    VT_HRESULT= 25,
    VT_PTR      = 26,
    VT_SAFEARRAY   = 27,
    VT_CARRAY      = 28,
    VT_USERDEFINED = 29,
    VT_LPSTR       = 30,
    VT_LPWSTR      = 31,
    VT_RECORD      = 36,
    VT_INT_PTR   = 37,
    VT_UINT_PTR    = 38,
    VT_FILETIME    = 64,
    VT_BLOB      = 65,
    VT_STREAM      = 66,
    VT_STORAGE   = 67,
    VT_STREAMED_OBJECT= 68,
    VT_STORED_OBJECT    = 69,
    VT_BLOB_OBJECT      = 70,
    VT_CF               = 71,
    VT_CLSID            = 72,
    VT_VERSIONED_STREAM = 73,
    VT_BSTR_BLOB      = 0x0fff,
    VT_VECTOR         = 0x1000,
    VT_ARRAY            = 0x2000,
    VT_BYREF            = 0x4000,
    VT_RESERVED         = 0x8000,
    VT_ILLEGAL          = 0xffff,
    VT_ILLEGALMASKED    = 0x0fff,
    VT_TYPEMASK         = 0x0fff
} ;

更详细的了解可去 https://docs.microsoft.com/en-us ... p/variant-data-type 看看。

现在进入正题。
下面将过程简单讲一下,先运行一下CM,看看界面:

该CM是要求输入一个Unlock码来解锁,将界面上的红灯变成绿灯,同时将上面那个锁的图片由“Activated”变成“Deactivated”。
该CM的算法相对简单,先生成一个数组,并初始化一些计算后的数据,一些固定值,但CM为了增加难度,是动态运算生成的数据。并且还会生成另外一个索引数组,CM居然一个一个的数组元素进行赋值,一共62个元素,进行了62次同样的操作,导到p-code代码好长。不过,所有的算法都是在一个方法内,没有多层次的调用。

要跟踪p-code代码,先要定位解释p-code代码执行的代码在 MSVBVM50.DLL 中的位置,这个就是通过 WKTVBDebugger 来完成的,下图是 WKTVBDebugger 界面(在我win10机器上,要运行loader两次才会弹出这个调试界面):

先点击“Form Manager (Ctrl+F)”按钮,调出 VB Form资源管理界面,见上图的左边,在最上面的下拉框中选择“Main",下面几个按钮就可以用了,再点击”command“按钮,在弹出的面板中的下拉框中,选择”Command1“,就显示出了VB程序command按钮的相关信息,如下图:

我们再将上图中的”BPX“按钮按下,这样就可以断下Command1的Click()事件。
再次我们点击WTKVBDebugger主界面中的”Opcodes (Ctrl+O)“按钮,就会弹出一个”Opcodes Control dialog“,这里就是各种p-code指令在MSVBVM50.DLL中解释执行的子例程地址了,如下图所示:

如 p-code指令LitStr:的p-code码为0x1B,由msvbvm50.dll中地址为0x741BDE59开始的代码来解释执行的。通过这个地址,我们就可以在 OllyICE中下条件断点了,条件就是ESI的值,这个值在哪里找呢,可以在 WKTVBDebugger中找到,也可以通过VBExplorer来找到,用WKTVBDebugger不方便,我是用VBExplorer来找这个值的,如下图:

在VBExplorer中,很方便就可以定位各个控件的事件处理过程,在VB中叫做Sub(),可以在代码区看到,指令区第一列就是地址,如上图中的":0041EA08",这个值就是条件断点中ESI的值。
VB还可以查看Form中各个控件的参数,如下图,在列表中可以选择各个控件,查看相关参数:

以上是一些基础知识,下面进入 OllyICE 操作,启动OllyICE,并载入noos.3.exe程序:

载入后,只有两行指令,就是进入MSVBVM50.DLL。右击”转到-->表达式”,进入下图:

我们在VBExplorer中查到Command1.Click()事件中的第一条指令是“LargeBos”,然后通过 WKTVBDebugger中的Opcodes Control Dialog查得"LargeBos"的解释代码位于MSVBVM50.DLL中地址0x741BD39E处,我们就在OllyICE中输入这个地址,点“确定”,来到了MSVBVM50.DLL的代码空间:

然后就在这里下断点,就可以断下Command1.Click()事件,不过要设置条件,如下图,下一个条件断点:

条件就是p-code的ESI指令寄存器的值,这个值就是 VBExplorer中的指令地址值,因此条件是 ESI>= 0x0041EA08,这里不要用==,要用>=,因为p-code执行前会先取指,当执行这个指令时esi一般指向了指令的参数或下一个指令了,如下图:

下完条件断点后,地址区会显示紫色背景,如下图:

到这里,就可以进行跟踪执行了,在OllyICE中跟踪执行p-code代码时,要时刻对照VBExplorer中的指令,通过OllyICE中的ESI和VBExplorer中的地址值,就可以确定当前正在执行哪条p-code指令,这样就能大概确定当前代码正在干什么,不然就是在MSVBVM中乱转了。
下面是跟踪本CM下的所有断点,可以参考一下:

下面説一下,VB的Variant类型变量在内存中的形式,如下图:

这是一个16字节的Variant变量,保存了字符串"z"。最前面黄色框框中0x0008表示变量保存的是字符串,后面黄色框框中保存的是“z”字符串的地址,表示"z"保存在“0x006116A4"处,如下图:

如果是数字类变量,则保存的直接就是数字,不是地址了。

以上是一些操作手法説明,下面对VB代码进一步説明,具体的每个p-code代码的跟踪就不説了,那是一个体力活。

该CM的算法写在一个子函数中(在VBExplorer中可找到),Command1.Click()事件会调用这个子函数,下面对函数作简要分析:
:0041F468F500000000                      LitI4                      ;Push 00000000
:0041F46DF53D000000                      LitI4                      ;Push 0000003D
:0041F4720418FF                        FLdRfVar                   ;Push LOCAL_00E8
:0041F475FE8E0100FFFF1000                Redim                      ;
******Possible String Ref To->"0"
                               |
:0041F47F3A08FF1100                      LitVarStr                  ;PushVarString ptr_0041DDE0
:0041F484F500000000                      LitI4                      ;Push 00000000
:0041F4896C18FF                        ILdRf                      ;Push DWORD
:0041F48CFCB0                            Ary1StVarCopy            ;
******Possible String Ref To->"1"
                               |
:0041F48E3AF8FE1200                      LitVarStr                  ;PushVarString ptr_0041DDE8
:0041F493F501000000                      LitI4                      ;Push 00000001
:0041F4986C18FF                        ILdRf                      ;Push DWORD
:0041F49BFCB0                            Ary1StVarCopy            ;
'
'
'此处省略 n 行 p-code代码,这大段代码(包括前后留下来的部分代码)就是给一个62个元素的数组赋值
'
'
:0041F801FCB0                            Ary1StVarCopy            ;
******Possible String Ref To->"y"
                               |
:0041F8033A48FB4D00                      LitVarStr                  ;PushVarString ptr_0041DFA0
:0041F808F53C000000                      LitI4                      ;Push 0000003C
:0041F80D6C18FF                        ILdRf                      ;Push DWORD
:0041F810FCB0                            Ary1StVarCopy            ;
******Possible String Ref To->"z"
                               |
:0041F8123A38FB4E00                      LitVarStr                  ;PushVarString ptr_0041DFA8
:0041F817F53D000000                      LitI4                      ;Push 0000003D
:0041F81C6C18FF                        ILdRf                      ;Push DWORD
:0041F81FFCB0                            Ary1StVarCopy            ;
'
'上面代码生成一个含62个元素的数组,每个元素保存一个字符,字符如下,我们用字符串代替:
' Dim charString As String
' charString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
'这个数组就是后面用来进行对照我们输入的unlock码进行查表的
'
:0041F8210418FF                        FLdRfVar                   ;Push LOCAL_00E8
:0041F8240428FB                        FLdRfVar                   ;Push LOCAL_04D8
**********Reference To->msvbvm50.rtcArray
                               |
:0041F8270A4F000800                      ImpAdCallFPR4            ;Call ptr_0040100C; check stack 0008; Push EAX
:0041F82C0418FF                        FLdRfVar                   ;Push LOCAL_00E8
:0041F82F5A                              Erase                      ;vbaErase
:0041F8300428FB                        FLdRfVar                   ;Push LOCAL_04D8
:0041F833FCF668FF                        FStVar                     ;
******Possible String Ref To->""
                               |
:0041F8371B0500                        LitStr                     ;Push ptr_0041DD34
:0041F83A4364FF                        FStStrCopy               ;=SysAllocStringByteLen(Pop, ); SysFreeString Pop
'
'以上代码是复制前面生成的数组,并清理临时变量的空间
'我们将这个数组定义为:
'Dim charArray(62) As Variant 'LOCAL_00E8
'
:0041F83DF518000000                      LitI4                      ;Push 00000018
:0041F8427154FF                        FStR4                      ;Pop DWORD
:0041F845F51C000000                      LitI4                      ;Push 0000001C
:0041F84A7158FF                        FStR4                      ;Pop DWORD
'
'以上代码生成两个变量并初始化,我们定义如下,并初始化:
'Dim baseA As Long 'LOCAL_00A8
'Dim baseB As Long 'LOCAL_00AC
'baseA = 28 'H1C
'baseB = 24 'H18
' 下面两个是其它变量定义,后面要用到,这里没相应代码,也没有初始化
'Dim i As Long, j As Long
'Dim f As Double
'--------------------------------------------------------------------------
'下面是定义另一个数组,也是62个元素,并且初始化,我们定义如下:
' Dim Dim charArray2(62) As Variant ' LOCAL_00CC
'
'以下代码初始化前10个元素:
:0041F84DF500000000                      LitI4                      ;Push 00000000
:0041F852045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041F855F509000000                      LitI4                      ;Push 00000009
:0041F85AFE6420FB3104                  ForI4                      ;
'上面是一个For循环定义
' for i=0 to 9 step 1
'
:0041F8606C5CFF                        ILdRf                      ;Push DWORD
:0041F8636C5CFF                        ILdRf                      ;Push DWORD
:0041F866B2                              MulI4                      ;
:0041F8676C5CFF                        ILdRf                      ;Push DWORD
:0041F86AB2                              MulI4                      ;
:0041F86BFD6908FF                        CVarI4                     ;
:0041F86F6C5CFF                        ILdRf                      ;Push DWORD
:0041F8720434FF                        FLdRfVar                   ;Push LOCAL_00CC
:0041F87552                              Ary1StVar                  ;
:0041F8766C5CFF                        ILdRf                      ;Push DWORD
:0041F8790434FF                        FLdRfVar                   ;Push LOCAL_00CC
:0041F87CFC96                            Ary1LdRfVar                ;
:0041F87E2808FF1700                      LitVarI2                   ;PushVarInteger 0017
:0041F883FB9428FB                        AddVar                     ;
:0041F8876C5CFF                        ILdRf                      ;Push DWORD
:0041F88A0434FF                        FLdRfVar                   ;Push LOCAL_00CC
:0041F88D52                              Ary1StVar                  ;
:0041F88E3528FB                        FFree1Var                  ;Free LOCAL_04D8
'以上是循环体,生成第二个数组的前10个元素,对应VB代码如下,是不是很简单:
'   charArray2(i) = i * i * i + 23
'
:0041F891045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041F8946620FBF803                      NextI4                     ;
'循环结束
'Next i
''以下代码初始化中间26个元素:
:0041F899F50A000000                      LitI4                      ;Push 0000000A
:0041F89E045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041F8A1F523000000                      LitI4                      ;Push 00000023
:0041F8A6FE6418FB8E04                  ForI4                      ;
'上面是一个For循环定义
' for i=10 to 35 step 1
'
:0041F8AC6C58FF                        ILdRf                      ;Push DWORD
:0041F8AFF502000000                      LitI4                      ;Push 00000002
:0041F8B4B2                              MulI4                      ;
:0041F8B57158FF                        FStR4                      ;Pop DWORD
:0041F8B86C58FF                        ILdRf                      ;Push DWORD
******Possible String Ref To->"MTV"
                               |
:0041F8BB1B5000                        LitStr                     ;Push ptr_0041DFB0
**********Reference To->msvbvm50.rtcAnsiValueBstr
                               |
:0041F8BE0B51000400                      ImpAdCallI2                ;Call ptr_00401012; check stack 0004; Push EAX
:0041F8C3E7                              CI4UI1                     ;
:0041F8C4FB13                            XorI4                      ;
:0041F8C6EC                              CR8I4                      ;
:0041F8C7744CFF                        FStFPR8                  ;Fstp#8
:0041F8CA6F4CFF                        FLdFPR8                  ;Fld#8
:0041F8CDF405                            LitI2_Byte               ;Push 05
:0041F8CFEB                              CR8I2                      ;
:0041F8D0AB                              AddR8                      ;
:0041F8D1744CFF                        FStFPR8                  ;Fstp#8
:0041F8D46F4CFF                        FLdFPR8                  ;Fld#8
:0041F8D7FD6B08FF                        CVarR8                     ;
:0041F8DB6C5CFF                        ILdRf                      ;Push DWORD
:0041F8DE0434FF                        FLdRfVar                   ;Push LOCAL_00CC
:0041F8E152                              Ary1StVar                  ;
:0041F8E26C58FF                        ILdRf                      ;Push DWORD
:0041F8E5F501000000                      LitI4                      ;Push 00000001
:0041F8EAAA                              AddI4                      ;
:0041F8EB7158FF                        FStR4                      ;Pop DWORD
'以上是循环体,生成第二个数组的中间26个元素,对应VB代码如下:
'    baseA = baseA * 2
'    f = (baseA Xor Asc("MTV")) + 5
'    charArray2(i) = f
'    baseA = baseA + 1
'
:0041F8EE045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041F8F16618FB4404                      NextI4                     ;
'循环结束
'Next i
''下面是初始化第二个数组的最后26个元素:
:0041F8F6F524000000                      LitI4                      ;Push 00000024
:0041F8FB045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041F8FEF53D000000                      LitI4                      ;Push 0000003D
:0041F903FE6410FBEB04                  ForI4                      ;
'上面是For循环定义
' For i=36 to 61 step 1
'
:0041F9096C54FF                        ILdRf                      ;Push DWORD
:0041F90CF502000000                      LitI4                      ;Push 00000002
:0041F911B2                              MulI4                      ;
:0041F9127154FF                        FStR4                      ;Pop DWORD
:0041F9156C54FF                        ILdRf                      ;Push DWORD
******Possible String Ref To->"TMF"
                               |
:0041F9181B5200                        LitStr                     ;Push ptr_0041DFBC
**********Reference To->msvbvm50.rtcAnsiValueBstr
                               |
:0041F91B0B51000400                      ImpAdCallI2                ;Call ptr_00401012; check stack 0004; Push EAX
:0041F920E7                              CI4UI1                     ;
:0041F921FB13                            XorI4                      ;
:0041F923EC                              CR8I4                      ;
:0041F924744CFF                        FStFPR8                  ;Fstp#8
:0041F9276F4CFF                        FLdFPR8                  ;Fld#8
:0041F92AF43E                            LitI2_Byte               ;Push 3E
:0041F92CEB                              CR8I2                      ;
:0041F92DAB                              AddR8                      ;
:0041F92E744CFF                        FStFPR8                  ;Fstp#8
:0041F9316F4CFF                        FLdFPR8                  ;Fld#8
:0041F934FD6B08FF                        CVarR8                     ;
:0041F9386C5CFF                        ILdRf                      ;Push DWORD
:0041F93B0434FF                        FLdRfVar                   ;Push LOCAL_00CC
:0041F93E52                              Ary1StVar                  ;
:0041F93F6C54FF                        ILdRf                      ;Push DWORD
:0041F942F501000000                      LitI4                      ;Push 00000001
:0041F947AA                              AddI4                      ;
:0041F9487154FF                        FStR4                      ;Pop DWORD
'以上是循环体,生成第二个数组的中间26个元素,对应VB代码如下:
'    baseB = baseB * 2
'    f = (baseB Xor Asc("TMF")) + 62
'    charArray2(i) = f
'    baseB = baseB + 1
'
:0041F94B045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041F94E6610FBA104                      NextI4                     ;
' 循环结束
'Next i
''初始化结束,下面就是校验unlock码的过程
:0041F9530408FB                        FLdRfVar                   ;Push LOCAL_04F8
:0041F956050100                        ImpAdLdRf                  ;Push ptr
:0041F959240200                        NewIfNullPr                ;
:0041F95C0F1C03                        VCallAd                  ;Return the control index 09
:0041F95F190CFB                        FStAdFunc                  ;
:0041F962080CFB                        FLdPr                      ;=
***********Reference To:TextBox.Text
                              |
:0041F9650DA0000600                      VCallHresult               ;Call ptr_0041DD38
'以上代码,是取界面文本框中的 unlock code
' Dim sn As String
' sn = txtCode.Text
'
:0041F96A6C08FB                        ILdRf                      ;Push DWORD
:0041F96D4A                              FnLenStr                   ;vbaLenBstr
'以上代码计算unlock code 的长度
'Dim snlen As Integer
'Dim snlen2 As Integer
'
'snlen = Len(sn)
'
:0041F96EF501000000                      LitI4                      ;Push 00000001
:0041F973AA                              AddI4                      ;
:0041F974FC0E                            CUI1I4                     ;
:0041F976FCF062FF                        FStUI1                     ;
:0041F97A2F08FB                        FFree1Str                  ;SysFreeString ; =0
:0041F97D1A0CFB                        FFree1Ad                   ;Push ; Call [[]+8]; []=0
:0041F980F401                            LitI2_Byte               ;Push 01, 循环起点 i=1
:0041F982FC0D                            CUI1I2                     ;
:0041F9840460FF                        FLdRfVar                   ;Push LOCAL_00A0, 循环变量 i
:0041F987FCE062FF                        FLdUI1                     ;
:0041F98BFC14                            CI2UI1                     ;No Operation
:0041F98DF401                            LitI2_Byte               ;Push 01
:0041F98FAD                              SubI2                      ;
:0041F990FC0D                            CUI1I2                     ;, 循环结束值 = (snlen2-1)
:0041F992FE6204FB8005                  ForUI1                     ;
'上面生成另一个长度值加上1的变量,作为后面循环的结束值:
' snlen2 = (snlen + 1)
'同时生成一个循环
' Dim snArray(22) As String
' unlock code 最长为20字符,界面上限定了,最多输入20个字符,因此定义 snArray(22) 用于保存unlock code
' For j=1 to (snlen2-1) step 1
'-----------------------------------------------------------------------
' 下面是一个循环体将unlock code 分解成单个字符并放入数组snArray(22)
:0041F9980408FB                        FLdRfVar                   ;Push LOCAL_04F8
:0041F99B050100                        ImpAdLdRf                  ;Push ptr
:0041F99E240200                        NewIfNullPr                ;
:0041F9A10F1C03                        VCallAd                  ;Return the control index 09
:0041F9A4190CFB                        FStAdFunc                  ;
:0041F9A7080CFB                        FLdPr                      ;=
***********Reference To:TextBox.Text
                              |
:0041F9AA0DA0000600                      VCallHresult               ;Call ptr_0041DD38
:0041F9AF28F4FA0100                      LitVarI2                   ;PushVarInteger 0001
:0041F9B4FCE060FF                        FLdUI1                     ;
:0041F9B8E7                              CI4UI1                     ;
:0041F9B93E08FB                        FLdZeroAd                  ;Push DWORD ; =0
:0041F9BC4628FB                        CVarStr                  ;
:0041F9BF04E4FA                        FLdRfVar                   ;Push LOCAL_051C
**********Reference To->msvbvm50.rtcMidCharVar
                               |
:0041F9C20A53001000                      ImpAdCallFPR4            ;Call ptr_00401018; check stack 0010; Push EAX
:0041F9C704E4FA                        FLdRfVar                   ;Push LOCAL_051C
:0041F9CAFCE060FF                        FLdUI1                     ;
:0041F9CEE7                              CI4UI1                     ;
:0041F9CF041CFF                        FLdRfVar                   ;Push LOCAL_00E4
:0041F9D252                              Ary1StVar                  ;
:0041F9D31A0CFB                        FFree1Ad                   ;Push ; Call [[]+8]; []=0
:0041F9D636060028FBF4FAE4                FFreeVar                   ;Free 0006/2 variants
'以上是循环体,完成以下功能,就是将unlock code 分解成字符存入数组
'snArray(j) = Mid(sn, j, 1)
'
:0041F9DF0460FF                        FLdRfVar                   ;Push LOCAL_00A0
:0041F9E2FE7804FB3005                  NextUI1                  ;
'循环结束
'Next j
'--------------------------------------------------------------------------------------
'下面是对unlock code 数组进行运算校验是否合法,为两层循环,进行校验。
:0041F9E8F400                            LitI2_Byte               ;Push 00
:0041F9EAFBFD                            CStrUI1                  ;vbaStrI2
:0041F9EC3178FF                        FStStr                     ;SysFreeString ; =Pop
'始化变量
'Dim s3 As String
's3 = CStr(0)
'Dim f3 As Double
'f3 = 0
'
:0041F9EFF501000000                      LitI4                      ;Push 00000001
:0041F9F40458FF                        FLdRfVar                   ;Push LOCAL_00A8
:0041F9F7FCE062FF                        FLdUI1                     ;
:0041F9FBFC14                            CI2UI1                     ;No Operation
:0041F9FDF401                            LitI2_Byte               ;Push 01
:0041F9FFAD                              SubI2                      ;
:0041FA00E7                              CI4UI1                     ;
:0041FA01FE64DCFA0A06                  ForI4                      ;
'第一层循环
' For i=1 to (snlen2-1) step 1
' 遍历 unlock code 数组
:0041FA07F500000000                      LitI4                      ;Push 00000000
:0041FA0C045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041FA0FF53D000000                      LitI4                      ;Push 0000003D
:0041FA14FE64D4FA0206                  ForI4                      ;
'第二层循环
' For j=0 to 61 step 1
'遍历那个 0~z的字符串数组,找出与unlock code 中对应的字符的索引值,通过索引值查询第2个62元素数组中保存的相同索引的数据
:0041FA1A6C58FF                        ILdRf                      ;Push DWORD
:0041FA1D041CFF                        FLdRfVar                   ;Push LOCAL_00E4
:0041FA20FC96                            Ary1LdRfVar                ;
:0041FA22045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041FA25FD930340                        CDargRef                   ;
:0041FA290468FF                        FLdRfVar                   ;Push LOCAL_0098
:0041FA2CFEAE28FB0100                  VarIndexLdVar            ;
:0041FA32FB33                            EqVarBool                  ;
:0041FA343528FB                        FFree1Var                  ;Free LOCAL_04D8
:0041FA371CFA05                        BranchF                  ;If Pop=0 then ESI=0041FA62
:0041FA3A6C78FF                        ILdRf                      ;Push DWORD
:0041FA3D4608FF                        CVarStr                  ;
:0041FA406C5CFF                        ILdRf                      ;Push DWORD
:0041FA430434FF                        FLdRfVar                   ;Push LOCAL_00CC
:0041FA46FC96                            Ary1LdRfVar                ;
:0041FA48FB9428FB                        AddVar                     ;
:0041FA4C60                              CStrVarTmp               ;
:0041FA4D3178FF                        FStStr                     ;SysFreeString ; =Pop
:0041FA503528FB                        FFree1Var                  ;Free LOCAL_04D8
:0041FA536C78FF                        ILdRf                      ;Push DWORD
:0041FA56FC33                            CR8Str                     ;
:0041FA586C58FF                        ILdRf                      ;Push DWORD
:0041FA5BEC                              CR8I4                      ;
:0041FA5CB3                              MulR8                      ;
:0041FA5DFC00                            CStrR8                     ;
:0041FA5F3178FF                        FStStr                     ;SysFreeString ; =Pop
'循环体,完成以下运算过程:
'      'If snArray(i) = charArray(j) Then   'using array
'      If snArray(i) = Mid(charString, j + 1, 1) Then 'using string
'         f3 = Val(s3) + charArray2(j)
'         s3 = CStr(f3 * i)
'      End If
'
:0041FA62045CFF                        FLdRfVar                   ;Push LOCAL_00A4
:0041FA6566D4FAB205                      NextI4                     ;
'内层循环结束
'Next j
':0041FA6A0458FF                        FLdRfVar                   ;Push LOCAL_00A8
:0041FA6D66DCFA9F05                      NextI4                     ;
'外层循环结束
'Next i
':0041FA726C78FF                        ILdRf                      ;Push DWORD
:0041FA75FC33                            CR8Str                     ;
:0041FA77FA407D1573D18401                LitDate                  ;
:0041FA80C8                              EqR4                     ;
:0041FA811CC806                        BranchF                  ;If Pop=0 then ESI=0041FB30
'以上代码是对运行结果与一个常量进行对比,上面p-code代码显示是 Date类型,反正就是一个长整数"616388714737576#",VB代码如下:
' If Val(s3) != 616388714737576# Then
'   goto 0041FB30
' End if
'
' 下面校验正确时的处理:
******Possible String Ref To->"Thank you for registering"
                               |
:0041FA841B0000                        LitStr                     ;Push ptr_0041DCFC
:0041FA875408001000                      FMemStStrCopy            ;
:0041FA8C2828FB0100                      LitVarI2                   ;PushVarInteger 0001
:0041FA91F508000000                      LitI4                      ;Push 00000008
:0041FA960478FF                        FLdRfVar                   ;Push LOCAL_0088
:0041FA994D08FF0840                      CVarRef                  ;
:0041FA9E04F4FA                        FLdRfVar                   ;Push LOCAL_050C
**********Reference To->msvbvm50.rtcMidCharVar
                               |
:0041FAA10A53001000                      ImpAdCallFPR4            ;Call ptr_00401018; check stack 0010; Push EAX
:0041FAA604F4FA                        FLdRfVar                   ;Push LOCAL_050C
:0041FAA928E4FA0100                      LitVarI2                   ;PushVarInteger 0001
:0041FAAEF501000000                      LitI4                      ;Push 00000001
:0041FAB30478FF                        FLdRfVar                   ;Push LOCAL_0088
:0041FAB64DE8FE0840                      CVarRef                  ;
:0041FABB04C4FA                        FLdRfVar                   ;Push LOCAL_053C
**********Reference To->msvbvm50.rtcMidCharVar
                               |
:0041FABE0A53001000                      ImpAdCallFPR4            ;Call ptr_00401018; check stack 0010; Push EAX
:0041FAC304C4FA                        FLdRfVar                   ;Push LOCAL_053C
:0041FAC6FB94B4FA                        AddVar                     ;
:0041FACAFC22                            CI4Var                     ;vbaI4Var
:0041FACC9908001400                      FMemStI4                   ;Pop DWORD [+0008]
:0041FAD1360A0028FBE4FAF4                FFreeVar                   ;Free 000A/2 variants
:0041FADE2828FB0100                      LitVarI2                   ;PushVarInteger 0001
:0041FAE3F50D000000                      LitI4                      ;Push 0000000D
:0041FAE80478FF                        FLdRfVar                   ;Push LOCAL_0088
:0041FAEB4D08FF0840                      CVarRef                  ;
:0041FAF004F4FA                        FLdRfVar                   ;Push LOCAL_050C
**********Reference To->msvbvm50.rtcMidCharVar
                               |
:0041FAF30A53001000                      ImpAdCallFPR4            ;Call ptr_00401018; check stack 0010; Push EAX
:0041FAF804F4FA                        FLdRfVar                   ;Push LOCAL_050C
:0041FAFB28E4FA0100                      LitVarI2                   ;PushVarInteger 0001
:0041FB00F505000000                      LitI4                      ;Push 00000005
:0041FB050478FF                        FLdRfVar                   ;Push LOCAL_0088
:0041FB084DE8FE0840                      CVarRef                  ;
:0041FB0D04C4FA                        FLdRfVar                   ;Push LOCAL_053C
**********Reference To->msvbvm50.rtcMidCharVar
                               |
:0041FB100A53001000                      ImpAdCallFPR4            ;Call ptr_00401018; check stack 0010; Push EAX
:0041FB1504C4FA                        FLdRfVar                   ;Push LOCAL_053C
:0041FB18FB94B4FA                        AddVar                     ;
:0041FB1CFC22                            CI4Var                     ;vbaI4Var
:0041FB1E9908001800                      FMemStI4                   ;Pop DWORD [+0008]
:0041FB23360A0028FBE4FAF4                FFreeVar                   ;Free 000A/2 variants
'
'上面代码是生成三个变量:
'Dim thankyou As String
'Dim a1 As Integer, a2 As Integer
'   thankyou = "Thank you for registering"
'   a1 = Val(Mid(s3, 8, 1) + Mid(s3, 1, 1)) '16
'   a2 = Val(Mid(s3, 13, 1) + Mid(s3, 5, 1)) '58
'这三个变量,是下面子函数调用的参数。
'其中一个字符串是"Thank you for registering",不过并不会通过对话框提示
'a1, a2 则是计算后的校验码约束:
' 1、计算后的校验码第8位为1
' 2、计算后的校验码第1位为6
' 3、计算后的校验码第13位为5
' 4、计算后的校验码第5位为8
'-----------------------------------------------------------------------------------------------
' 校验失败来到这里
' 调用一个了函数进行界面状态的显示(不管校验正确与否,都会调用下面的子函数)
**********Reference To-> sub_0041F430
                               |
:0041FB300A54000000                      ImpAdCallFPR4            ;Call ptr_0041D60C; check stack 0000; Push EAX
:0041FB3514                              ExitProc                   ;end proc
:0041FB360000                            LargeBos                   ;IDE beginning of line with 00 byte codes


上面就是对关键算法的分析,用Visual Basic语法表示如下:

Public Sub SUB_0041FB38(ByRef iFlag%)
Dim str1 As String 'LOCAL_009C
Dim baseA As Long 'LOCAL_00A8
Dim baseB As Long 'LOCAL_00AC
' iFlag -->00A4
Dim charArray(62) As Variant 'LOCAL_00E8
Dim charArray2(62) As Variant ' LOCAL_00CC

Dim charString As String

On Error Resume Next

charString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
'using string replace of array follow:
charArray(0) = "0"
charArray(1) = "1"
charArray(2) = "2"
charArray(3) = "3"
charArray(4) = "4"
charArray(5) = "5"
charArray(6) = "6"
charArray(7) = "7"
charArray(8) = "8"
charArray(9) = "9"
charArray(10) = "A"
charArray(11) = "B"
charArray(12) = "C"
charArray(13) = "D"
charArray(14) = "E"
charArray(15) = "F"
charArray(16) = "G"
charArray(17) = "H"
charArray(18) = "I"
charArray(19) = "J"
charArray(20) = "K"
charArray(21) = "L"
charArray(22) = "M"
charArray(23) = "N"
charArray(24) = "O"
charArray(25) = "P"
charArray(26) = "Q"
charArray(27) = "R"
charArray(28) = "S"
charArray(29) = "T"
charArray(30) = "U"
charArray(31) = "V"
charArray(32) = "W"
charArray(33) = "X"
charArray(34) = "Y"
charArray(35) = "Z"
charArray(36) = "a"
charArray(37) = "b"
charArray(38) = "c"
charArray(39) = "d"
charArray(40) = "e"
charArray(41) = "f"
charArray(42) = "g"
charArray(43) = "h"
charArray(44) = "i"
charArray(45) = "j"
charArray(46) = "k"
charArray(47) = "l"
charArray(48) = "m"
charArray(49) = "n"
charArray(50) = "o"
charArray(51) = "p"
charArray(52) = "q"
charArray(53) = "r"
charArray(54) = "s"
charArray(55) = "t"
charArray(56) = "u"
charArray(57) = "v"
charArray(58) = "w"
charArray(59) = "x"
charArray(60) = "y"
charArray(61) = "z"

baseA = 28 'H1C
baseB = 24 'H18

Dim s1 As String
Dim s2 As String

s1 = "MTV"
s2 = "TMF"

Dim i As Long, j As Long
Dim f As Double

For i = 0 To 9 Step 1
    charArray2(i) = i * i * i + 23
Next i

For i = 10 To 35 Step 1
    baseA = baseA * 2
    f = (baseA Xor Asc(s1)) + 5
    charArray2(i) = f
    baseA = baseA + 1
Next i

For i = 36 To 61 Step 1
    baseB = baseB * 2
    f = (baseB Xor Asc(s2)) + 62
    charArray2(i) = f
    baseB = baseB + 1
Next i

Dim sn As String
sn = txtCode.Text

Dim snArray(22) As String

Dim snlen As Integer
Dim snlen2 As Integer

snlen = Len(sn)
snlen2 = snlen + 1

For j = 1 To (snlen2-1) Step 1
snArray(j) = Mid(sn, j, 1)
Next j

Dim s3 As String

s3 = CStr(0)

Dim f3 As Double
f3 = 0

For i = 1 To (snlen2-1) Step 1
    For j = 0 To 61 Step 1
      'If snArray(i) = charArray(j) Then   'using array
      If snArray(i) = Mid(charString, j + 1, 1) Then 'using string
         f3 = Val(s3) + charArray2(j)
         s3 = CStr(f3 * i)
      End If
    Next j
Next i

'MsgBox (CStr(s3))

Dim thankyou As String
Dim a1 As Integer, a2 As Integer

If Val(s3) = 616388714737576# Then
    thankyou = "Thank you for registering"

    a1 = Val(Mid(s3, 8, 1) + Mid(s3, 1, 1)) '16
    a2 = Val(Mid(s3, 13, 1) + Mid(s3, 5, 1)) '58

End If

Call sub_41F430(thankyou, a1, a2)

End Sub
算法出来了,通过分析,主要就是查表求和并乘以相应的索引值,也没好办法反向计算,主要查表的索引是通过人为输入的,只有爆破计算unlock code 了,爆破代码如下,大概5秒就可以找到一个unlock code了。
代码是C++的,通过Dev-C++调试通过:
#include <iostream>
#include <string.h>
#include <time.h>

unsigned long long base[] = {
      23, 24, 31, 50, 87, 148, 239, 366, 535, 752,   //CrackMe中生成前10个数字,由(i * i * i + 23) 生成

      122,68, 176, 392, 984, 1912, 3640, 7352, 14776, 29624, 59320, 118712, 237496, 475064, 950200, 1900472,
      3801016, 7602104, 15204280, 30408632, 60817336, 121634744, 243269560, 486539192, 973078456, 1946156984,//// 中间26个数字

      162, 116, 208, 536, 904, 1704, 3176, 6376, 12776, 25576, 51176, 102376, 204776, 409576, 819176, 1638376,
      3276776, 6553576, 13107176, 26214376, 52428776, 104857576, 209715176, 419430376, 838860776, 1677721576    //// 最后26个数字
    };

char sn_str[] = "0123456789ABCDEDGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

//char sn_ok = {'-'};
char sn_ok[] = "----------------------";
int sn_bit = {0};

int testSN(unsigned long long sn, int bit);
int main(int argc, char** argv) {
      
      //unsigned long long sn = 4330972930LL;//616388714737576LL;
      unsigned long long sn_result = 616388714737576LL;
      
      sn_ok = '\0';

      int n=0;
      /// unlock code 最多20个字符,先取得与sn_result能整除几个索引值(也就是长度值)
      for(int i=20; i>1; i--) {
                if((sn_result % i) == 0) {
                        sn_bit = i;
                }
      }
      
      /*
      // n = 11,8,4,2, 最可能的是11,8位两种长度
      11: 56035337703416,
         8: 77048589342197,
         4: 154097178684394,
         2: 308194357368788,
         */
      for(int i=0; i<n; i++) {
                printf("n(%d) = %d\n", i, sn_bit);
      }
      
      time_t timep;
      time (&timep);
      printf("\nStart time: %s\n", ctime(&timep));

      if(n>2) {
                n = 2;/// 只测试 11位,8位unlockcode
      }
      for(int i=0; i<n; i++) {
                sn_ok] = '\0';
                printf("\ntest bits = %d\n", sn_bit);
                int b = testSN(sn_result, sn_bit);
                if(b == 1) {
                        printf("\nUnlock Code: %s\n", sn_ok);
                        break;
                }
      }
      time (&timep);
      printf("\nEnd time: %s", ctime(&timep));

      //system("pause");
      
      return 0;
}

int count = 0;

int testSN(unsigned long long sn, int bit) {
      sn= sn / bit;
      bit = bit - 1;
      for(int j=61; j>=0; j--) {
                unsigned long long sn1 = sn - base;
                if(sn1<0) {
                        continue;
                }
                if((bit==0)) {
                        if((sn1==0)) {
                              sn_ok = sn_str;
                              //printf("sn_ok = %s\n", &sn_ok);
                              return 1;
                        } else {
                              continue;
                        }
                }
                if((sn1 % bit) == 0) {
                        sn_ok = sn_str;
                        //printf("bit=%d, sn_part = %s\n", bit+1, sn_ok);
                        int b = testSN(sn1, bit);
                        if(b==1) {
                              //printf("\nSN_OK: %s\n", sn_ok);
                              return 1;
                        } else {
                              sn_ok = '-';
                        }
                }
      }
      
      return 0;
}

其运行结果如下:
n(0) = 11
n(1) = 8
n(2) = 4
n(3) = 2

Start time: Mon Apr 29 15:21:46 2019


test bits = 11

Unlock Code: LSgbo3t8zzz

End time: Mon Apr 29 15:21:51 2019

--------------------------------
Process exited after 4.65 seconds with return value 0
请按任意键继续. . .

输入生成的 unlock code: LSgbo3t8zzz
看到效果如下图:




完毕!!!!
附件是 noos.3 crackme 的VB源码。

solly 发表于 2019-4-29 15:59

本帖最后由 solly 于 2019-4-29 16:01 编辑

鬼手56 发表于 2019-4-29 15:37
请教一下 WKTVBDebugger不是只支持Windows 2000和NT吗?为什么你的W10可以用
不清楚,我直接可以用,就是要启动两次才可以(第一次闪退,第二次后就可以了)。我的是 Win10 x64 1809 版本。还就就是快捷键不可用,需要鼠标点,这个不方便,代码区也不能上下走,只能看它显示的代码。

solly 发表于 2019-4-29 16:51

鬼手56 发表于 2019-4-29 16:03
能否分享下你用的WKTVBDebugger 最近也在调P-Code,奈何一直找不到能用的WKTVBDebugger
是在 pediy.com下载的,可能是我把VB程序需要的相关库文件,包括 MSVBVM50.DLL,以及OCX和其它DLL 都放到应用程序目录,没有放到 windows 的 SysOnWOW64目录。

鬼手56 发表于 2019-4-29 15:37

请教一下 WKTVBDebugger不是只支持Windows 2000和NT吗?为什么你的W10可以用

鬼手56 发表于 2019-4-29 16:03

能否分享下你用的WKTVBDebugger 最近也在调P-Code,奈何一直找不到能用的WKTVBDebugger

无的世界零 发表于 2019-4-29 16:30

写的很详细,谢谢分享

学士天下 发表于 2019-4-29 16:59

学习学习,谢谢楼主的分享,谢谢啦!!!

FENGMUTIAN 发表于 2019-4-29 17:22

前来学习

独狼 发表于 2019-4-29 20:41

哈哈哈哈哈

solly 发表于 2019-4-29 20:58

独狼 发表于 2019-4-29 20:41
哈哈哈哈哈

什么情况???{:1_904:}
页: [1] 2
查看完整版本: 160 个CrackMe之 124 - noos.3 (VB5 p-code)算法分析和5秒爆破算码注册机