首页
社区
课程
招聘
[原创]看雪CTF2019Q3 第二题WP
发表于: 2019-9-25 17:00 6759

[原创]看雪CTF2019Q3 第二题WP

2019-9-25 17:00
6759

一、概述

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编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (3)
雪    币: 19
活跃值: (128)
能力值: ( LV9,RANK:146 )
在线值:
发帖
回帖
粉丝
2
哦哦,原来是这样子,'嗯'我已经完全搞懂了
2019-9-25 19:14
0
雪    币: 5182
活跃值: (9697)
能力值: ( LV9,RANK:181 )
在线值:
发帖
回帖
粉丝
3
这题最牛的是,如果调试的时候输入了正确的结果,也提示错误!!!让我完美的错过了侥幸获取flag的机会。
2019-9-27 12:06
0
雪    币: 441
活跃值: (419)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
师傅,你这个脚本编写的时候那几个地址的来历可以普及下吗
2019-10-4 22:35
0
游客
登录 | 注册 方可回帖
返回
//