-
-
[原创]看雪CTF2019Q3 第二题WP
-
发表于: 2019-9-25 17:00 6760
-
一、概述
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”
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);
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;
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
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 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 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 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); }
1、调用callCodeDecrypt(4774D8)对指令进行解密
2、调用callVmpHandle(475C08)对指令进行分析和执行,一直到所有指令执行完毕。
八、
callVmpHandle函数
char *__fastcall callVmpHandle(struct vmpInfo *vmpInfo, int codeAddr, _BYTE *vmpCode_1, int pinsLen) { struct vmpInfo *vmpInfo1; // esi struct tempStruct tempStruct; // [esp+8h] [ebp-10h] char *vmpCodeEnd; // [esp+10h] [ebp-8h] int vmpCode; // [esp+14h] [ebp-4h] vmpCode = (int)vmpCode_1; vmpInfo1 = vmpInfo; vmpCodeEnd = vmpCode_1; if ( vmpInfo->isVmpFlag == 1 ) { if ( ifCode_simvm_orign(vmpInfo, (char **)&vmpCode) == 2 ) { vmpCodeEnd = (char *)vmpCode; } else { vmpCodeEnd = (char *)vmpCode; if ( GetTempStructByVmpCmd(vmpInfo1, *(_BYTE *)vmpCode, &tempStruct) ) vmpCodeEnd = vmpHandle(vmpInfo1, vmpCode, (_BYTE *)pinsLen, (int)tempStruct.vmpInfo, tempStruct.copyFun); else vmpInfo1->isVmpFlag = 2; } } return vmpCodeEnd; }
1、调用ifCode_simvm_orign判断指令类型
2、如果是VMP指令则
1)调用GetTempStructByVmpCmd函数初始化相关结构,并获取对应真实代码的处理函数(474F08)
char *__fastcall callVmpHandle(struct vmpInfo *vmpInfo, int codeAddr, _BYTE *vmpCode_1, int pinsLen) { struct vmpInfo *vmpInfo1; // esi struct tempStruct tempStruct; // [esp+8h] [ebp-10h] char *vmpCodeEnd; // [esp+10h] [ebp-8h] int vmpCode; // [esp+14h] [ebp-4h] vmpCode = (int)vmpCode_1; vmpInfo1 = vmpInfo; vmpCodeEnd = vmpCode_1; if ( vmpInfo->isVmpFlag == 1 ) { if ( ifCode_simvm_orign(vmpInfo, (char **)&vmpCode) == 2 ) { vmpCodeEnd = (char *)vmpCode; } else { vmpCodeEnd = (char *)vmpCode; if ( GetTempStructByVmpCmd(vmpInfo1, *(_BYTE *)vmpCode, &tempStruct) ) vmpCodeEnd = vmpHandle(vmpInfo1, vmpCode, (_BYTE *)pinsLen, (int)tempStruct.vmpInfo, tempStruct.copyFun); else vmpInfo1->isVmpFlag = 2; } } return vmpCodeEnd; }
2)调用vmpHandle(474EAC)
九、
vmpHandle (474EAC)函数
char *__fastcall vmpHandle(struct vmpInfo *vmpInfo, int vmpCode, _BYTE *pinsLen1, int vmpInfo1, int vmpExeFun) { _BYTE *pinsLen; // esi struct vmpInfo *vmpInfo_1; // ebx char *vmpInsEnd; // edi int out_size; // [esp+Ch] [ebp-14h] char out21; // [esp+10h] [ebp-10h] char out11; // [esp+14h] [ebp-Ch] int out_orgReg; // [esp+18h] [ebp-8h] int out_aimReg; // [esp+1Ch] [ebp-4h] pinsLen = pinsLen1; vmpInfo_1 = vmpInfo; vmpInsEnd = copyVMPcode(vmpInfo, (char *)vmpCode); *pinsLen = vmpInfo_1->vmpCode.codeInsLen; getVmpReg(vmpInfo_1, &out_aimReg, &out_orgReg, &out11, &out21, &out_size); vmpRealFunHandle((int)vmpInfo_1, out_aimReg, out_orgReg, vmpInfo1, (int (__fastcall *)(int, int))vmpExeFun, out_size); return vmpInsEnd; }
1、调用copyVMPcode拷贝当前条VMP指令,一条指令长度为0x64
2、调用getVmpReg,应该是获取相关寄存器或者执行简单指令执行
3、执行该指令的真实指令处理函数
4、稍微分析了下虚拟指令
firt ins +0 01 00 00 00 +4 00 00 00 00 setout1Flag +8 17 11 01 F0 vmpcmd 1 reg1 +c 00 00 00 00 +10 00 00 00 00 +14 00 00 00 00 offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 unknowcmd1 +2A 01 +2B 00 +2C 0C 00 00 F0 vmdcmp2 +30 00 00 00 00 +34 00 00 00 00 +38 30 00 00 00 offset +3C 01 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 +4E 00 +4F 02 +50 07 insSize +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 if ( vmpInfo->vmpCode.vmpCmd1 ) F0011117 { out1 = result0Reg out3 = [result0Reg]+offset if(unknowcmd1 !=3) out5 = unknowcmd1 if(setout1Flag || offset) out1 = out3 } //0x01 varc = fs:[offset2] var7 = fs:[offset2] + offset1 ================================================ +0 01 00 00 00 +4 00 aimreg_SetFlag +5 00 +6 00 +7 00 +8 17 01 00 F0 aimreg_cmd +C 00 00 00 00 +10 00 00 00 00 +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1C 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 01 aimreg_size +2A 01 +2B 00 +2C 17 11 01 F0 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 +38 02 00 00 00 orgreg_offset +3C 01 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 orgreg_size +4E 01 +4F 02 +50 03 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //============================================================== +0 01 00 00 00 +4 01 00 00 00 aimreg_SetFlag +8 02 00 00 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 EF FF FF FF aimreg_offset +18 02 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 17 01 00 F0 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 +38 00 00 00 00 orgreg_offset +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 01 orgreg_size +4E 00 +4F 02 +50 03 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //===================================================== cmp [aimreg+offset], orgreg_value set global.cmpReg 0x80位 +0 08 00 00 00 +4 01 00 00 00 aimreg_SetFlag +8 02 00 00 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 EF FF FF FF aimreg_offset +18 02 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 01 00 00 00 orgreg_value +38 00 00 00 00 orgreg_offset +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 orgreg_size +4E 01 +4F 02 +50 04 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //========================================================================== +0 1F 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 00 00 00 00 +c 00 00 00 00 +10 6A 01 00 00 branch_offset +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 orgreg_value +38 00 00 00 00 orgreg_offset +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 00 orgreg_size +4E 00 +4F 01 +50 06 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //=================================================== cmp [aimreg+offset], orgreg_value set global.cmpReg 0x80位 +0 08 00 00 00 +4 01 00 00 00 aimreg_SetFlag +8 14 11 01 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 44 03 00 00 aimreg_offset +18 01 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 01 00 00 00 orgreg_value +38 00 00 00 00 +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 orgreg_size +4E 01 +4F 02 +50 07 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 ============================================================================ +0 1F 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 00 00 00 00 aimreg_cmd +c 00 00 00 00 +10 5D 01 00 00 branch_offset +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 orgreg_value +38 00 00 00 00 +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 00 orgreg_size +4E 00 +4F 01 +50 06 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //==================================================== //获取输入sn的地址 mov aimreg+aimReg, [org+orgOfset] +0 01 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 17 11 01 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 01 +2B 00 +2c 02 00 00 F0 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 orgreg_value +38 E4 FF FF FF orgreg_offset +3C 02 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +48 00 04 00 02 +4C 03 +4D 00 orgreg_size +4E 00 +4F 00 +50 00 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 //=================================================== +0 0B 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 00 00 00 00 aimreg_cmd +c 00 00 00 00 +10 6D B1 F6 FF branch_offset +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 +38 00 00 00 00 +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +48 00 00 00 01 +4C 05 code_offset +4D 00 orgreg_size +4E 00 +4F 00 +50 00 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 08 00 00 00
其中如下函数vmp_opcode_0x08为处理比较指令的,跟踪到这里后即基本可以还原了。
truct vmpInfo *__userpurge vmp_opcode_0x08@<eax>(struct vmpInfo *vmpInfo@<eax>, _DWORD *out1@<edx>, _DWORD *vmpcodeLocation@<ecx>, unsigned int vmpcodeEnd@<edi>, int out5) { unsigned int v5; // et0 unsigned int v6; // et0 unsigned int v7; // et0 unsigned int v8; // et0 struct vmpInfo *result; // eax switch ( out5 ) { case 1: v5 = __readeflags(); __writeeflags(v5); result = vmp_opcode_0x08_setCmpFlag( vmpInfo, *(unsigned __int8 *)out1 - *(unsigned __int8 *)vmpcodeLocation, 1, v5); break; case 2: v6 = __readeflags(); __writeeflags(v6); result = vmp_opcode_0x08_setCmpFlag(vmpInfo, vmpcodeEnd, 2, v6); break; case 4: v7 = __readeflags(); __writeeflags(v7); result = vmp_opcode_0x08_setCmpFlag(vmpInfo, *out1 - *vmpcodeLocation, out5, v7); break; default: v8 = __readeflags(); __writeeflags(v8); result = vmp_opcode_0x08_setCmpFlag(vmpInfo, *out1 - *vmpcodeLocation, 4, v8); break; } return result; }
使用输入的字符-0x7F,然后与如下地址中的E0 B2 B1比较相等即为正确。
CODE:00499570 clts CODE:00499572 mov al, 0B1h CODE:00499574 mov dl, 0B8h CODE:00499576 mov bh, 0E0h CODE:00499578 mov dl, 0B1h CODE:0049957A mov al, 0E2h
其中 499572地址敲好在产生异常clts指令的下方。
最后得到flag为:
Simpower91a321
ps :
作者的dephi相关题目已经出到了3.0阶段
1.0 ---------------------dephi+jscrypt 内存中暴露了key
2.0---------------------- dephi+代码重定位 +VMP;虽然有vmp但是主要算法在代码重定位部分
3.0----------------------
dephi+代码重定位 +VMP:所有代码都在vmp
4.0----------------------
dephi+代码重定位 +VMP:算法复杂化????????????
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); }
1、调用callCodeDecrypt(4774D8)对指令进行解密
2、调用callVmpHandle(475C08)对指令进行分析和执行,一直到所有指令执行完毕。
八、
callVmpHandle函数
char *__fastcall callVmpHandle(struct vmpInfo *vmpInfo, int codeAddr, _BYTE *vmpCode_1, int pinsLen) { struct vmpInfo *vmpInfo1; // esi struct tempStruct tempStruct; // [esp+8h] [ebp-10h] char *vmpCodeEnd; // [esp+10h] [ebp-8h] int vmpCode; // [esp+14h] [ebp-4h] vmpCode = (int)vmpCode_1; vmpInfo1 = vmpInfo; vmpCodeEnd = vmpCode_1; if ( vmpInfo->isVmpFlag == 1 ) { if ( ifCode_simvm_orign(vmpInfo, (char **)&vmpCode) == 2 ) { vmpCodeEnd = (char *)vmpCode; } else { vmpCodeEnd = (char *)vmpCode; if ( GetTempStructByVmpCmd(vmpInfo1, *(_BYTE *)vmpCode, &tempStruct) ) vmpCodeEnd = vmpHandle(vmpInfo1, vmpCode, (_BYTE *)pinsLen, (int)tempStruct.vmpInfo, tempStruct.copyFun); else vmpInfo1->isVmpFlag = 2; } } return vmpCodeEnd; }
1、调用ifCode_simvm_orign判断指令类型
2、如果是VMP指令则
1)调用GetTempStructByVmpCmd函数初始化相关结构,并获取对应真实代码的处理函数(474F08)
char *__fastcall callVmpHandle(struct vmpInfo *vmpInfo, int codeAddr, _BYTE *vmpCode_1, int pinsLen) { struct vmpInfo *vmpInfo1; // esi struct tempStruct tempStruct; // [esp+8h] [ebp-10h] char *vmpCodeEnd; // [esp+10h] [ebp-8h] int vmpCode; // [esp+14h] [ebp-4h] vmpCode = (int)vmpCode_1; vmpInfo1 = vmpInfo; vmpCodeEnd = vmpCode_1; if ( vmpInfo->isVmpFlag == 1 ) { if ( ifCode_simvm_orign(vmpInfo, (char **)&vmpCode) == 2 ) { vmpCodeEnd = (char *)vmpCode; } else { vmpCodeEnd = (char *)vmpCode; if ( GetTempStructByVmpCmd(vmpInfo1, *(_BYTE *)vmpCode, &tempStruct) ) vmpCodeEnd = vmpHandle(vmpInfo1, vmpCode, (_BYTE *)pinsLen, (int)tempStruct.vmpInfo, tempStruct.copyFun); else vmpInfo1->isVmpFlag = 2; } } return vmpCodeEnd; }
2)调用vmpHandle(474EAC)
九、
vmpHandle (474EAC)函数
char *__fastcall vmpHandle(struct vmpInfo *vmpInfo, int vmpCode, _BYTE *pinsLen1, int vmpInfo1, int vmpExeFun) { _BYTE *pinsLen; // esi struct vmpInfo *vmpInfo_1; // ebx char *vmpInsEnd; // edi int out_size; // [esp+Ch] [ebp-14h] char out21; // [esp+10h] [ebp-10h] char out11; // [esp+14h] [ebp-Ch] int out_orgReg; // [esp+18h] [ebp-8h] int out_aimReg; // [esp+1Ch] [ebp-4h] pinsLen = pinsLen1; vmpInfo_1 = vmpInfo; vmpInsEnd = copyVMPcode(vmpInfo, (char *)vmpCode); *pinsLen = vmpInfo_1->vmpCode.codeInsLen; getVmpReg(vmpInfo_1, &out_aimReg, &out_orgReg, &out11, &out21, &out_size); vmpRealFunHandle((int)vmpInfo_1, out_aimReg, out_orgReg, vmpInfo1, (int (__fastcall *)(int, int))vmpExeFun, out_size); return vmpInsEnd; }
1、调用copyVMPcode拷贝当前条VMP指令,一条指令长度为0x64
2、调用getVmpReg,应该是获取相关寄存器或者执行简单指令执行
3、执行该指令的真实指令处理函数
4、稍微分析了下虚拟指令
firt ins +0 01 00 00 00 +4 00 00 00 00 setout1Flag +8 17 11 01 F0 vmpcmd 1 reg1 +c 00 00 00 00 +10 00 00 00 00 +14 00 00 00 00 offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 unknowcmd1 +2A 01 +2B 00 +2C 0C 00 00 F0 vmdcmp2 +30 00 00 00 00 +34 00 00 00 00 +38 30 00 00 00 offset +3C 01 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 +4E 00 +4F 02 +50 07 insSize +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 if ( vmpInfo->vmpCode.vmpCmd1 ) F0011117 { out1 = result0Reg out3 = [result0Reg]+offset if(unknowcmd1 !=3) out5 = unknowcmd1 if(setout1Flag || offset) out1 = out3 } //0x01 varc = fs:[offset2] var7 = fs:[offset2] + offset1 ================================================ +0 01 00 00 00 +4 00 aimreg_SetFlag +5 00 +6 00 +7 00 +8 17 01 00 F0 aimreg_cmd +C 00 00 00 00 +10 00 00 00 00 +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1C 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 01 aimreg_size +2A 01 +2B 00 +2C 17 11 01 F0 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 +38 02 00 00 00 orgreg_offset +3C 01 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 orgreg_size +4E 01 +4F 02 +50 03 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //============================================================== +0 01 00 00 00 +4 01 00 00 00 aimreg_SetFlag +8 02 00 00 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 EF FF FF FF aimreg_offset +18 02 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 17 01 00 F0 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 +38 00 00 00 00 orgreg_offset +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 01 orgreg_size +4E 00 +4F 02 +50 03 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //===================================================== cmp [aimreg+offset], orgreg_value set global.cmpReg 0x80位 +0 08 00 00 00 +4 01 00 00 00 aimreg_SetFlag +8 02 00 00 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 EF FF FF FF aimreg_offset +18 02 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 01 00 00 00 orgreg_value +38 00 00 00 00 orgreg_offset +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 orgreg_size +4E 01 +4F 02 +50 04 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //========================================================================== +0 1F 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 00 00 00 00 +c 00 00 00 00 +10 6A 01 00 00 branch_offset +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 orgreg_value +38 00 00 00 00 orgreg_offset +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 00 orgreg_size +4E 00 +4F 01 +50 06 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //=================================================== cmp [aimreg+offset], orgreg_value set global.cmpReg 0x80位 +0 08 00 00 00 +4 01 00 00 00 aimreg_SetFlag +8 14 11 01 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 44 03 00 00 aimreg_offset +18 01 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 01 00 00 00 orgreg_value +38 00 00 00 00 +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 04 orgreg_size +4E 01 +4F 02 +50 07 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 ============================================================================ +0 1F 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 00 00 00 00 aimreg_cmd +c 00 00 00 00 +10 5D 01 00 00 branch_offset +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 orgreg_value +38 00 00 00 00 +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +4C 00 +4D 00 orgreg_size +4E 00 +4F 01 +50 06 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 00 00 00 00 //==================================================== //获取输入sn的地址 mov aimreg+aimReg, [org+orgOfset] +0 01 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 17 11 01 F0 aimreg_cmd +c 00 00 00 00 +10 00 00 00 00 +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 01 +2B 00 +2c 02 00 00 F0 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 orgreg_value +38 E4 FF FF FF orgreg_offset +3C 02 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +48 00 04 00 02 +4C 03 +4D 00 orgreg_size +4E 00 +4F 00 +50 00 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 //=================================================== +0 0B 00 00 00 +4 00 00 00 00 aimreg_SetFlag +8 00 00 00 00 aimreg_cmd +c 00 00 00 00 +10 6D B1 F6 FF branch_offset +14 00 00 00 00 aimreg_offset +18 00 00 00 00 +1c 00 00 00 00 +20 00 00 00 00 +24 00 00 00 00 +28 00 +29 04 aimreg_size +2A 00 +2B 00 +2c 00 00 00 00 orgreg_cmd +30 00 00 00 00 +34 00 00 00 00 +38 00 00 00 00 +3C 00 00 00 00 +40 00 00 00 00 +44 00 00 00 00 +48 00 00 00 00 +48 00 00 00 01 +4C 05 code_offset +4D 00 orgreg_size +4E 00 +4F 00 +50 00 +51 00 +52 00 +53 00 +54 00 00 00 00 +58 00 00 00 00 +5C 00 00 00 00 +60 08 00 00 00
其中如下函数vmp_opcode_0x08为处理比较指令的,跟踪到这里后即基本可以还原了。
truct vmpInfo *__userpurge vmp_opcode_0x08@<eax>(struct vmpInfo *vmpInfo@<eax>, _DWORD *out1@<edx>, _DWORD *vmpcodeLocation@<ecx>, unsigned int vmpcodeEnd@<edi>, int out5) { unsigned int v5; // et0 unsigned int v6; // et0 unsigned int v7; // et0 unsigned int v8; // et0 struct vmpInfo *result; // eax switch ( out5 ) { case 1: v5 = __readeflags(); __writeeflags(v5); result = vmp_opcode_0x08_setCmpFlag( vmpInfo, *(unsigned __int8 *)out1 - *(unsigned __int8 *)vmpcodeLocation, 1, v5); break; case 2: v6 = __readeflags(); __writeeflags(v6); result = vmp_opcode_0x08_setCmpFlag(vmpInfo, vmpcodeEnd, 2, v6); break; case 4: v7 = __readeflags(); __writeeflags(v7); result = vmp_opcode_0x08_setCmpFlag(vmpInfo, *out1 - *vmpcodeLocation, out5, v7); break; default: v8 = __readeflags(); __writeeflags(v8); result = vmp_opcode_0x08_setCmpFlag(vmpInfo, *out1 - *vmpcodeLocation, 4, v8); break; } return result; }
使用输入的字符-0x7F,然后与如下地址中的E0 B2 B1比较相等即为正确。
CODE:00499570 clts CODE:00499572 mov al, 0B1h CODE:00499574 mov dl, 0B8h CODE:00499576 mov bh, 0E0h CODE:00499578 mov dl, 0B1h CODE:0049957A mov al, 0E2h
其中 499572地址敲好在产生异常clts指令的下方。
最后得到flag为:
Simpower91a321
ps :
作者的dephi相关题目已经出到了3.0阶段
1.0 ---------------------dephi+jscrypt 内存中暴露了key
2.0---------------------- dephi+代码重定位 +VMP;虽然有vmp但是主要算法在代码重定位部分
3.0----------------------
dephi+代码重定位 +VMP:所有代码都在vmp
4.0----------------------
dephi+代码重定位 +VMP:算法复杂化????????????
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分析
#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为异常处理函数
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处理函数
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为指令大小
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函数
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-9-25 17:53
被oooAooo编辑
,原因:
赞赏
他的文章
- 看雪CTF 2019总决赛 第六题 三道八佛 IDA脱壳脚本 5669
- [原创]看雪CTF2019Q3第四题WP 5934
- [原创]看雪CTF2019Q3 第二题WP 6761
- [2019看雪CTF晋级赛Q3第九题WP 12491
- [原创]看雪CTF2019晋级赛Q2第三题 5022
看原图
赞赏
雪币:
留言: