首页
社区
课程
招聘
[原创]看雪 2016 CTF 第十七题 大隐于市
发表于: 2016-12-6 11:41 4606

[原创]看雪 2016 CTF 第十七题 大隐于市

HHHso 活跃值
22
2016-12-6 11:41
4606

作者用的反调试虽然不多,寥寥三种,
但分散淹没在静态编译的主(3.16M)从(1.76M)两个模块中,足以抵近中级强度反调试效果。
其中一种反调试比较有意思,即:程序一头不时制造异常,另一头负责接管校正异常。
作者将核心校验算法函数体随机加密扩散到一个100~168份集合中,从主模块到从模块进行大挪移。
校验时随机抽取一份使用。且将唯一的扩散函数淹没在666个从模块导出函数中,大隐隐于市。
xp直跑,起不来,win7可以跑起来,原因未深究,可能是反调试机制引起,去掉反调试机制后xp通畅无阻。

分析过程
第一阶段:IDA静态分析理清反调试机制,借助OD修正去掉反调试。
第二阶段:OD定位关键业务逻辑,借助IDA+Windbg小跑IDAPython得到注册码。
第三阶段:溯源隐藏机制。

1.反调试及其清除。

1.1 通过检测父进程的反调试机制("Progman")
个人xp平台直接双击跑不起来,不科学!用OD加载,跑到一半还是跑不起来,这时候估计有反调试或什么其他bug。
win7直跑飞起。没辙,直接上IDA静态分析,直觉GetWindowTextW导入函数的交叉引用!太多,没有效率。
也尝试下IsDebuggerPresent的引用,没发现有效信息。必须承认,下面是运气时间,
我也想找个合理的步骤解释怎么得到的,但还真没法还原这个反调试突破口发现的过程。
可能是在寻找succee一类的信息,或者是溯源交叉引用的过程中,不小心看到了"Progman"字符串。
.text:004027B8 push    offset ClassName ; "Progman"
.text:004027BD call    ds:FindWindow
遇上"Progman"是偶然,但能不能抓住,这个就不是偶然事件了。
CTF第十五题的各种反调试还历历在目,参考十五题分析的3.3,意思是:
这种反调试检测父进程甄别程序启动方式,双击启动的程序父进程为explorer.exe,
如果是调试器启动的,其父进程就不是explorer.exe,以此来弱检测。

1.2 主模块镜像名称反修改检测。
在1.1上下文,主要到StrStrW,这是检测防止修改镜像名称的机制,理论上不算反调试机制。
不过对我这种一Download解压出镜像文件后立马修改为CTFXXX.exe的人来时,不幸中枪。

.text:0040284E push    104h            ; nSize
.text:00402853 lea     eax, [ebp+Filename]
.text:00402859 push    eax             ; lpFilename
.text:0040285A push    0               ; hModule
.text:0040285C call    ds:GetModuleFileNameW
.text:00402862 push    offset Srch     ; "Susuger_CrackMe.exe"
.text:00402867 lea     eax, [ebp+Filename]
.text:0040286D push    eax             ; lpFirst
.text:0040286E call    ds:StrStrW

1.3 主动发起并接管异常反调试机制
其位于1.2的上方,不得不说这是被StrStrW连累暴露的。

.text:0040282E push    offset Hi_TopLevelExceptionFilter_sub_402620
.text:00402833 call    ds:SetUnhandledExceptionFilter
.text:00402839 push    0               ; lpThreadId
.text:0040283B push    0               ; dwCreationFlags
.text:0040283D push    0               ; lpParameter
.text:0040283F push    offset Hi_raise_callException_sub_402590
.text:00402844 push    0               ; dwStackSize
.text:00402846 push    0               ; lpThreadAttributes
.text:00402848 call    ds:CreateThread

Hi_raise_callException_sub_402590 线程函数负责每5000ms发起调用异常。
而异常最终会被注册的异常过滤函数Hi_TopLevelExceptionFilter_sub_402620 处理修正。

(1.3.1)Hi_raise_callException_sub_402590 伪码如下,
其备份GetCurrentProcess函数的首字,并设置为非法操作码0xCCCC,
线程循环没间隔5000ms调用GetCurrentProcess时就会产生异常。
Hi_raise_callException_sub_402590{
  flOldProtect = 0;
  VirtualProtect(GetCurrentProcess, 2u, PAGE_EXECUTE_READWRITE, &flOldProtect);
  memmove(&Hi_GetCurrentProcess_BackUpValidCode_word_5B4DF, GetCurrentProcess, 2u);
  VirtualProtect(GetCurrentProcess, 2u, flOldProtect, 0);
  while ( 1 )
  {
    InValidCode = 0xCCCC;
    VirtualProtect(GetCurrentProcess, 2u, PAGE_EXECUTE_READWRITE, &flOldProtect);
    memmove(GetCurrentProcess, &InValidCode, 2u);
    VirtualProtect(GetCurrentProcess, 2u, flOldProtect, 0);
    GetCurrentProcess();
    Sleep(5000u);
  }
}
(1.3.2)Hi_TopLevelExceptionFilter_sub_402620 函数实现比较简单,
只需将备份的操作码 Hi_GetCurrentProcess_BackUpValidCode_word_5B4DF 回填即可
异常处理完后,EIP或继续GetCurrentProcess的正确调用。若调试器接管该异常处理,
由于不能针对性修正,就会出错。
Hi_TopLevelExceptionFilter_sub_402620(struct _EXCEPTION_POINTERS *ExceptionInfo){
  memmove(GetCurrentProcess, &Hi_GetCurrentProcess_BackUpValidCode_word_5B4DF, 2u);
  return -1;
}

1.4 清除反调试
在Susuger_CrackMe.exe和 CrackMeLibrary.dll 模块中,发现有阻碍的反调试就上述三种。
主要是通过查找StrStrW函数引用,"Progman"字符串引用,SetUnhandledExceptionFilter函数引用
来定位出所有反调试代码逻辑。这里我们用尽量少的修改去去掉反调试,直接跳过的方式。
1.4.1 Susuger_CrackMe.exe 反调试修改
其中,a位于Hi_anti1_Progman_Exception_AppName_sub_402640函数中,属于CSusuger_CrackMeDlg的成员函数。
初始化时由00410D00 call dword ptr [edx+174h]发起调用。
b位于 Hi_anti2_AppName_WM_PAINT_sub_4029D0 ,是CSusuger_CrackMeDlg的WM_PAINT消息响应函数。
c和d位于 Hi_Hi_anti3_AppName_Progman_sub_402B20,是CSusuger_CrackMeDlg的WM_COMMAND消息响应函数

a. 修改指令"004027D9 jz loc_40282E"为"jmp 4028BD"
.text:004027CE call    ds:CreateToolhelp32Snapshot
.text:004027D4 mov     edi, eax
.text:004027D6 cmp     edi, 0FFFFFFFFh
.text:004027D9 jz      short loc_40282E //这里直接通过OD修改为 jmp 4028BD
.text:004027D9                          //即可去掉三种检测
//此处省略"Progman"的反调试检测代码
.text:0040282E push    offset Hi_TopLevelExceptionFilter_sub_402620
.text:00402833 call    ds:SetUnhandledExceptionFilter
.text:00402839 push    0               ; lpThreadId
.text:0040283B push    0               ; dwCreationFlags
.text:0040283D push    0               ; lpParameter
.text:0040283F push    offset Hi_raise_callException_sub_402590
.text:00402844 push    0               ; dwStackSize
.text:00402846 push    0               ; lpThreadAttributes
.text:00402848 call    ds:CreateThread
.text:0040284E push    104h            ; nSize
.text:00402853 lea     eax, [ebp+Filename]
.text:00402859 push    eax             ; lpFilename
.text:0040285A push    0               ; hModule
.text:0040285C call    ds:GetModuleFileNameW
.text:00402862 push    offset Srch     ; "Susuger_CrackMe.exe"
.text:00402867 lea     eax, [ebp+Filename]
.text:0040286D push    eax             ; lpFirst
.text:0040286E call    ds:StrStrW
.text:00402874 test    eax, eax
.text:00402876 jnz     short loc_4028BD
...
.text:004028BD loc_4028BD:
.text:004028BD lea     eax, [esi+

b. 修改指令"00402A13 jnz"为"jmp"
.text:00402A01 push    offset Srch     ; "Susuger_CrackMe.exe"
.text:00402A06 lea     eax, [esp+28Ch+Filename]
.text:00402A0A push    eax             ; lpFirst
.text:00402A0B call    ds:StrStrW
.text:00402A11 test    eax, eax
.text:00402A13 jnz     short loc_402A38 //jnz 直接修改为 jmp 即可跳过

c. 修改指令"00402B76 jnz"为"jmp"
.text:00402B62 push    offset Srch     ; "Susuger_CrackMe.exe"
.text:00402B67 lea     eax, [ebp+Filename]
.text:00402B6D push    eax             ; lpFirst
.text:00402B6E call    ds:StrStrW
.text:00402B74 test    eax, eax
.text:00402B76 jnz     short loc_402B9C //同b,jnz 直接修改为 jmp 即可跳过

d. 修改指令"00402C2F jz"为"jmp"
.text:00402C24 call    ds:CreateToolhelp32Snapshot
.text:00402C2A mov     edi, eax
.text:00402C2C cmp     edi, 0FFFFFFFFh
.text:00402C2F jz      short loc_402C8E

1.4.2 CrackMeLibrary.dll 反调试修改
其中,a和b位于Hi_dll_anti1_Exception_AppName_Progman_sub_100074F0,
其为CMyCrackMe的成员函数,模块初始化时调用。
c和d位于Hi_dll_anti2_Progman_AppName_CheckThread_sub_10007940,
其为发起注册码检验的线程函数,有下面e所属函数发起。
e位于Hi_dll_anti3_AppName_MsgReaponse_sub_10007CC0,
其为CMyCrackMe的WM_COMMAND消息响应函数。
f和g位于Hi_dll_anti4_Progman_AppName_sub_10001B20
其为CCrackMeLibApp成员函数。

a. 在10007526处修改为"jmp 1000759C"跳过异常反调和StrStrW对比
.text:10007526 push    offset Hi_TopLevelExceptionFilter_sub_100074D0
.text:1000752B call    ds:SetUnhandledExceptionFilter
.text:10007531 push    0               ; lpThreadId
.text:10007533 push    0               ; dwCreationFlags
.text:10007535 push    0               ; lpParameter
.text:10007537 push    offset Hi_raise_callException_sub_10007440
.text:1000753C push    0               ; dwStackSize
.text:1000753E push    0               ; lpThreadAttributes
.text:10007540 call    ds:CreateThread
.text:10007546 push    104h            ; nSize
.text:1000754B lea     eax, [ebp+Filename]
.text:10007551 push    eax             ; lpFilename
.text:10007552 push    0               ; hModule
.text:10007554 call    ds:GetModuleFileNameW
.text:1000755A push    offset Srch     ; "Susuger_CrackMe.exe"
.text:1000755F lea     eax, [ebp+Filename]
.text:10007565 push    eax             ; lpFirst
.text:10007566 call    ds:StrStrW
.text:1000756C test    eax, eax
.text:1000756E jnz     short loc_1000759C

b. "1000763A jz"改为"jmp"
.text:1000762F call    ds:CreateToolhelp32Snapshot
.text:10007635 mov     edi, eax
.text:10007637 cmp     edi, 0FFFFFFFFh
.text:1000763A jz      short loc_10007694

c. "10007A01 jz"改为"jmp"
.text:100079F6 call    ds:CreateToolhelp32Snapshot
.text:100079FC mov     edi, eax
.text:100079FE cmp     edi, 0FFFFFFFFh
.text:10007A01 jz      short loc_10007A5A

d. "10007A82 jnz"改为"jmp"
.text:10007A6E push    offset Srch     ; "Susuger_CrackMe.exe"
.text:10007A73 lea     eax, [ebp+Filename]
.text:10007A79 push    eax             ; lpFirst
.text:10007A7A call    ds:StrStrW
.text:10007A80 test    eax, eax
.text:10007A82 jnz     short loc_10007ACD

e. "10007CFE jnz"改为"jmp"
.text:10007CEA push    offset Srch     ; "Susuger_CrackMe.exe"
.text:10007CEF lea     eax, [ebp+Filename]
.text:10007CF5 push    eax             ; lpFirst
.text:10007CF6 call    ds:StrStrW
.text:10007CFC test    eax, eax
.text:10007CFE jnz     short loc_10007D2C

f. "10001BB0 jz"改为"jmp"
.text:10001BA5 call    ds:CreateToolhelp32Snapshot
.text:10001BAB mov     edi, eax
.text:10001BAD cmp     edi, 0FFFFFFFFh
.text:10001BB0 jz      short loc_10001C0B

g. "10001C35 jnz"改为"jmp"
g有f拖累暴露出来,用的是wcsicmp而不是StrStrW,
这里就需要我们进一步检查所有wcsicmp的引用,发现只有这一处作为检测。
.text:10001C25 push    offset Srch     ; "Susuger_CrackMe.exe"
.text:10001C2A push    eax             ; wchar_t *
.text:10001C2B call    __wcsicmp
.text:10001C30 add     esp, 8
.text:10001C33 test    eax, eax
.text:10001C35 jnz     short loc_10001C7D

2.跟踪定位关键逻辑代码逆向注册码
用OD通过上述1.x的指令修改保存后,即可进行任意调戏。
2.1 F2在 GetWindowTextW 断,由于其代码的MFC机制,通用F2断点会被下述调用干扰
0013E754   771A8566  /CALL to GetWindowTextW from COMCTL32.771A8560
0013E758   00C70A64  |hWnd = 00C70A64 ('Exit',class='Button',parent=00B90A46)
0013E75C   001D2A38  |Buffer = 001D2A38
0013E760   00000005  \Count = 5
2.2 这时候需要条件断点进行过滤
可通过OD->View->Window查找Class为"Edit"对应条目的句柄,也可以用Spy++直接捕捉。
在 GetWindowTextW 下条件断点:[esp+4]==015D0A6A(Edit句柄值),输入,点击"Submit"提交,断下

0013E794   015839FE  /CALL to GetWindowTextW from CrackMeL.015839F8
0013E798   015D0A6A  |hWnd = 015D0A6A (class='Edit',parent=00B90A46)
0013E79C   0013E9E4  |Buffer = 0013E9E4
0013E7A0   0000000C  \Count = C (12.)
...
0013E7C8  |01577D3D  RETURN to CrackMeL.01577D3D from CrackMeL.015839DA

2.3 溯源调用于消息响应函数 Hi_dll_anti3_AppName_MsgReaponse_sub_10007CC0 内,如下
其通过Hi_getKey_buf_cbSize_sub_100139DA获取用户输入最大字符长度为0x0B=0x0C-1的
StringKey,然后保存到全局静态变量Hi_wKeyStr_101AC480中,最后创建线程函数
Hi_dll_anti2_Progman_AppName_CheckThread_sub_10007940 发起注册校验。

.text:10007D2C push    0Ch
.text:10007D2E lea     eax, [ebp+StringKey]
.text:10007D31 push    eax          //获取输入key,可见其字符长度为0x0B=0x0C-1
.text:10007D32 lea     ecx, [esi+0BCh]
.text:10007D38 call    Hi_getKey_buf_cbSize_sub_100139DA
.text:10007D3D movdqu  xmm0, xmmword ptr [ebp+StringKey]
.text:10007D42 push    0               ; lpThreadId
.text:10007D44 push    0               ; dwCreationFlags
.text:10007D46 push    0               ; lpParameter
.text:10007D48 push    offset Hi_dll_anti2_Progman_AppName_CheckThread_sub_10007940
.text:10007D4D movdqu  xmmword ptr Hi_wKeyStr_101AC480, xmm0 //保存至模块全局变量
.text:10007D55 push    0               ; dwStackSize
.text:10007D57 movq    xmm0, qword ptr [ebp+StringKey+10h]
.text:10007D5C push    0               ; lpThreadAttributes
.text:10007D5E movq    qword ptr Hi_wKeyStr_101AC480+10h, xmm0
.text:10007D66 call    ds:CreateThread

2.4 Hi_dll_anti2_Progman_AppName_CheckThread_sub_10007940
其中Hi_vectorEncFunInfos_101AC588向量为加密函数信息体集合,集合元素个数100~168个,
函数信息体FunInfo元素大小为0x1C,结构如下,来源参考后续溯源分析。
FunInfo{ cbSize:0x1C
.00hww.FunAddr     加密函数地址
.04h.xorvector [04hb,.08hb,.0Chb,.10hb,.14hb] 随机累加异或加密因子
.18hww.xorStartCaseIndex  累加异或加密起始索引
}

FunInfo信息加解密基本原理是:
a.其随机产生xor因子数组 xorvector,
b.并随机产生 xorStartCaseIndex,
c.用xorvector[xorStartCaseIndex:] 对函数体累加异或加密,
FunAddr = Address of New Copy of TgtFunction
for i in xrange(0,Hi_0x80_EncFunCodeSize_101AC474,):
   for xortor in xorvector[xorStartCaseIndex]:
     FunAddr[i] = FunAddr[i] ^ xortor //累加异或

虽然是累加异或,但FunInfo集合中每一个FunInfo信息都足以完成自行解密和加密。
以下发起检验的基本过程是:
a。随机选择一份加密函数信息,对加密函数体解密得到核心校验函数。
b. 传递用户输入的Hi_wKeyStr_101AC480信息,调用解密出来的核心检验函数,并返回校验结果。
c。将解密出来的核心校验函数体加密回去。
d。 检测b的检验结果,如果成功,则调用Hi_show_Successful_tips_sub_10007210显示成功信息。

.text:10007ADA mov     ecx, Hi_vectorEncFunInfos_101AC588._Mylast
.text:10007AE0 mov     eax, 92492493h
.text:10007AE5 sub     ecx, Hi_vectorEncFunInfos_101AC588._Myfirst
.text:10007AEB imul    ecx
.text:10007AED add     edx, ecx
.text:10007AEF sar     edx, 4
.text:10007AF2 mov     edi, edx
.text:10007AF4 shr     edi, 1Fh
.text:10007AF7 add     edi, edx
.text:10007AF9 call    Hi_ptd_rand_sub_101241E4
.text:10007AFE xor     edx, edx
.text:10007B00 div     edi
.text:10007B02 mov     eax, Hi_vectorEncFunInfos_101AC588._Myfirst
.text:10007B07 push    0               ; lpflOldProtect
.text:10007B09 push    PAGE_EXECUTE    ; flNewProtect
.text:10007B0B push    Hi_0x80_EncFunCodeSize_101AC474, ; dwSize
.text:10007B11 lea     edi, ds:0[edx*8]  //随机数edx,即为随机选中的第edx个随机加密函数
.text:10007B18 sub     edi, edx
.text:10007B1A mov     ebx, [eax+edi*4]
.text:10007B1D push    ebx             ; lpAddress
.text:10007B1E call    ds:VirtualProtect
.text:10007B24 mov     edx, Hi_vectorEncFunInfos_101AC588._Myfirst
.text:10007B2A xor     ecx, ecx
.text:10007B2C cmp     Hi_0x80_EncFunCodeSize_101AC474,, ecx
.text:10007B32 jbe     short loc_10007B8F
//省略,将加密的函数体{FunAddr,Hi_0x80_EncFunCodeSize_101AC474,}解密,下面接着调用
.text:10007B8F mov     eax, [edx+edi*4]
.text:10007B92 push    offset Hi_wKeyStr_101AC480
.text:10007B97 call    eax  //对解密出来的核心校验函数调用
.text:10007B99 add     esp, 4
.text:10007B9C mov     [ebp+var_454_CheckStatus], eax //检验结果,0表示成功
//省略,将解密出来的函数体加密回去
.text:10007C24                 call    ds:VirtualProtect
.text:10007C2A                 cmp     [ebp+var_454_CheckStatus], 0
.text:10007C31                 jnz     short loc_10007C48
.text:10007C33                 push    0               ; lpThreadId
.text:10007C35                 push    0               ; dwCreationFlags
.text:10007C37                 push    0               ; lpParameter
.text:10007C39                 push    offset Hi_show_Successful_tips_sub_10007210 ;
.text:10007C39                 //若成功,则显示成功提示信息
.text:10007C3E                 push    0               ; dwStackSize
.text:10007C40                 push    0               ; lpThreadAttributes
.text:10007C42                 call    ds:CreateThread

2.5 跟进随机选择并解密出来的核心校验函数 Hi_enckey_and_cmpInnerEncKey_sub_19E0000
100~168份随机加密的核心校验函数都源自同一个函数 Hi_enckey_and_cmpInnerEncKey_sub_402450,参考后续溯源分析。

2.51. 这里用IDA(IDAPython)+Windbg追踪,比较看OD没有流程,
断点设在 .text:10007B97 call    eax  //对解密出来的核心校验函数调用
然后单步根进,快捷键"C"重建代码,并创建函数,这样就可以比较清晰看到流程图,
这里直接上根据的解密函数体,如下

debug191:019E0000 ; ---------------------------------------------------------------------------
debug191:019E0000 Hi_enckey_and_cmpInnerEncKey_sub_19E0000 proc near
debug191:019E0000
debug191:019E0000 var_18= xmmword ptr -18h
debug191:019E0000 var_8= qword ptr -8
debug191:019E0000 P1_wKeyStr= dword ptr  8  //---------用户输入的 key
debug191:019E0000
debug191:019E0000 push    ebp
debug191:019E0001 mov     ebp, esp
debug191:019E0003 sub     esp, 18h
debug191:019E0006 mov     eax, [ebp+P1_wKeyStr]
debug191:019E0009 xor     ecx, ecx
debug191:019E000B push    esi
debug191:019E000C mov     esi, 3A1h
debug191:019E0011 movdqu  xmm0, xmmword ptr [eax]
debug191:019E0015 movdqu  [ebp+var_18], xmm0
debug191:019E001A movq    xmm0, qword ptr [eax+10h]
debug191:019E001F movq    [ebp+var_8], xmm0
debug191:019E0024
debug191:019E0024 loc_19E0024:  //-----------------------对key加密循环体头部
debug191:019E0024 lea     eax, [ecx+1]
debug191:019E0027 and     eax, 80000001h
debug191:019E002C jns     short loc_19E0033
debug191:019E002E dec     eax
debug191:019E002F or      eax, 0FFFFFFFEh
debug191:019E0032 inc     eax
debug191:019E0033
debug191:019E0033 loc_19E0033:                           
debug191:019E0033 jnz     short loc_19E0042 //对于偶位置的宽字符在异或加密前先求补码
debug191:019E0035 mov     ax, word ptr [ebp+ecx*2+var_18]
debug191:019E003A neg     ax
debug191:019E003D mov     word ptr [ebp+ecx*2+var_18], ax
debug191:019E0042
debug191:019E0042 loc_19E0042: ;          //异或上 si = 0x3A1 加密
debug191:019E0042 xor     word ptr [ebp+ecx*2+var_18], si
debug191:019E0047 inc     ecx
debug191:019E0048 cmp     ecx, 0Ch
debug191:019E004B jl      short loc_19E0024 // 对key加密循环体尾部,
debug191:019E004B                   //若未加密完key的0x0C个宽字符,回到前面循环体头部继续
debug191:019E004D xor     eax, eax
debug191:019E004F pop     esi
debug191:019E0050
debug191:019E0050 loc_19E0050:  //与内部的加密key: word wArrayInnerEncKey_589B50[0x0C]比对
debug191:019E0050 mov     cx, word ptr [ebp+eax+var_18]
debug191:019E0055 cmp     cx, ds:wArrayInnerEncKey_589B50[eax]
debug191:019E005C jnz     short loc_19E006C
debug191:019E005E add     eax, 2
debug191:019E0061 cmp     eax, 18h
debug191:019E0064 jl      short loc_19E0050
debug191:019E0066 xor     eax, eax  //所有都匹配,返回 0,成功
debug191:019E0068 mov     esp, ebp
debug191:019E006A pop     ebp
debug191:019E006B retn
debug191:019E006C ; ---------------------------------------------------------------------------
debug191:019E006C
debug191:019E006C loc_19E006C:          //不匹配,返回 1,失败
debug191:019E006C mov     eax, 1
debug191:019E0071 mov     esp, ebp
debug191:019E0073 pop     ebp
debug191:019E0074 retn
debug191:019E0074 Hi_enckey_and_cmpInnerEncKey_sub_19E0000 endp
debug191:019E0074 ; ---------------------------------------------------------------------------

2.5.2 逆向注册码
上述3.5.1的核心校验函数比较简单,
其是对输入的Hi_wKeyStr_101AC480加密(python实现参考enckey),
然后与wArrayInnerEncKey_589B50加密的ekey对比。
即只需间wArrayInnerEncKey_589B50加密的ekey解密出来即可,
解密为enckey的逆运算,参考deckey实现,如下。
在单步根据核心解密函数后(并不一定需要)
WINDBG命令行切换到IDAPython,将下属#----之间的代码复制粘贴运行即可得到注册码:
SusugerZRO7
#------- ------- ------- ------- ------- ------- -------
def enckey(key):
  si = 0x3A1
  idx = 0
  wc = c_ushort(0)
  while idx < 0x0C:
    wc.value = key[idx]
    if (idx + 1) % 2 ==0 :
      wc.value = -wc.value
    wc.value ^= si
    key[idx] = wc.value
    idx += 1

def deckey(ekey):
  si = 0x3A1
  idx = 0
  wc = c_ushort(0)
  while idx < 0x0C:
    wc.value = ekey[idx]
    wc.value ^= si
    if (idx + 1) % 2 ==0 :
      wc.value = -wc.value
    ekey[idx] = wc.value
    idx += 1

cmp_ekey = []
for i in xrange(0,0x0C):
  cmp_ekey.append(Word(0x589B50+i*2))

deckey(cmp_ekey)
b"".join([chr(ch) for ch in cmp_ekey])
#SusugerZRO7
#------- ------- ------- ------- ------- ------- -------

3.溯源加密函数体的由来、隐藏机制
上面2.5提到"100~168份随机加密的核心校验函数都源自同一个函数 Hi_enckey_and_cmpInnerEncKey_sub_402450"
作者注意设计的主要考量就在于隐藏此核心校验函数。
主要通过2.4出现的以下模块全局变量进行溯源
Hi_vectorEncFunInfos_101AC588
Hi_0x80_EncFunCodeSize_101AC474,

3.1 Hi_0x80_EncFunCodeSize_101AC474在下述地方被赋值
其位于 Hi_dll_anti1_Exception_AppName_Progman_sub_100074F0 函数中

.text:1000769B mov     eax, Hi_FunCodesSize_101AC468
.text:100076A0 push    0               ; lParam
.text:100076A2 push    0Bh             ; wParam
.text:100076A4 push    0C5h            ; Msg
.text:100076A9 push    dword ptr [ebx+0DCh] ; hWnd
.text:100076AF mov     Hi_0x80_EncFunCodeSize_101AC474,,

可见Hi_0x80_EncFunCodeSize_101AC474 来自Hi_FunCodesSize_101AC468的赋值。
在Hi_dll_anti1_Exception_AppName_Progman_sub_100074F0中
Hi_vectorEncFunInfos_101AC588随机份数随机加密的函数体信息产生伪代码如下
//--------------------------------------------------------------------
VecEleCnt = Hi_ptd_rand_sub_101241E4 % 0x44 + 0x64  //随机份数 100~168份
idx = 0
while idx < VecEleCnt:
  //随机累加异或加密xorStartCaseIndex
  xorStartCaseIndex = Randc = Hi_ptd_rand_sub_101241E4 & 0x03 + 1
  //随机异或加密因子
  xortor1 = Rand1 = Hi_ptd_rand_sub_101241E4 % 0x112 + 0x68
  xortor2 = Rand2 = Hi_ptd_rand_sub_101241E4 % 0x4E + 0x17A
  xortor3 = Rand3 = Hi_ptd_rand_sub_101241E4 % (Rand2-Rand1) + Rand1
  //Hi_ptd_rand_sub_101241E4
  xortor4 = Rand4 = Hi_ptd_rand_sub_101241E4 % (Rand3-Rand2) + Rand2
  xortor5 = 0
  //xorvector = [xortor1,xortor2,xortor3,xortor4,xortor5]
  FunAddr = VirtualAlloc(0x1000)
  memmove(FunAddr,Hi_FunAddr_101AC464,Hi_0x80_EncFunCodeSize_101AC474)
  //
  **用xorvector[xorStartCaseIndex:]对拷贝的函数体累加异或加密FunAddr**
  //
  {FunInfo 随机加密函数信息体,局部变量
    -0000066C var_66C_FunAddr
    -00000668 var_668_xortor1 dd
    -00000664 var_664_xortor2 dd
    -00000660 var_660_xortor3 dd
    -0000065C var_65C_xortor4 dd
    -00000658 var_658_xortor5 dd
    -00000654 var_654_xorStartCaseIndex dd
  }
  {//通过Hi_vectorEncFunInfos_append_sub_10函数
   //添加到 Hi_vectorEncFunInfos_101AC588 信息集合中
    .text:10007854 lea eax, [ebp+var_66C_FunAddr]
    .text:1000785A mov [ebp+var_66C_FunAddr], edi
    .text:10007860 push eax
    .text:10007861 call Hi_vectorEncFunInfos_append_sub_10
  }
  idx += 1
//--------------------------------------------------------------------

3.2 核心校验函数体溯源a
上述3.1中的 Hi_FunAddr_101AC464和Hi_FunCodesSize_101AC468在
Hi_hide_in_sea_sub_10001CC0 函数中赋值,如下,而该函数调用于_My_GetProcAddr378@4。
其为CrackMeLibrary.dll导出的666个函数中的一个。

.text:10001CE4 cmp     Hi_bInitFunInfo_dword_101AC460, 1
.text:10001CEB jz      short loc_10001D50
.text:10001CED mov     eax, [ebp+P1_FunSize]
.text:10001CF0 mov     Hi_FunCodesSize_101AC468, eax
.text:10001CF5 mov     eax, [ebp+P2_FunAddr]
.text:10001CF8 push    1A4h            ; size_t
.text:10001CFD mov     Hi_FunAddr_101AC464, eax
.text:10001D02 mov     Hi_bInitFunInfo_dword_101AC460, 1

3.3 核心校验函数体溯源b
_My_GetProcAddr378@4的调用隐藏在666此对CrackMeLibrary所有导出函数的调用中。
具体位于Susuger_CrackMed的完成CrackMeLibrary.dll加载后的操作。

3.3.1下述紧连的代码块分别完成
*加密函数体大小的计算,得到var_48C_EncCmpFunSize
*CrackMeLibrary导出函数指针数组初始化。var_248_vectorAddrPtr
*加载CrackMeLibrary.dll并将666个函数指针存放于 var_248_vectorAddrPtr 向量中

.text:00402C8E mov     eax, offset Hi_FunTail_byte_4024D0
.text:00402C93 mov     [ebp+var_248_vectorAddrPtr._Myend], 0
.text:00402C9D xorps   xmm0, xmm0
.text:00402CA0 xor     edi, edi
.text:00402CA2 sub     eax, offset Hi_enckey_and_cmpInnerEncKey_sub_402450
.text:00402CA7 movq    qword ptr [ebp+var_248_vectorAddrPtr._Myfirst], xmm0
.text:00402CAF xor     ebx, ebx
.text:00402CB1 mov     [ebp+var_48C_EncCmpFunSize], eax
//--------------------------------------------------
.text:00402CB7 mov     [ebp+var_248_vectorAddrPtr._Myfirst], edi
.text:00402CBD mov     [ebp+var_248_vectorAddrPtr._Mylast], edi
.text:00402CC3 mov     [ebp+var_248_vectorAddrPtr._Myend], ebx
.text:00402CC9 mov     byte ptr [ebp+var_4], 1
.text:00402CCD cmp     Hi_hCrackMeLibrary_dword_5B4DF8, ebx
.text:00402CD3 jnz     short loc_402CE5
//--------------------------------------------------
.text:00402CD5 push    offset aCrackmelibrary ; "CrackMeLibrary.dll"
.text:00402CDA call    ds:LoadLibraryW
.text:00402CE0 mov     Hi_hCrackMeLibrary_dword_5B4DF8, eax

3.3.2 紧接3.3.1,其会传递0参数调用var_248_vectorAddrPtr中的所有导出函数数,
用于并筛选出目标扩散函数_My_GetProcAddr378@4在ecx=var_248_vectorAddrPtr中的索引0x179
.text:00402EBE mov     edi, ecx
.text:00402EC0 push    3E36E1DCh
.text:00402EC5 mov     eax, [edi+esi*4]
.text:00402EC8 call    eax  //_My_GetProcAddr378(0x3E36E1DCh)通过第二参数返回扩散函数
.text:00402ECA push    offset Hi_enckey_and_cmpInnerEncKey_sub_402450
.text:00402ECF push    [ebp+var_48C_EncCmpFunSize]
.text:00402ED5 call    eax //返回的扩散函数为 Hi_hide_in_sea_sub_10001CC0

可见,扩散函数Hi_hide_in_sea_sub_10001CC0的参数为函数首地址和函数体大小。
即核心校验函数为 Hi_enckey_and_cmpInnerEncKey_sub_402450,
其被通过666个导出的函数的调用中筛选出的目标扩散函数传递到从模块CrackMeLibrary.dll中,
从模块Hi_dll_anti1_Exception_AppName_Progman_sub_100074F0扩散实现再随机进行扩散加密保存,
以供后续关键检验逻辑随机提取加密函数体,并解密出来执行校验。

即核心校验函数的源头为 Hi_enckey_and_cmpInnerEncKey_sub_402450,
其中我们也看到了加密的wArrayEncKey_589B50,
所以也可以直接在主模块里直接执行上述IDAPython得到注册码。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//