首页
社区
课程
招聘
[原创]看雪 2016 CTF 第二题 Solution
发表于: 2016-11-6 10:16 5088

[原创]看雪 2016 CTF 第二题 Solution

HHHso 活跃值
22
2016-11-6 10:16
5088
破解历程:
1.        第一阶段的攻击确定了字符串解密机制、并初步猜测内嵌了脚本模块,并幸运低由解密机制关联出加密机制(为后续由加密逆推使用解密奠定了基调,解密注册码所需)
2.        第二阶段确认了Lua修改脚本模块并很幸运逆推到开发者修改参照的Lua版本(这为后续准确推断函数名称及准确反编译出脚本奠定了基础),这个过程整了一个通宵,11月5日早上七点多才睡下,可惜八点多就醒了。之后参加了十一点同学的婚礼,回来已经顶不住昏睡过去了,晚上吃了点夜宵继续。
3.        这要重审第一阶段的注册过程确定注册机制,并在算法还原和直接零碎调用之间做破解抉择。得到了注册码。

(1.1)
一开始从错误提示信息着手,想法肯定是寻找成功提示的信息点在哪里。
由于是中文,考虑到IDA string的缺陷,所以打算手工找找中文存储形式。
.text:0040121A push    eax
.text:0040121B push    [esp+4+hWnd]    ; hWnd
.text:0040121F call    ds:SetWindow

(1.2)溯源SetWindow调用栈时发现文本设置用的是一套通行机制,其中一环(命名)函数 Hi_SetTips_sub_4011C0 提供字符串和时间
而其前名是对中文字符串的内容的解密过程,解密函数为 Hi_maybe_dectrypstr_sub_41B422,追溯了所有引用点。
并通过在运行的OD的直接修改EIP的方式对所有引用点关联的字符内容进行一次统一解密(原因是解密代码可以重用,且解密部分代码片段耦合度高,堆栈相对稳定)
.text:004014B0                 push    offset dword_42D864 ;
.text:004014B0                                         ; 正在注册, 请稍候...
.text:004014B5                 push    eax             ; void *
.text:004014B6                 mov     [ebp+lpString], eax
.text:004014B9                 call    _memcpy
.text:004014BE                 movzx   eax, ds:byte_42D21C
.text:004014C5                 add     esp, 10h
.text:004014C8                 mov     ecx, Hi_dword_439850_withDebug ;
.text:004014C8                                         ; its.0Chww would be set 0 if isdebugging
.text:004014CE                 mov     [ebp+var_10], esi
.text:004014D1                 push    eax
.text:004014D2                 lea     eax, [ebp+var_10]
.text:004014D5                 push    eax
.text:004014D6                 lea     eax, [ebp+lpString]
.text:004014D9                 push    eax
.text:004014DA                 call    Hi_maybe_dectrypstr_sub_41B422
.text:004014DF                 test    eax, eax
.text:004014E1                 pop     esi
.text:004014E2                 jz      short loc_4014F3
.text:004014E4                 push    320h            ; dwMilliseconds
.text:004014E9                 push    [ebp+lpString]  ; lpString
.text:004014EC                 call    Hi_SetTips_sub_4011C0

以下是直接修改OD EIP 执行片段解密出的大部分中文信息
0042D364  "Pediy CTF 2016 Crackme by SilentGamb"
0042D464 看雪CrackMe攻防大赛2016  **********************************两处引用
    看雪CrackMe攻防大赛2016
      输入注册码完成后, 按回车键进行注册
      正在注册, 请稍候...
      重试次数太多了,请重新运行程序
      注册失败..., 请重新输入
0042D564 Serial
0042D964 输入注册码完成后, 按回车键进行注册
0042D864 正在注册, 请稍候...  **********************************
0042D764 "注册失败..., 请重新输入"  
0042D664  重试次数太多了,请重新运行程序
0042DE64 "fnGetRegSnToVerify"
0042DF64 "fnCalcUserInputRegSnAfterEnc"
0042D204 ************************* cbSize:0x400
42E064 userRegister
42E164 getRegSnAfterCal

(1.3)在解密函数 Hi_maybe_dectrypstr_sub_41B422 中
基本过程是:
解密信息体初始化 Hi_decObjInit,
解密过程 Hi_decObjDecrypt,
解密信息移动 Hi_decObjCopyOut,
解密信息体析构 Hi_decObjDtor
而在 Hi_decObjInit 函数交叉引用分析中,确定了加密函数  0041B3B0 sub_41B3B0,其对 输入的注册码进行加密

.text:0041B422 Hi_maybe_dectrypstr_sub_41B422 proc near
.text:0041B422
.text:0041B422 var_AC= byte ptr -0ACh
.text:0041B422 var_C= dword ptr -0Ch
.text:0041B422 var_4= dword ptr -4
.text:0041B422 P1_rStr= dword ptr  8
.text:0041B422 P2_rSize= dword ptr  0Ch
.text:0041B422 P3_rTor= dword ptr  10h
.text:0041B422
.text:0041B422 mov     eax, offset loc_42C727
.text:0041B427 call    __EH_prolog
.text:0041B42C sub     esp, 0A0h
.text:0041B432 push    esi
.text:0041B433 push    edi
.text:0041B434 lea     ecx, [ebp+var_AC]
.text:0041B43A call    Hi_decObjInit
.text:0041B43F mov     edi, [ebp+P1_rStr]
.text:0041B442 mov     esi, [ebp+P2_rSize]
.text:0041B445 and     [ebp+var_4], 0
.text:0041B449 lea     ecx, [ebp+var_AC]
.text:0041B44F push    dword ptr [edi] ; P3_str
.text:0041B451 push    dword ptr [esi] ; P2_size
.text:0041B453 push    [ebp+P3_rTor]   ; P1_tor
.text:0041B456 call    Hi_decObjDecrypt
.text:0041B45B push    dword ptr [edi] ; void *
.text:0041B45D lea     ecx, [ebp+var_AC]
.text:0041B463 push    dword ptr [esi] ; int
.text:0041B465 call    Hi_decObjCopyOut
.text:0041B46A xor     ecx, ecx
.text:0041B46C cmp     eax, [esi]
.text:0041B46E setz    cl
.text:0041B471 or      [ebp+var_4], 0FFFFFFFFh
.text:0041B475 mov     esi, ecx
.text:0041B477 lea     ecx, [ebp+var_AC]
.text:0041B47D call    Hi_decObjDtor
.text:0041B482 mov     ecx, [ebp+var_C]
.text:0041B485 mov     eax, esi
.text:0041B487 pop     edi
.text:0041B488 pop     esi
.text:0041B489 mov     large fs:0, ecx
.text:0041B490 leave
.text:0041B491 retn    0Ch
.text:0041B491 Hi_maybe_dectrypstr_sub_41B42

(1.4) 从(1.3)确认的 加密函数 sub_41B3B0 溯源 到 Hi_encrypt_sub_41B494,其上下文是获取输入的注册码,长度必须为 0x10的倍数,
且由函数 Hi_CheckAZaz09_sub_4018FB 检测保证必须是字母和数字
.text:0040184B                 call    ds:GetWindowTextA
.text:00401851                 push    dword ptr [esi] ; char *
.text:00401853                 call    _strlen
.text:00401858                 test    eax, eax
.text:0040185A                 pop     ecx
.text:0040185B                 jz      short loc_401884
.text:0040185D                 mov     ecx, eax
.text:0040185F                 and     ecx, 0Fh
.text:00401862                 test    cl, cl
.text:00401864                 jnz     short loc_401884
.text:00401866                 push    eax
.text:00401867                 push    dword ptr [esi]
.text:00401869                 call    Hi_CheckAZaz09_sub_4018FB
.text:0040186E                 pop     ecx
.text:0040186F                 test    eax, eax
.text:00401871                 pop     ecx
.text:00401872                 jz      short loc_401884
.text:00401874                 mov     ecx, Hi_dword_439850_withDebug ;
.text:00401874                                         ; its.0Chww would be set 0 if isdebugging
.text:0040187A                 push    edi
.text:0040187B                 push    esi
.text:0040187C                 call    Hi_encrypt_sub_41B494

(1.5)紧接(1.4)继续溯源,基本验证过程就在 消息响应函数 sub_401482 中,
其先通过 00401498 call    Hi_getEncSerail_sub_4017EA 获取加密后的序列号
提示正在注册 004014EC call    Hi_SetTips_sub_4011C0 ”正在注册, 请稍候..."
然后进入主要注册检验环节
.text:00401508 lea     eax, [ebp+P2_rEncSerialSize]
.text:0040150B push    eax             ; P2_rEncSerialSize
.text:0040150C lea     eax, [ebp+P1_rEncSerail]
.text:0040150F push    eax             ; P1_rEncSerial
.text:00401510 call    sub_41AEF1

(2.1)
在(1.5)进入主要注册校验环节 sub_41AEF1 后,解密出改版的Lua脚本并执行,
一开始并为甄别确认 041AF84 call    luaL_loadbuffer 函数,蛮力分析后发现其自成一套读取使用解密出的字节内容的系统(后来确认为Lua的ZIO)
.text:0041AF5F movzx   eax, byte ptr ds:dword_42D204
.text:0041AF66 add     esp, 0Ch
.text:0041AF69 mov     ecx, esi
.text:0041AF6B push    eax
.text:0041AF6C lea     eax, [ebp+var_8]
.text:0041AF6F push    eax
.text:0041AF70 lea     eax, [ebp+lpMem]
.text:0041AF73 push    eax
.text:0041AF74 call    Hi_maybe_dectrypstr_sub_41B422
.text:0041AF79 push    ebx
.text:0041AF7A push    ebx
.text:0041AF7B push    [ebp+var_8]
.text:0041AF7E push    [ebp+lpMem]
.text:0041AF81 push    dword ptr [esi+10h]
.text:0041AF84 call    luaL_loadbuffer

(2.2)Lua的确认过程
这个过程开了挂,毕竟第一阶段的猜测只猜到了开始,对部分行数都使用了Hi_script_run_xxx的命名,也留意到了以下字符信息,
一开始百度了部分关键字符没有得到很直观的开发代码关联(一开始也没上谷歌,这个还是靠谱些,以前找开源代码特征每每都是它帮上大忙)
不小心瞄了一眼群上有些聪明的大侠纷纷给出了Lua的特征;恰好电脑上有自己之前搞分析
的Lua源码,也就上vs2010和UltraEdit遍历搜索了一下,很不幸命中了。
.data:00435204 aTable          db 'table',0            ; DATA XREF: .rdata:0042E5B8
.data:00435204                                         ; .rdata:0042ED04
.data:0043520A                 align 4
.data:0043520C aCoroutine      db 'coroutine',0        ; DATA XREF: .rdata:0042E5B0
.data:00435216                 align 4
.data:00435218 aPackage        db 'package',0          ; DATA XREF: .rdata:0042E5A8
.data:00435220 aSetvbuf        db 'setvbuf',0          ; DATA XREF: .rdata:0042E6B8
.data:00435228 aSeek           db 'seek',0   

(2.3)对着手头上的Lua源码进行函数的甄别,从lua.c的 main 到 pmain ,关键的 Lua_status 需要初始化,
在第一阶段猜定为自成一套的buf读写机制时,和其他的笼统分析中,都注意到了[xxx_reg + 10] 的输入参数,xxx_reg是一个固定信息体或对象的thisPtr指针;
这个信息体的最初由前面跟踪对注册码输入的合法性检验函数 004018FB Hi_CheckAZaz09_sub_4018FB  中引出 ,在这个函数中
00401934 call    Hi_BinToHexChars{
    ...
    00401985 call    Hi_BinToHexChar{
        ...
        .text:004018D6                 call    ds:IsDebuggerPresent
        .text:004018DC                 test    eax, eax
        .text:004018DE                 jz      short loc_4018F3
        .text:004018E0                 mov     eax, Hi_dword_439850_withDebug ;
        .text:004018E0                                         ; its.0Chww would be set 0 if isdebugging
        .text:004018E5                 mov     Hi_set1_by_IsDebuggerPresent, 1
        .text:004018EF                 and     dword ptr [eax+0Ch], 0
        ...
    }
    ...
}
的调用对二进制转成十六进制字符串信息,上面有调试检测,不过后续怎么影响,直接在OD加载后跳转到 IsDebuggerPresent
修改为(原谅,一般都只用原版OD,一般只带od script插件,不会自动帮我过滤反调试)
7527CA70 > 64:A1 30000000   MOV EAX,DWORD PTR FS:[30]
7527CA76   33C0             XOR EAX,EAX
7527CA78   90               NOP
7527CA79   90               NOP
7527CA7A   C3               RETN

(2.4)跑题了!无论从程序入口start顺推还是从消息处理逆推,在 00401000 _WinMain@16 中的 00401013 call    sub_41AD8F 有对 该信息体进行初始化。
当然 [xxx_reg + 10] 也初步裁定为 lua_State
所以在 sub_41AD8F 中,猜定了 luaL_newstate 和 luaL_openlibs,并由 luaL_openlibs 的甄别得到了猜定的相互印证(作者修改lua所参考的版本的确定由luaL_openlibs关联是信息确定)

.text:0041ADC3                 call    luaL_newstate   ;
.text:0041ADC3                                         ; #define lua_open()      luaL_newstate()
.text:0041ADC8                 cmp     eax, ebx
.text:0041ADCA                 mov     [esi+10h], eax
.text:0041ADCD                 jz      loc_41AEA0
.text:0041ADD3                 push    edi
.text:0041ADD4                 push    eax
.text:0041ADD5                 call    luaL_openlibs

在0041ADD5 call    luaL_openlibs的入口引用了 0042E5A0 loadedlibs 信息表,
而我电脑的现有版本是Lua 5.1, 没有 "_G",也没有"utf8"
所以直接百度(还是谷歌?)忘了,关键字是 "lua  utf8",utf8为最新lua版本所支持,所以下在了Lua 5.33, 比对 Lua 5.33的 loadedlibs,果然匹配上了。
于是后期全部基于Lua 5.33做函数甄别。
.text:0040C06F luaL_openlibs   proc near               ; CODE XREF: sub_41AD8F+46
.text:0040C06F
.text:0040C06F P1_lua_State    = dword ptr  4
.text:0040C06F
.text:0040C06F                 mov     eax, ds:off_42E5A4
.text:0040C074                 push    esi
.text:0040C075                 mov     esi, offset loadedlibs

关键的关联信息表
.rdata:0042E5A0 loadedlibs      dd offset a_g           ; DATA XREF: luaL_openlibs+6
.rdata:0042E5A0                                         ; "_G"
.rdata:0042E5A4 off_42E5A4      dd offset luaopen_base  ; DATA XREF: luaL_openlibs
.rdata:0042E5A8                 dd offset aPackage      ; "package"
.rdata:0042E5AC                 dd offset luaopen_package
.rdata:0042E5B0                 dd offset aCoroutine    ; "coroutine"
.rdata:0042E5B4                 dd offset sub_408209
.rdata:0042E5B8                 dd offset aTable        ; "table"
.rdata:0042E5BC                 dd offset luaopen_table
.rdata:0042E5C0                 dd offset aIo           ; "io"
.rdata:0042E5C4                 dd offset luaopen_io
.rdata:0042E5C8                 dd offset aOs           ; "os"
.rdata:0042E5CC                 dd offset luaopen_os
.rdata:0042E5D0                 dd offset aString       ; "string"
.rdata:0042E5D4                 dd offset luaopen_string
.rdata:0042E5D8                 dd offset aMath         ; "math"
.rdata:0042E5DC                 dd offset luaopen_math
.rdata:0042E5E0                 dd offset aUtf8         ; "utf8"
.rdata:0042E5E4                 dd offset luaopen_utf8
.rdata:0042E5E8                 dd offset aDebug        ; "debug"
.rdata:0042E5EC                 dd offset luaopen_debug
.rdata:0042E5F0                 dd 0
.rdata:0042E5F4                 dd 0

(2.5)Lua 5.33 函数的甄别进行了很长时间,还是很有趣的。
在 sub_41AD8F 中,除了 "0041ADC3 call    luaL_newstate","0041ADD5 call    luaL_openlibs",
最关键是对注册关联了两个Lua的C函数,实际破解工作基于
fnGetRegSnToVerify = 4019A2 Hi_fnGetRegSnToVerify
fnCalcUserInputRegSnAfterEnc = 4019C7 Hi_fnCalcUserInputRegSnAfterEnc
两个函数即可,不过还是完成的反编译了作者加密的"ls"版本的Lua脚本。

.text:0041AE16 push    offset Hi_fnGetRegSnToVerify
.text:0041AE1B push    dword ptr [esi+10h]
.text:0041AE1E call    lua_pushcclosure
.text:0041AE23 push    [ebp+lpMem]
.text:0041AE26 push    dword ptr [esi+10h]
.text:0041AE29 call    lua_setglobal

.text:0041AE76 push    ebx
.text:0041AE77 push    offset Hi_fnCalcUserInputRegSnAfterEnc
.text:0041AE7C push    dword ptr [esi+10h]
.text:0041AE7F call    lua_pushcclosure
.text:0041AE84 push    [ebp+lpMem]
.text:0041AE87 push    dword ptr [esi+10h]
.text:0041AE8A call    lua_setglo

(2.6)Lua 脚本反编译
.text:0041AF51 push    [ebp+var_8]     ; size_t
.text:0041AF54 push    offset dword_42DA64
.text:0041AF59 push    eax             ; void *
.text:0041AF5A call    _memcpy
.text:0041AF5F movzx   eax, byte ptr ds:dword_42D204
.text:0041AF66 add     esp, 0Ch
.text:0041AF69 mov     ecx, esi
.text:0041AF6B push    eax
.text:0041AF6C lea     eax, [ebp+var_8]
.text:0041AF6F push    eax
.text:0041AF70 lea     eax, [ebp+lpMem]
.text:0041AF73 push    eax
.text:0041AF74 call    Hi_maybe_dectrypstr_sub_41B422
.text:0041AF79 push    ebx
.text:0041AF7A push    ebx
.text:0041AF7B push    [ebp+var_8]
.text:0041AF7E push    [ebp+lpMem]
.text:0041AF81 push    dword ptr [esi+10h]
.text:0041AF84 call    luaL_loadbuff

上述代码片段解密出 Lua脚本并加载,作者将 "Lua 5.33" 修改成了 "ls 1.1"
一开始找不到合适的Lua反编译工具,考虑到编译的0x400代码不多,最初做了
手工反编译(手工初步反编译的信息参考后续),看到手工的结果还是放弃了治疗。
短时间用python也写不出完备的反编译工具。幸运的是休息调整过后,找到了
https://github.com/viruscamp/luadec
最合适的是它支持5.33,下载它和着Lua 5.33的源码进行编译。得到了反编译工具。
当然,最合适的方式是也学作者把"Lua 5.33"改为 "ls 1.1"定制反编译工具。
不过基于Lua的机制分支,对比了dump出来d "ls 1.1"编译脚本ls_11_script.out,和Lua 5.33的 hello.out脚本,
选择了直接对 ls_11_script.out 文件头部修正为 "Lua 5.3",并进行反编译,结果还差强人意。反编译的Lua脚本和手工反编译的信息如下。
(还是工具好呀!)
(2.6.1)工具反编译结果,应该联合前面的
fnGetRegSnToVerify = 4019A2 Hi_fnGetRegSnToVerify
fnCalcUserInputRegSnAfterEnc = 4019C7 Hi_fnCalcUserInputRegSnAfterEnc
信息形成完整lua脚本
------- ------- ------- ------- ------- ------- -------
-- Decompiled using luadec 2.2 rev:  for Lua 5.3 from https://github.com/viruscamp/luadec
-- Command line: enc.out

-- params : ...
-- function num : 0 , upvalues : _ENV
g_strRegSn = " "
g_strRegSnToVerify = ""
userRegister = function(strRegSnIn)
  -- function num : 0_0 , upvalues : _ENV
  local iRc = -1
  g_strRegSn = strRegSnIn
  g_strRegSnToVerify = fnGetRegSnToVerify()
  g_strRegSn = fnCalcUserInputRegSnAfterEnc(g_strRegSn)
  if g_strRegSn == g_strRegSnToVerify then
    iRc = 1024
  end
  g_strRegSnToVerify = ""
  return iRc
end

getRegSnAfterCalc = function(strRegSnIn)
  -- function num : 0_1 , upvalues : _ENV
  return g_strRegSn
end
------- ------- ------- ------- ------- ------- -------
(2.6.2)手工反编译结果,code指令的操作码可以通过下属python脚本提取,后面尝试提取A,B,C参数时涉及到的一大堆的属性,
考虑到时间问题,放弃了治疗(也大致猜到了代码的逻辑)
#define SIZE_C                9
#define SIZE_B                9
#define SIZE_Bx                18 (SIZE_C + SIZE_B)
#define SIZE_A                8
#define SIZE_Ax                26 (SIZE_C + SIZE_B + SIZE_A)

#define SIZE_OP                6

#define POS_OP                0
#define POS_A                6 (POS_OP + SIZE_OP)
#define POS_C                14 (POS_A + SIZE_A)
#define POS_B                23 (POS_C + SIZE_C)
#define POS_Bx                14 POS_C
#define POS_Ax                6 POS_A

ii = 0x80404008
def g(p,s,ebx = ii):
  #p=6;s=8;
  if s == 8:
    return c_byte((ebx>>p)&(pow(2,s+1)-1) & 0xFF).value
  else:
    return (ebx>>p)&(pow(2,s+1)-1)
   
指令操作码 = g(0,6,指令码)
------- ------- ------- ------- ------- ------- -------
@TaskBegin.ls(P1,P2,...){
    v1,v2,v3,v4,v5,v6,v7;
    code(7):
      08 40 40 80  /*        A B C        UpValue[A][RK(B)] := RK(C)                        */
      08 C0 40 81  /*        A B C        UpValue[A][RK(B)] := RK(C)                        */
      2C 00 00 00
      08 00 00 82
      2C 40 00 00
      08 00 80 82
      26 00 80 00
      08,40,40,80
    const(6):
      L1 = "g_strRegSn"
      L2 = " "
      L3 = "g_strRegSnToVerify"
      L4 = ""
      L5 = "userRegister"
      L6 = "getRegSnAfterCalc"
    Upvalues(1):
      (1,0)
    Protos(2):
      ""[ln.5,ln.10](P1){
          V1~V4
          code(0x11):
            41 00 00 00
            08 00 80 80
            86 C0 40 00
            A4 80 80 00  
            08 80 00 81
            86 00 41 00
            C6 40 40 00
            A4 80 00 01  
            08 80 80 80
            86 40 40 00
            C6 80 40 00
            1F C0 00 01  
            1E 00 00 80
            41 40 01 00
            08 80 41 81
            66 00 00 01  
            26 00 80 00
         const(7):
              L1 = Integer(-1)   
              L2 = "g_strRegSn"
              L3 = "g_strRegSnToVerify"
              L4 = "fnGetRegSnToVerify"
              L5 = "fnCalcUserInputRegSnAfterEnc"
              L6 = Integer(0x400)
              L7 = ""
         Upvalues(1):
           (0,0)
         Proto(0):
         debug:
           lineinfo.size = 0x11 * u4LineNo
           n = 0x02 *:
             {varname."strRegSnIn",sPC.0,ePC.0x11}
             {varname."iRC",sPC.0x01,ePC.0x11}
           upvalues.names cnt = 0x01:
             {"_ENV"}
      }
      ""[ln.0x13,ln.0x15](P1){
          V1,V2;
          code(3):
            46 00 40 00
            66 00 00 01
            26 00 80 00
          const(1):
            L1 = "g_strRegSn"
           Upvalues(1):
             (0,0)
           Proto(0):
           debug:
           lineinfo.size = 0x03 * u4LineNo>> 0x14,0x14,0x15
           n = 0x01 *:
             {varname."strRegSnIn",sPC.0,ePC.0x3}
           upvalues.names cnt = 0x01:
             {"_ENV"}
      }
    debug:
      line = 0x07 * >> 01 02 10 05 15 13 15
      n = 0: nothing
      name cnt = 10
      {_ENV}
}
------- ------- ------- ------- ------- ------- -------

(3.1)注册码破解
核心注册码计算函数为 041B22D call    sub_402B8C,
核心算法步骤是,对加密后的输入注册码进行两次异或xor操作,根据可逆性,直接提取异或矩阵酒完事了。

fnGetRegSnToVerify = 4019A2 Hi_fnGetRegSnToVerify
fnCalcUserInputRegSnAfterEnc = 4019C7 Hi_fnCalcUserInputRegSnAfterEnc

(3.1.1)测试输入 1234567890ABCDEF1234567890ABCDEF
(3.1.2)由前面解密字符串函数对应的解密函数(参考前面分析)加密得到
testEncryptedSerial_in
00CA84B8  0B 97 00 33 BE 94 1B 5B 16 2B 16 7F DD B8 14 76  
00CA84C8  0B 97 00 33 BE 94 1B 5B 16 2B 16 7F DD B8 14 76  
(3.1.3)异或矩阵1
xor0 = [0xF5, 0x91, 0x23, 0x5E, 0x8D, 0xB0, 0x87, 0xE2, 0xAE, 0xEE, 0xDE, 0x93, 0x88, 0xF2, 0xAC, 0xA3,
  0x4F, 0x9F, 0xB7, 0x61, 0x10, 0x23, 0xFB, 0x30, 0x19, 0x69, 0xB8, 0xAD, 0xCE, 0x52, 0x00, 0x6C]
(3.1.4)异或矩阵2
xor1 = [0x1A, 0xAB, 0xD4, 0x70, 0xAE, 0x1A, 0x31, 0xD7, 0x4E, 0x7F, 0x02, 0x27, 0xDA, 0x3A, 0xD3, 0xC0,
  0xC7, 0xBF, 0xA0, 0xE2, 0xD7, 0x92, 0xF0, 0xE5, 0xF8, 0x64, 0xD3, 0x04, 0x96, 0xAD, 0x17, 0x41]
(3.1.5)计算的注册码结果
testEncryptedSerial_out = [0xE4, 0xAD, 0xF7, 0x1D, 0x9D, 0x3E, 0xAD, 0x6E, 0xF6, 0xBA, 0xCA, 0xCB, 0x8F, 0x70, 0x6B, 0x15,
  0x83, 0xB7, 0x17, 0xB0, 0x79, 0x25, 0x10, 0x8E, 0xF7, 0x26, 0x7D, 0xD6, 0x85, 0x47, 0x03, 0x5B]
  
逆算注册码过程,4019A2 Hi_fnGetRegSnToVerify 函数得到校验的注册码
g_strRegSnToVerify=[0xA4, 0x47, 0x98, 0x0C, 0x9E, 0x40, 0xD7, 0xF6, 0xEB, 0x76, 0x6E, 0x6D, 0x7E, 0xA3, 0x3E, 0xEB,
  0xD5, 0x51, 0x30, 0x06, 0x7D, 0xC0, 0xFB, 0x6C, 0xC2, 0x7A, 0x43, 0xC5, 0xA4, 0xC9, 0xB1, 0xFD]
通过二次异或得到加密的注册码,简单换算pyth脚本如下
def dexor(inm):
  for i in xrange(0,0x10):
    #print "0x{:02X}, ".format((inm[i] ^ xor0[i] ^ xor1[i])&0xFF),
    print "{:02X} ".format((inm[i] ^ xor0[i] ^ xor1[i])&0xFF),
  print
  for i in xrange(0x10,0x20):
    #print "0x{:02X}, ".format((inm[i] ^ xor0[i] ^ xor1[i])&0xFF),
    print "{:02X} ".format((inm[i] ^ xor0[i] ^ xor1[i])&0xFF),
  print
  
4B  7D  6F  22  BD  EA  61  C3  0B  E7  B2  D9  2C  6B  41  88
5D  71  27  85  BA  71  F0  B9  23  77  28  6C  FC  36  A6  D0

tgtEndSerail=[0x4B,  0x7D,  0x6F,  0x22,  0xBD,  0xEA,  0x61,  0xC3,  0x0B,  0xE7,  0xB2,  0xD9,  0x2C,  0x6B,  0x41,  0x88,
  0x5D,  0x71,  0x27,  0x85,  0xBA,  0x71,  0xF0,  0xB9,  0x23,  0x77,  0x28,  0x6C,  0xFC,  0x36,  0xA6,  0xD0,]

最后解密得到(还记得前面解密中文字符串的过程么,对了,也是直接修改OD的EIP,填充参数的输入内容,之后揭秘得到要输入的注册码)
022C8848  73 74 4B 35 43 4B 70 42 73 77 37 54 50 46 34 35  stK5CKpBsw7TPF45

一下是两个异或算子提取位置,和解密使用的代码片段(修改最少,解密都关联了一个一字节的因子,对注册码的是0x18,对其他中文字符串是其他)
(3.1.a)异或矩阵1 Hi_xor1_unk_42D224 ,静态,直接提取
.text:00402BF7                 xor     edx, edx
.text:00402BF9                 cmp     [esi+474h], edx
.text:00402BFF                 jnz     short loc_402C1D
.text:00402C01                 cmp     ecx, edx
.text:00402C03                 jle     short loc_402C1D
.text:00402C05                 mov     edi, offset Hi_xor1_unk_42D224
.text:00402C0A                 mov     eax, ebx
.text:00402C0C                 sub     edi, ebx
.text:00402C0E
.text:00402C0E loc_402C0E:                             ; CODE XREF: sub_402B8C+8F
.text:00402C0E                 mov     cl, [edi+eax]
.text:00402C11                 xor     [eax], cl
.text:00402C13                 inc     edx
.text:00402C14                 inc     eax
.text:00402C15                 cmp     edx, [esi+3CCh]
.text:00402C1B                 jl      short loc_402C0E
(3.1.b)异或矩阵1  edi 指示的内存区
.text:00402C1D lea     edi, [esi+3F4h]
.text:00402C23 push    ebx
.text:00402C24 push    edi
.text:00402C25 mov     ecx, esi
.text:00402C27 call    sub_402D7C
.text:00402C2C push    [ebp+P1_inBuf]  ; P2_outbuf
.text:00402C2F mov     ecx, esi
.text:00402C31 push    edi             ; P1_3f4hbuf
.text:00402C32 call    sub_40264E
(3.1.c)解密代码调用片段, dword_42D204 为输入的算法因子,两处引用,除了这里,就是前面的"0041B494 Hi_encrypt_sub_41B494"函数里
这也是Lua脚本的解密过程。直接修改OD EIP = 0041AF37,运行过程中跳过 memecpy关联的指令,
在调用进入0041AF74之前,间参数1和2分别修改为要解密的内容和大小,直接F8之后得到解密内容
text:0041AF37                 mov     eax, 400h
.text:0041AF3C                 push    eax             ; unsigned int
.text:0041AF3D                 mov     [ebp+var_8], eax
.text:0041AF40                 call    ??2@YAPAXI@Z    ; operator new(uint)
.text:0041AF45                 cmp     eax, ebx
.text:0041AF47                 pop     ecx
.text:0041AF48                 mov     [ebp+lpMem], eax
.text:0041AF4B                 jz      loc_41B12E
//.text:0041AF51                 push    [ebp+var_8]     ; size_t
//.text:0041AF54                 push    offset dword_42DA64
//.text:0041AF59                 push    eax             ; void *
//.text:0041AF5A                 call    _memcpy
.text:0041AF5F                 movzx   eax, byte ptr ds:dword_42D204
//.text:0041AF66                 add     esp, 0Ch
.text:0041AF69                 mov     ecx, esi
.text:0041AF6B                 push    eax
.text:0041AF6C                 lea     eax, [ebp+var_8]
.text:0041AF6F                 push    eax
.text:0041AF70                 lea     eax, [ebp+lpMem]
.text:0041AF73                 push    eax
.text:0041AF74                 call    Hi_maybe_dectrypstr_sub_41B422

附件为 Lua 5.33 及 反编译工具,和 和解密出来的修改前后的 脚本反编译
luadec.exe  ls_11_script.out
得到前面的lua脚本反编译结果

LuaDec533with_ls_11_script.7z

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 1355
活跃值: (339)
能力值: ( LV13,RANK:920 )
在线值:
发帖
回帖
粉丝
2
sub_41B422 为 RC6 加密主程序。
2016-11-6 12:08
1
游客
登录 | 注册 方可回帖
返回
//