-
-
[原创]看雪CTF2019Q3 第二题WP
-
发表于: 2019-9-25 17:00 7033
-
一、概述
1 依然是dephi+脚本形式;
2 与前版相比加入了反调试;
3 与前版相比解密部分全部用虚拟机实现,不会用代码执行非虚拟机指令;
4 使用异常处理进入真正的解密函数。
二、脚本获取
1、通过跟踪函数Tfrmcrackme_FormShow,可以获取脚本,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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
1 2 3 4 | HIDWORD(v5) = v1; LODWORD(v5) = &sncheck; Teengine::TTeeFunction::InternalSetPeriod(*(Teengine::TTeeFunction **)(v1 + 824), v5); Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 824), v6); |
1 2 3 4 | 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
1 2 | v10 = sub_477DDC(( int )&cls_antiDebug_TAntiDebug, 1, 0); *(_DWORD *)(v1 + 828) = v10; |
1 2 | v10 = sub_477DDC(( int )&cls_antiDebug_TAntiDebug, 1, 0); *(_DWORD *)(v1 + 828) = v10; |
2、在Tfrmcrackme_FormCreate中貌似设置反调试函数:49978C
1 2 3 4 | HIDWORD(v11) = v1; LODWORD(v11) = sub_49978C; Teengine::TTeeFunction::InternalSetPeriod(v10, v12, v13, v11); Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 828), v14); |
3、反调试函数477F64
1 2 3 4 | HIDWORD(v11) = v1; LODWORD(v11) = sub_49978C; Teengine::TTeeFunction::InternalSetPeriod(v10, v12, v13, v11); Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 828), v14); |
3、反调试函数477F64
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
1 2 3 4 5 6 7 8 9 10 | 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 、过掉反调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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、异常处理函数注册
1 2 3 4 5 6 7 | 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为异常处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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指令
1 2 | 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函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | 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函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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)函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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、稍微分析了下虚拟指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | 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为处理比较指令的,跟踪到这里后即基本可以还原了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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比较相等即为正确。
1 2 3 4 5 6 | 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:算法复杂化????????????
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
1 2 3 4 5 6 7 8 9 10 | 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 、过掉反调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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、异常处理函数注册
1 2 3 4 5 6 7 | 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为异常处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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指令
1 2 | 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函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | 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函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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)函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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、稍微分析了下虚拟指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | 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为处理比较指令的,跟踪到这里后即基本可以还原了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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比较相等即为正确。
1 2 3 4 5 6 | 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:算法复杂化????????????
1 2 3 4 5 6 7 8 9 10 | 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 、过掉反调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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、异常处理函数注册
1 2 3 4 5 6 7 | 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为异常处理函数
1 2 3 4 5 6 7 | 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为异常处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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指令
1 2 | CODE:00499530 call GetVmpCode CODE:00499535 mov [ebp+var_10], eax |
其中 eax+4 为VMP指令地址,
eax+8为指令大小
1 2 | 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脱壳脚本 5862
- [原创]看雪CTF2019Q3第四题WP 6129
- [原创]看雪CTF2019Q3 第二题WP 7034
- [2019看雪CTF晋级赛Q3第九题WP 12771
- [原创]看雪CTF2019晋级赛Q2第三题 5194
赞赏
雪币:
留言: