看雪CTF2019Q3 第二题WP
2019-09-25 18:14:08 Author: bbs.pediy.com(查看原文) 阅读量:195 收藏

一、概述

1  依然是dephi+脚本形式;

2  与前版相比加入了反调试;

3 与前版相比解密部分全部用虚拟机实现,不会用代码执行非虚拟机指令;

4 使用异常处理进入真正的解密函数。

二、脚本获取

1、通过跟踪函数Tfrmcrackme_FormShow,可以获取脚本,如下:

Function tion sptWBCallback(spt_wb_id,spt_wb_name,optionstr)
{
	url='#sptWBCallback:id=';
	url=url+spt_wb_id+';
	eventName='+spt_wb_name;
	if(optionstr) 
		url=url+';
	params=optionstr';
	location=url;
}

<script language="vbscript">
function alert(msg_str)
	MsgBox msg_str,vbOKOnly + vbExclamation + vbApplicationModal,""
	
End Function
	
</script>


<body bgcolor=f0f0f0 topmargn=0 leftmargn=0 >、
	<center>
	<br><br><br>
	<input value="" id="pswd" size=39>
	</input>
	<br><br><br>
	<input type=button value="checkMyFlag" onclick="ckpswd();">
	</center>

</body>

function ckpswd() {
    key = "Simpower91";
    a = document.all.pswd.value;
    if (a.indexOf(key) == 0) {
        l = a.length;
        i = key.length;
        sptWBCallback(a.substring(i, l));
    } else {
        alert("wrong!<" + a + "> is not my GUID ;-)");
        return "1234";
    }
}

function ok() {
    alert("congratulations!");
}

CMShowingChanged
Tfrmcrackme_ApplicationEvents1Message
_Tfrmcrackme_FormCreate


about:blank#sptWBCallback:id=111;eventName=undefined

2 在脚本部分要求输入sn前部分字符必须是“Simpower91”

3、然后通过sptWBCallback进入dephi回调函数执行校验。

三、回调函数定位-sncheck

在函数Tfrmcrackme_FormCreate中注册:0049945C

HIDWORD(v5) = v1;
  LODWORD(v5) = &sncheck;
  Teengine::TTeeFunction::InternalSetPeriod(*(Teengine::TTeeFunction **)(v1 + 824), v5);
  Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 824), v6);

四、反调试

1、 在Tfrmcrackme_FormCreate中调用反调试相关函数477DDC

v10 = sub_477DDC((int)&cls_antiDebug_TAntiDebug, 1, 0);
  *(_DWORD *)(v1 + 828) = v10;

2、在Tfrmcrackme_FormCreate中貌似设置反调试函数:49978C

HIDWORD(v11) = v1;
  LODWORD(v11) = sub_49978C;
  Teengine::TTeeFunction::InternalSetPeriod(v10, v12, v13, v11);
  Idsyslogmessage::TIdSysLogMessage::SetTimeStamp(*(Idsyslogmessage::TIdSysLogMessage **)(v1 + 828), v14);

3、反调试函数477F64

int __usercall antiDebug5@<eax>(int a1@<eax>, int a2@<ebx>)
{
  int v2; // esi
  int v4; // [esp+0h] [ebp-408h]

  v2 = a1;
  Windows::FillMemory(&v4, 0x400u, 0);
  DeleteFiber(&v4);
  if ( GetLastError_0() == 0x57 )
  {
    a2 = 0;
    unknown_libname_1058(v2);
  }
  else
  {
    LOBYTE(a2) = 1;
    unknown_libname_1059(v2);
  }
  return a2;
}

4、反调试函数478110

int __usercall antiDebug3@<eax>(int a1@<eax>, int a2@<ebx>)
{
  if ( *(_BYTE *)(__readfsdword(0x30u) + 2) )
  {
    LOBYTE(a2) = 1;
    unknown_libname_1059(a1);
  }
  else
  {
    a2 = 0;
    unknown_libname_1058(a1);
  }
  return a2;
}

5、反调试47814C

int __usercall antiDebug4@<eax>(int a1@<eax>, int a2@<ebx>)
{
  if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )
  {
    LOBYTE(a2) = 1;
    unknown_libname_1059(a1);
  }
  else
  {
    a2 = 0;
    unknown_libname_1058(a1);
  }
  return a2;
}

6、反调试函数:477F64

int __usercall antiDebug5@<eax>(int a1@<eax>, int a2@<ebx>)
{
  int v2; // esi
  int v4; // [esp+0h] [ebp-408h]

  v2 = a1;
  Windows::FillMemory(&v4, 0x400u, 0);
  DeleteFiber(&v4);
  if ( GetLastError_0() == 0x57 )
  {
    a2 = 0;
    unknown_libname_1058(v2);
  }
  else
  {
    LOBYTE(a2) = 1;
    unknown_libname_1059(v2);
  }
  return a2;
}

7、反调试函数4780B8

int antiDebug7()
{
  HMODULE v0; // eax
  HANDLE v1; // eax

  v0 = LoadLibraryA("ntdll.dll");
  dword_49DCC0 = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress_0(v0, "NtSetInformationThread");
  v1 = GetCurrentThread();
  return dword_49DCC0(v1, 17, 0, 0);
}

8 、过掉反调试

#coding=utf-8
import struct
from idaapi import *
from idc import *
from idautils import *

dbg_write_memory(0x478070, '\xEB')#antiDebug2
refresh_debugger_memory()

dbg_write_memory(0x47812E, '\x90\x90')#antiDebug3
refresh_debugger_memory()

dbg_write_memory(0x47816C, '\x90\x90')#antiDebug4
refresh_debugger_memory()

dbg_write_memory(0x4781F4, '\x90\x90')#antiDebug6
refresh_debugger_memory()

dbg_write_memory(0x477F64, '\x33\xC0\xC3\x90')#antiDebug5
refresh_debugger_memory()

dbg_write_memory(0x4780B8, '\x33\xC0\xC3\x90')#antiDebug7
refresh_debugger_memory()

五、sncheck分析

1、异常处理函数注册

CODE:004994A7                 push    dword ptr fs:[eax]
CODE:004994AA                 mov     fs:[eax], esp
CODE:004994AD                 mov     eax, offset sub_475ED8
CODE:004994B2                 mov     edx, ds:off_49C3D8
CODE:004994B8                 mov     [edx+4], eax
CODE:004994BB                 mov     eax, ds:off_49C3D8
CODE:004994C0                 call    sub_46590C

其中sub_475ED8为异常处理函数

signed int __cdecl sub_475ED8(_DWORD *a1, int a2, int a3)
{
  signed int result; // eax

  result = 1;
  if ( !a1[1] )
  {
    if ( *a1 == 0xC0000096 )
    {
      *(_DWORD *)(a3 + 184) = realSnCheck;
      result = 0;
    }
    else if ( *a1 == 0xC00000FD )
    {
      *(_DWORD *)(a3 + 196) -= 256;
    }
  }
  return result;
}

其中 475EBC(realSnCheck)为异常0C0000096h处理函数

2、获取vmp指令

CODE:00499530                 call    GetVmpCode
CODE:00499535                 mov     [ebp+var_10], eax

其中 eax+4 为VMP指令地址,  eax+8为指令大小

3、产生异常,进入 475EBC(realSnCheck)

六、 realSnCheck( 475EBC )

1、realSnCheck  调用477778 与之前类似

     1)解密代码和指令

     2)判断加密的指令是否是真实代码,如果是,这将相应代码进行重定位动态执行

     3)如果是VMP指令,则进入虚拟机执行。

      本题没有真实代码,所有指令都为虚拟机代码

2、最终会进入476B8C(codeExe)函数中

七、codeExe函数

int __stdcall codeExe(int a1, int codeData, int retAddr, int dataOfFile, _DWORD *a5, int *a6, _DWORD *a7)
{
  struct globalInfo *global; // ebx
  struct vmpInfo *vmpInfo; // esi
  int v9; // edi
  int v10; // ecx
  int v11; // esi
  int v12; // edx
  int *v14; // [esp+1Ch] [ebp-380h]
  void *v15; // [esp+20h] [ebp-37Ch]
  int *v16; // [esp+24h] [ebp-378h]
  void *v17; // [esp+28h] [ebp-374h]
  int v18; // [esp+34h] [ebp-368h]
  int *v19; // [esp+38h] [ebp-364h]
  int v20; // [esp+3Ch] [ebp-360h]
  char v21; // [esp+40h] [ebp-35Ch]
  char v22; // [esp+140h] [ebp-25Ch]
  _BYTE *v23; // [esp+374h] [ebp-28h]
  char a4[6]; // [esp+37Ah] [ebp-22h]
  int v25; // [esp+380h] [ebp-1Ch]
  char *a2; // [esp+384h] [ebp-18h]
  int v27; // [esp+388h] [ebp-14h]
  int vmpDataSize; // [esp+38Ch] [ebp-10h]
  char *v29; // [esp+390h] [ebp-Ch]
  char *insSize; // [esp+394h] [ebp-8h]
  int v31; // [esp+398h] [ebp-4h]
  int vars0; // [esp+39Ch] [ebp+0h]

  v19 = 0;
  v18 = 0;
  v27 = 0;
  v16 = &vars0;
  v15 = &loc_477095;
  v14 = (int *)__readfsdword(0);
  __writefsdword(0, (unsigned int)&v14);
  global = sub_4760D0();
  v31 = vars0 + 8;
  global->retAddr = retAddr;
  *(_DWORD *)&a4[2] = global->curVmpCode1;
  a2 = *(char **)&a4[2];
  vmpInfo = global->vmpInfo;
  if ( !vmpInfo )
  {
    sub_4778E8(global);
    vmpInfo = global->vmpInfo;
  }
  a4[1] = 0;
  if ( vmpInfo->isVmpFlag == 1 )
  {
    a4[1] = 1;
    simvm_Init(global, v31, 16);
  }
  while ( 1 )
  {
    callCodeDecrypt((int)global, dataOfFile, *(int *)&a4[2], &vmpDataSize);
    while ( 1 )
    {
      while ( vmpInfo->isVmpFlag == 1 )
      {
        a4[0] = 0;
        v25 = (int)callVmpHandle(vmpInfo, codeData, *(_BYTE **)&a4[2], (int)a4);
        insSize = (char *)(v25 - *(_DWORD *)&a4[2]);
        if ( dataOfFile == global->curVmpCodeAddr )
        {
          GetNextValueByLen(&codeData, (unsigned __int8)a4[0]);
          global->curCodeAddr = codeData;
          GetNextValueByLen(&dataOfFile, insSize);
          global->curVmpCodeAddr = dataOfFile;
        }
        else
        {
          dataOfFile = global->curVmpCodeAddr;
          codeData = global->curCodeAddr;
        }
        callCodeDecrypt((int)global, dataOfFile, *(int *)&a4[2], &vmpDataSize);
      }
      if ( ifCode_simvm_orign(vmpInfo, &a2) != 1 )
        break;
      a2 = *(char **)&a4[2];
      a4[1] = 1;
      simvm_Init(global, v31, 16);
    }
    insSize = &a2[-*(_DWORD *)&a4[2]];
    a2 = *(char **)&a4[2];
    if ( !a4[1] )
      break;
    a4[1] = 0;
    vmpInfo->field_914 = 1;
  }
  if ( (signed int)insSize > 0 )
  {
    GetNextValueByLen(&dataOfFile, insSize);
    callCodeDecrypt((int)global, dataOfFile, *(int *)&a4[2], &vmpDataSize);
  }
  vmpDataSize = off_49DCA8(*(_DWORD *)&a4[2], vmpDataSize, 0, &v20, 4, v14);
  *a5 = vmpDataSize;
  v9 = unknown_libname_29(32 - vmpDataSize - 6);
  a2 = (char *)&global->field_1038 + v9;
  qmemcpy(a2, *(const void **)&a4[2], vmpDataSize);
  v14 = 0;
  *((_BYTE *)&global->field_1038 + v9 + vmpDataSize) = 104;
  unknown_libname_56(&v27, &v22);
  if ( (unsigned __int8)sub_476200(v27, global->cmpReg1) )
  {
    unknown_libname_56(&v27, &v21);
    if ( sub_466514(&str_FF_0[1], v27) != 1 )
    {
      insSize = (char *)sub_466514(&str___29[1], v27);
      v14 = &v27;
      sn11111(v27);
      System::__linkproc__ LStrCopy(v14);
      insSize = (char *)sub_466578(v27);
      if ( vmpDataSize - 1 >= 0 )
      {
        v10 = vmpDataSize;
        v29 = 0;
        do
        {
          v14 = 0;
          (v29++)[v9 + 4152 + (_DWORD)global] = -112;
          --v10;
        }
        while ( v10 );
      }
      insSize += vmpDataSize;
      vmpInfo->isVmpFlag1 = vmpInfo->isVmpFlag;
      if ( (signed int)insSize <= 0 )
      {
        insSize += dataOfFile - global->field_1070;
        v14 = (int *)&v29;
        callCodeDecrypt((int)global, global->field_1070, *(int *)&a4[2], &v29);
      }
      v23 = sub_475A90(vmpInfo, *(int *)&a4[2], (unsigned int)insSize);
      vmpInfo->isVmpFlag = vmpInfo->isVmpFlag1;
      if ( !v23 )
        global->curCodeAddr1 = global->curCodeAddr;
    }
  }
  else if ( sub_466514(&str_CALL_0[1], v27) == 1 )
  {
    unknown_libname_56(&v27, &v21);
    if ( sub_466514(&str_FF_0[1], v27) != 1 )
    {
      insSize = (char *)sub_466514(&str___29[1], v27);
      v29 = insSize - 1;
      v11 = (signed int)(insSize - 1) / 2;
      a2 = (char *)&global->field_1038 + v9 + v11;
      v14 = &v27;
      sn11111(v27);
      System::__linkproc__ LStrCopy(v14);
      insSize = (char *)sub_466578(v27);
      v29 = (char *)(&insSize[codeData] - ((char *)&global->field_1038 + v9));
      qmemcpy(a2, &v29, vmpDataSize - v11);
    }
    insSize = (char *)vmpDataSize;
    v23 = (_BYTE *)vmpDataSize;
  }
  else
  {
    insSize = (char *)vmpDataSize;
    v23 = (_BYTE *)vmpDataSize;
  }
  *a7 = insSize;
  insSize = (char *)retAddr;
  a2 = (char *)&global->field_1038 + v9 + vmpDataSize + 1;
  qmemcpy(a2, &insSize, 4u);
  v14 = 0;
  *((_BYTE *)&global->field_103C + v9 + vmpDataSize + 1) = -61;
  *a6 = (int)&global->field_1038 + v9;
  global->curCodeAddr = codeData + *a7;
  global->curVmpCodeAddr = (int)&v23[dataOfFile];
  unknown_libname_56(&v19, &v22);
  v14 = v19;
  unknown_libname_56(&v18, &v21);
  v12 = *a6;
  sub_47745C(vmpDataSize, v18, v14);
  __writefsdword(0, (unsigned int)v15);
  v17 = &loc_47709C;
  System::__linkproc__ LStrArrayClr(&v18, 2);
  return System::__linkproc__ LStrClr(&v27);
}

[培训]《安卓高级研修班》彻底搞定函数抽取型壳!现在报名得源码和安卓8.1脱壳机!10月20日深圳专场不见不散!


文章来源: https://bbs.pediy.com/thread-254697.htm
如有侵权请联系:admin#unsafe.sh