一、概述
1 依然是dephi+脚本形式;
2 与前版相比加入了反调试;
3 与前版相比解密部分全部用虚拟机实现,不会用代码执行非虚拟机指令;
4 使用异常处理进入真正的解密函数。
二、脚本获取
1、通过跟踪函数Tfrmcrackme_FormShow,可以获取脚本,如下:
Function tion sptWBCallback(spt_wb_id,spt_wb_name,optionstr) { url='#sptWBCallback:id='; url=url+spt_wb_id+'; eventName='+spt_wb_name; if(optionstr) url=url+'; params=optionstr'; location=url; } <script language="vbscript"> function alert(msg_str) MsgBox msg_str,vbOKOnly + vbExclamation + vbApplicationModal,"" End Function </script> <body bgcolor=f0f0f0 topmargn=0 leftmargn=0 >、 <center> <br><br><br> <input value="" id="pswd" size=39> </input> <br><br><br> <input type=button value="checkMyFlag" onclick="ckpswd();"> </center> </body> function ckpswd() { key = "Simpower91"; a = document.all.pswd.value; if (a.indexOf(key) == 0) { l = a.length; i = key.length; sptWBCallback(a.substring(i, l)); } else { alert("wrong!<" + a + "> is not my GUID ;-)"); return "1234"; } } function ok() { alert("congratulations!"); } CMShowingChanged Tfrmcrackme_ApplicationEvents1Message _Tfrmcrackme_FormCreate about:blank#sptWBCallback:id=111;eventName=undefined
2 在脚本部分要求输入sn前部分字符必须是“Simpower91”
3、然后通过sptWBCallback进入dephi回调函数执行校验。
三、回调函数定位-sncheck
在函数Tfrmcrackme_FormCreate中注册:0049945C
HIDWORD(v5) = v1; LODWORD(v5) = &sncheck; Teengine::TTeeFunction::InternalSetPeriod(*(Teengine::TTeeFunction **)(v1 + 824), v5); Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 824), v6);
四、反调试
1、 在Tfrmcrackme_FormCreate中调用反调试相关函数477DDC
v10 = sub_477DDC((int)&cls_antiDebug_TAntiDebug, 1, 0); *(_DWORD *)(v1 + 828) = v10;
2、在Tfrmcrackme_FormCreate中貌似设置反调试函数:49978C
HIDWORD(v11) = v1; LODWORD(v11) = sub_49978C; Teengine::TTeeFunction::InternalSetPeriod(v10, v12, v13, v11); Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 828), v14);
3、反调试函数477F64
int __usercall antiDebug5@<eax>(int a1@<eax>, int a2@<ebx>) { int v2; // esi int v4; // [esp+0h] [ebp-408h] v2 = a1; Windows::FillMemory(&v4, 0x400u, 0); DeleteFiber(&v4); if ( GetLastError_0() == 0x57 ) { a2 = 0; unknown_libname_1058(v2); } else { LOBYTE(a2) = 1; unknown_libname_1059(v2); } return a2; }
4、反调试函数478110
int __usercall antiDebug3@<eax>(int a1@<eax>, int a2@<ebx>) { if ( *(_BYTE *)(__readfsdword(0x30u) + 2) ) { LOBYTE(a2) = 1; unknown_libname_1059(a1); } else { a2 = 0; unknown_libname_1058(a1); } return a2; }
5、反调试47814C
int __usercall antiDebug4@<eax>(int a1@<eax>, int a2@<ebx>) { if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 ) { LOBYTE(a2) = 1; unknown_libname_1059(a1); } else { a2 = 0; unknown_libname_1058(a1); } return a2; }
6、反调试函数:477F64
int __usercall antiDebug5@<eax>(int a1@<eax>, int a2@<ebx>) { int v2; // esi int v4; // [esp+0h] [ebp-408h] v2 = a1; Windows::FillMemory(&v4, 0x400u, 0); DeleteFiber(&v4); if ( GetLastError_0() == 0x57 ) { a2 = 0; unknown_libname_1058(v2); } else { LOBYTE(a2) = 1; unknown_libname_1059(v2); } return a2; }
7、反调试函数4780B8
int antiDebug7() { HMODULE v0; // eax HANDLE v1; // eax v0 = LoadLibraryA("ntdll.dll"); dword_49DCC0 = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress_0(v0, "NtSetInformationThread"); v1 = GetCurrentThread(); return dword_49DCC0(v1, 17, 0, 0); }
8 、过掉反调试
#coding=utf-8 import struct from idaapi import * from idc import * from idautils import * dbg_write_memory(0x478070, '\xEB')#antiDebug2 refresh_debugger_memory() dbg_write_memory(0x47812E, '\x90\x90')#antiDebug3 refresh_debugger_memory() dbg_write_memory(0x47816C, '\x90\x90')#antiDebug4 refresh_debugger_memory() dbg_write_memory(0x4781F4, '\x90\x90')#antiDebug6 refresh_debugger_memory() dbg_write_memory(0x477F64, '\x33\xC0\xC3\x90')#antiDebug5 refresh_debugger_memory() dbg_write_memory(0x4780B8, '\x33\xC0\xC3\x90')#antiDebug7 refresh_debugger_memory()
五、sncheck分析
1、异常处理函数注册
CODE:004994A7 push dword ptr fs:[eax] CODE:004994AA mov fs:[eax], esp CODE:004994AD mov eax, offset sub_475ED8 CODE:004994B2 mov edx, ds:off_49C3D8 CODE:004994B8 mov [edx+4], eax CODE:004994BB mov eax, ds:off_49C3D8 CODE:004994C0 call sub_46590C
其中sub_475ED8为异常处理函数
signed int __cdecl sub_475ED8(_DWORD *a1, int a2, int a3) { signed int result; // eax result = 1; if ( !a1[1] ) { if ( *a1 == 0xC0000096 ) { *(_DWORD *)(a3 + 184) = realSnCheck; result = 0; } else if ( *a1 == 0xC00000FD ) { *(_DWORD *)(a3 + 196) -= 256; } } return result; }
其中 475EBC(realSnCheck)为异常0C0000096h处理函数
2、获取vmp指令
CODE:00499530 call GetVmpCode CODE:00499535 mov [ebp+var_10], eax
其中 eax+4 为VMP指令地址, eax+8为指令大小
3、产生异常,进入 475EBC(realSnCheck)
六、 realSnCheck( 475EBC )
1、realSnCheck 调用477778 与之前类似
1)解密代码和指令
2)判断加密的指令是否是真实代码,如果是,这将相应代码进行重定位动态执行
3)如果是VMP指令,则进入虚拟机执行。
本题没有真实代码,所有指令都为虚拟机代码
2、最终会进入476B8C(codeExe)函数中
七、codeExe函数
int __stdcall codeExe(int a1, int codeData, int retAddr, int dataOfFile, _DWORD *a5, int *a6, _DWORD *a7) { struct globalInfo *global; // ebx struct vmpInfo *vmpInfo; // esi int v9; // edi int v10; // ecx int v11; // esi int v12; // edx int *v14; // [esp+1Ch] [ebp-380h] void *v15; // [esp+20h] [ebp-37Ch] int *v16; // [esp+24h] [ebp-378h] void *v17; // [esp+28h] [ebp-374h] int v18; // [esp+34h] [ebp-368h] int *v19; // [esp+38h] [ebp-364h] int v20; // [esp+3Ch] [ebp-360h] char v21; // [esp+40h] [ebp-35Ch] char v22; // [esp+140h] [ebp-25Ch] _BYTE *v23; // [esp+374h] [ebp-28h] char a4[6]; // [esp+37Ah] [ebp-22h] int v25; // [esp+380h] [ebp-1Ch] char *a2; // [esp+384h] [ebp-18h] int v27; // [esp+388h] [ebp-14h] int vmpDataSize; // [esp+38Ch] [ebp-10h] char *v29; // [esp+390h] [ebp-Ch] char *insSize; // [esp+394h] [ebp-8h] int v31; // [esp+398h] [ebp-4h] int vars0; // [esp+39Ch] [ebp+0h] v19 = 0; v18 = 0; v27 = 0; v16 = &vars0; v15 = &loc_477095; v14 = (int *)__readfsdword(0); __writefsdword(0, (unsigned int)&v14); global = sub_4760D0(); v31 = vars0 + 8; global->retAddr = retAddr; *(_DWORD *)&a4[2] = global->curVmpCode1; a2 = *(char **)&a4[2]; vmpInfo = global->vmpInfo; if ( !vmpInfo ) { sub_4778E8(global); vmpInfo = global->vmpInfo; } a4[1] = 0; if ( vmpInfo->isVmpFlag == 1 ) { a4[1] = 1; simvm_Init(global, v31, 16); } while ( 1 ) { callCodeDecrypt((int)global, dataOfFile, *(int *)&a4[2], &vmpDataSize); while ( 1 ) { while ( vmpInfo->isVmpFlag == 1 ) { a4[0] = 0; v25 = (int)callVmpHandle(vmpInfo, codeData, *(_BYTE **)&a4[2], (int)a4); insSize = (char *)(v25 - *(_DWORD *)&a4[2]); if ( dataOfFile == global->curVmpCodeAddr ) { GetNextValueByLen(&codeData, (unsigned __int8)a4[0]); global->curCodeAddr = codeData; GetNextValueByLen(&dataOfFile, insSize); global->curVmpCodeAddr = dataOfFile; } else { dataOfFile = global->curVmpCodeAddr; codeData = global->curCodeAddr; } callCodeDecrypt((int)global, dataOfFile, *(int *)&a4[2], &vmpDataSize); } if ( ifCode_simvm_orign(vmpInfo, &a2) != 1 ) break; a2 = *(char **)&a4[2]; a4[1] = 1; simvm_Init(global, v31, 16); } insSize = &a2[-*(_DWORD *)&a4[2]]; a2 = *(char **)&a4[2]; if ( !a4[1] ) break; a4[1] = 0; vmpInfo->field_914 = 1; } if ( (signed int)insSize > 0 ) { GetNextValueByLen(&dataOfFile, insSize); callCodeDecrypt((int)global, dataOfFile, *(int *)&a4[2], &vmpDataSize); } vmpDataSize = off_49DCA8(*(_DWORD *)&a4[2], vmpDataSize, 0, &v20, 4, v14); *a5 = vmpDataSize; v9 = unknown_libname_29(32 - vmpDataSize - 6); a2 = (char *)&global->field_1038 + v9; qmemcpy(a2, *(const void **)&a4[2], vmpDataSize); v14 = 0; *((_BYTE *)&global->field_1038 + v9 + vmpDataSize) = 104; unknown_libname_56(&v27, &v22); if ( (unsigned __int8)sub_476200(v27, global->cmpReg1) ) { unknown_libname_56(&v27, &v21); if ( sub_466514(&str_FF_0[1], v27) != 1 ) { insSize = (char *)sub_466514(&str___29[1], v27); v14 = &v27; sn11111(v27); System::__linkproc__ LStrCopy(v14); insSize = (char *)sub_466578(v27); if ( vmpDataSize - 1 >= 0 ) { v10 = vmpDataSize; v29 = 0; do { v14 = 0; (v29++)[v9 + 4152 + (_DWORD)global] = -112; --v10; } while ( v10 ); } insSize += vmpDataSize; vmpInfo->isVmpFlag1 = vmpInfo->isVmpFlag; if ( (signed int)insSize <= 0 ) { insSize += dataOfFile - global->field_1070; v14 = (int *)&v29; callCodeDecrypt((int)global, global->field_1070, *(int *)&a4[2], &v29); } v23 = sub_475A90(vmpInfo, *(int *)&a4[2], (unsigned int)insSize); vmpInfo->isVmpFlag = vmpInfo->isVmpFlag1; if ( !v23 ) global->curCodeAddr1 = global->curCodeAddr; } } else if ( sub_466514(&str_CALL_0[1], v27) == 1 ) { unknown_libname_56(&v27, &v21); if ( sub_466514(&str_FF_0[1], v27) != 1 ) { insSize = (char *)sub_466514(&str___29[1], v27); v29 = insSize - 1; v11 = (signed int)(insSize - 1) / 2; a2 = (char *)&global->field_1038 + v9 + v11; v14 = &v27; sn11111(v27); System::__linkproc__ LStrCopy(v14); insSize = (char *)sub_466578(v27); v29 = (char *)(&insSize[codeData] - ((char *)&global->field_1038 + v9)); qmemcpy(a2, &v29, vmpDataSize - v11); } insSize = (char *)vmpDataSize; v23 = (_BYTE *)vmpDataSize; } else { insSize = (char *)vmpDataSize; v23 = (_BYTE *)vmpDataSize; } *a7 = insSize; insSize = (char *)retAddr; a2 = (char *)&global->field_1038 + v9 + vmpDataSize + 1; qmemcpy(a2, &insSize, 4u); v14 = 0; *((_BYTE *)&global->field_103C + v9 + vmpDataSize + 1) = -61; *a6 = (int)&global->field_1038 + v9; global->curCodeAddr = codeData + *a7; global->curVmpCodeAddr = (int)&v23[dataOfFile]; unknown_libname_56(&v19, &v22); v14 = v19; unknown_libname_56(&v18, &v21); v12 = *a6; sub_47745C(vmpDataSize, v18, v14); __writefsdword(0, (unsigned int)v15); v17 = &loc_47709C; System::__linkproc__ LStrArrayClr(&v18, 2); return System::__linkproc__ LStrClr(&v27); }