首页
社区
课程
招聘
[原创]看雪 2017 CTF 第五题 DebugPort MD5
发表于: 2017-6-10 17:53 4590

[原创]看雪 2017 CTF 第五题 DebugPort MD5

HHHso 活跃值
22
2017-6-10 17:53
4590

一、通过驱动和客户进程逆向可驱动下述校验算法

char inputkey[6];

char salt[6]={1,1,2,3,4,5};

0==mbsicmp("888aeda4ab",MD5.hexdigest(MD5.hexdigest(reverse(tolow(inputkey))+salt)))

二、采用python枚举(海量工作需要策略,补刀策略(2)一个多小时命中)

(1)最初策略

#------- ------- ------- ------- ------- ------- ------- 

#python

import md5

saltv = [1,1,2,3,4,5]

chset = b"".join([chr(i) for i in xrange(ord('a'),ord('z')+1)])

nset = "1234567890"

chset = chset + nset   #-------[diff1]

for k0 in chset:       #-------[diff2]

  for k1 in chset:

    for k2 in chset:

      for k3 in chset:

        for k4 in chset:

          for k5 in chset:

            irk = b"".join([k0,k1,k2,k3,k4,k5])

            ik = b"".join([chr(ord(irk[i])+saltv[i]) for i in xrange(0,6)])

            m1 = md5.new()

            m2 = md5.new()

            m1.update(ik)

            m1d = m1.hexdigest()

            m2.update(m1d)

            m2d = m2.hexdigest()

            if m2d[2:0x0C] == "888aeda4ab":

              print irk

#------- ------- ------- ------- ------- ------- ------- 

(2)上述策略单进程跑一个多小时不见好,于是补充策略

将#-------[diff2]修改为下述后,开另一个进程协力跑

for k0 in chset[::-1]:       #-------[diff2]

其意思就是将chset反转,即

策略(1)从'abcdefghijklmnopqrstuvwxyz1234567890'顺着遍历

策略(2)从'0987654321zyxwvutsrqponmlkjihgfedcba'反着遍历

如此在得到有效结果前,两者向中间遍历靠拢,在完成全部遍历前时间可以缩减一半

实际上策略(2)在一个小时左右就得到了结果 "6891us" > 反过来是 "su1986"

#------- ------- ------- ------- ------- ------- ------- 

如果(1)(2)长时间还不出结果或遍历完

还可以(1)(2)基础上继续将#-------[diff2]做下述改动,另外加两个进程跑,时间可以再缩减一半

//(3)for k0 in chset[:chset.__len__()]: 

//(4)for k0 in chset[::-1][chset.__len__():]: 

前提是机器处理核心足够,可以采用多线程或进程的方式。

hashcat能否派上用场未知。

三、庖丁解牛

从程序执行入口到注册输入对话框初始化函数

在入口函数 0040C073 start 中,调用c全局对象初始化

0040C118 call    __cinit

在__cinit函数中,有c全局初始化函数数组

.text:0040C228 push    offset Hi_z_dword_42D0A8

.text:0040C22D push    offset Hi_x_dword_42D000

.text:0040C232 call    __initterm

在Hi_x_dword_42D000与Hi_z_dword_42D0A8之间,有下述CWinApp全局变量初始化函数

.data:0042D000 Hi_x_dword_42D000 dd 0                  ; DATA XREF: __cinit+1Fo

.data:0042D004                 dd offset sub_417D76

//省略

.data:0042D040                 dd offset Hi_global_CWinAppObj_init_sub_4010E0 //CWinApp全局变量初始化函数

//省略

.data:0042D0A8 Hi_z_dword_42D0A8 dd 0

CWinApp全局变量初始化函数

int Hi_global_CWinAppObj_init_sub_4010E0()

{

  Hi_CWinApp_ctor_sub_4010F0(); //CWinApp对象构造函数

  return atexit(Hi_WinApp_dtor_sub_401110);//CWinApp对象析构函数,在程序退出时调用释放资源

}

在CWinApp构造函数中,全局静态CWinApp对象(变量)位于Hi_CWinAppObj_dword_4312B8出

由下述初始化过程可知虚表为Hi_CWinApp_vft_off_423550

int *Hi_CWinApp_ctor_sub_4010F0()

{

  CWinApp::CWinApp(0);

  Hi_CStringList_sub_415E6C(10);

  Hi_CWinAppObj_dword_4312B8 = (int)&Hi_CWinApp_vft_off_423550;

  return &Hi_CWinAppObj_dword_4312B8;

}

在CWinApp的虚拟函数表Hi_CWinApp_vft_off_423550中,可定位到CWinApp对象初始化函数,

位于CWinApp:Run()函数之上的位置(MFC不成文规定,实际是CWinApp类的成员定义顺序决定)

.rdata:004235A0 dd offset Hi_CWinApp_initialize_sub_401120 //CWinApp对象初始化函数

.rdata:004235A4 dd offset CWinApp::Run(void)

在CWinApp对象初始化函数Hi_CWinApp_initialize_sub_401120中,有注册码输入框构造函数调用

.text:00401149 push    0

.text:0040114B call    Hi_CDialogKey_ctor_sub_4012D0

在 Hi_CDialogKey_ctor_sub_4012D0 

.text:004012D0 push    0FFFFFFFFh

.text:004012D2 push    offset SEH_4012D0

.text:004012D7 mov     eax, large fs:0

.text:004012DD push    eax

.text:004012DE mov     large fs:0, esp

.text:004012E5 push    ecx

.text:004012E6 mov     eax, [esp+10h+arg_0]

.text:004012EA push    esi

.text:004012EB push    edi

.text:004012EC mov     esi, ecx

.text:004012EE push    eax

.text:004012EF push    66h   //-----------------------注册对话框资源ID

.text:004012F1 mov     [esp+20h+var_10], esi

.text:004012F5 call    CDialog::CDialog(uint,CWnd *)

.text:004012FA mov     ecx, Hi_COccManager_typeinfo_off_42D540 //CString初始化

.text:00401300 mov     [esp+18h+var_4], 0

.text:00401308 mov     [esi+5Ch], ecx

.text:0040130B mov     edx, Hi_COccManager_typeinfo_off_42D540//CString初始化

.text:00401311 lea     ecx, [esi+68h]  ; this

.text:00401314 mov     [ecx], edx

.text:00401316 mov     eax, Hi_COccManager_typeinfo_off_42D540//CString初始化

.text:0040131B lea     edi, [esi+6Ch]

.text:0040131E mov     [edi], eax

.text:00401320 push    offset Hi_lpsznull_431398 ; lpString

.text:00401325 mov     byte ptr [esp+1Ch+var_4], 3

.text:0040132A mov     dword ptr [esi], offset Hi_CDialog_inputkey_vft_off_423760 //注册对话框虚表

.text:00401330 call    CString::operator=(char const *)

.text:00401335 push    offset Hi_lpsznull_431398 ; lpString

.text:0040133A mov     ecx, edi        ; this

.text:0040133C call    CString::operator=(char const *)

.text:00401341 call    AfxGetModuleState(void)

.text:00401346 call    AfxGetModuleState(void)

.text:0040134B mov     eax, [eax+0Ch]

.text:0040134E push    80h             ; lpIconName

.text:00401353 push    eax             ; hInstance

.text:00401354 call    ds:LoadIconA

.text:0040135A mov     ecx, [esp+18h+var_C]

.text:0040135E mov     [esi+70h], eax

.text:00401361 mov     eax, esi

.text:00401363 pop     edi

.text:00401364 pop     esi

.text:00401365 mov     large fs:0, ecx

.text:0040136C add     esp, 10h

.text:0040136F retn    4

.text:0040136F Hi_CDialogKey_ctor_sub_4012D0 endp

有上述初始化过程,可以得到注册对话框类对象的内存结构(包括虚表)

Hi_CDialog_inputkey_ctor_sub_4012D0

 .00hww Hi_CDialog_inputkey_vft_off_423760 ******************

 .5Chww.saltMD5_hexdigest_str = saltMD5(inKey)

 .64hww.isLoadSysDirverOK

 .68hww.inKey = CStr inputkey

 .6Chww.SuccessTips = CStr 

 .70hww = LoadIconA(MODULE_STATE.m_bDLL,0x80)

 

在虚拟函数表中,定位对话框初始化函数

.rdata:0042381C dd offset Hi_CDialogKey_OnInitDiaglog_sub_4013E0

在注册码输入对话框初始化函数Hi_CDialogKey_OnInitDiaglog_sub_4013E0中有以下主要操作

1.通过Hi_is_WinVersion_5_1_sub_402210函数检测系统是否为xp (5.1)

2.获取程序执行路径,构建驱动释放文件全路径名(即在当前目录释放vmxdr.sys驱动)

3.加载安装上述释放的驱动并启动驱动服务

4.删除释放的驱动文件并启动5个反调式线程(反调试线程具有控制开启驱动MD5功能作用,不能简单屏蔽处理)

1.通过Hi_is_WinVersion_5_1_sub_402210函数检测系统是否为xp (5.1)

.text:004014C0 call    Hi_is_WinVersion_5_1_sub_402210

.text:004014C5 mov     ebx, ds:PostMessageA

.text:004014CB test    eax, eax

.text:004014CD jnz     short loc_4014E9

.text:004014CF push    0               ; int

.text:004014D1 push    0               ; uType

.text:004014D3 push    offset Text     ; "请在xp平台中重新运行CrackMe"

.text:004014D8 call    Hi_kindly_reminder_sub_41DB51

2.获取程序执行路径,构建驱动释放文件全路径名(即在当前目录释放vmxdr.sys驱动)

.text:004014E9 lea     eax, [esp+12Ch+loc_vmxdrv_sys_path]

.text:004014ED push    eax             ; lpBuffer

.text:004014EE push    104h            ; nBufferLength

.text:004014F3 call    ds:GetCurrentDirectoryA

.text:004014F9 mov     edi, offset aVmxdrv_sys ; "\\vmxdrv.sys"

//省略

.text:00401566 call    Hi_release_P2ResType_P3ResId_to_P1FullFilePath_sub_401F20("./vmxdrv.sys",ResType:"DRV",ResID:0x84)

.text:0040156B test    eax, eax

.text:0040156D jnz     short loc_401587

.text:0040156F push    eax             ; int

.text:00401570 push    eax             ; uType

.text:00401571 push    offset asc_42D114 ;

.text:00401571 ; 驱动释放失败,

.text:00401571 ; 请在xp平台中重新运行CrackMe

.text:00401576 call    Hi_kindly_reminder_sub_41DB51

上述释放驱动函数过程如下

Hi_release_P2ResType_P3ResId_to_P1FullFilePath_sub_401F20{

 hFileSys = CreateFileA(lpszReleaseFullFilePath, 0x40000000u, 2u, 0, 2u, 0x80u, 0);

 if hFileSys != HANDLE_TYPE_INVALID:

    hResInfo = FindResourceA(0, lpResType, lpResId);

    hResDataPtr = LoadResource(0, hResInfo);

    cbResDataSize = SizeofResource(0, hResInfo);

    WriteFile(hFileSys, hResDataPtr, cbResDataSize, &NumberOfBytesWritten, 0);

    CloseHandle(hFileSys);

}

3.加载安装上述释放的驱动并启动驱动服务

.text:00401587 lea     edx, [esp+12Ch+loc_vmxdrv_sys_path]

.text:0040158B mov     ecx, ebp

.text:0040158D push    edx             ; lpVmxdrvSysFileName

.text:0040158E push    offset ServiceName ; "vmxdrv"

.text:00401593 call    Hi_Load_CreateOrOpenServiceAndStart_P1ServiceName_P2SysFileName_sub_401AA0 //参考后续分析

.text:00401598 test    eax, eax

.text:0040159A mov     [ebp+64h], eax   // .64hww.isLoadSysDirverOK 逆向对象结构

.text:0040159D jnz     short loc_4015C5

.text:0040159F push    offset ServiceName ; "vmxdrv"

.text:004015A4 mov     ecx, ebp

.text:004015A6 call    Hi_unLoad_OpenAndDeleteService_P1Servicename_sub_401C40

.text:004015AB push    0               ; int

.text:004015AD push    0               ; uType

.text:004015AF push    offset asc_42D0E0 ;

.text:004015AF ; 驱动加载失败,

.text:004015AF ; 请在xp平台中重新运行CrackMe

.text:004015B4 call    Hi_kindly_reminder_sub_41DB51

服务安装与启动过程如下

Hi_Load_CreateOrOpenServiceAndStart_P1ServiceName_P2SysFileName_sub_401AA0(lpVmxdrvSysFileName){

    GetFullPathNameA(lpVmxdrvSysFileName, 0x100u, &FullPathName, 0);

    hSCManager = OpenSCManagerA(0, 0, 0xF003Fu);

    if hSCManager == 0:

      Hi_fsprint_sub_40B946("OpenSCManager() Faild %d ! \n",GetLastError())

    else:

      Hi_fsprint_sub_40B946("OpenSCManager() ok ! \n")

      hService = CreateServiceA(hSCManager, lpServiceName, lpServiceName, 0xF01FFu, 1u, 3u, 0, &FullPathName, 0, 0, 0, 0, 0);

      if hService != 0:

        Hi_fsprint_sub_40B946("CrateService() ok ! \n")

      else:

        lastEr = GetLastError()

        if (lastEr == ERROR_IO_PENDING) or (lastEr == ERROR_SERVICE_EXISTS):

          Hi_fsprint_sub_40B946("CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! ")

          hService = OpenServiceA(v2, lpServiceName, 0xF01FFu);

          if hService == 0:

            Hi_fsprint_sub_40B946("OpenService() Faild %d ! \n",GetLastError())

            goto exitreturn;

          else:

            Hi_fsprint_sub_40B946("OpenService() ok ! \n")

        else:

         Hi_fsprint_sub_40B946("OpenService() Faild %d ! \n",GetLastError())

         goto exitreturn;

      //"OpenService() or CreateService() OK"

      if 0==StartServiceA(hService, 0, 0) ):

        lastEr = GetLastError()

        if (lastEr == ERROR_IO_PENDING):

          Hi_fsprint_sub_40B946("StartService() Faild ERROR_IO_PENDING !")

        elif (lastEr == ERROR_SERVICE_ALREADY_RUNNING):

          Hi_fsprint_sub_40B946("StartService() Faild ERROR_SERVICE_ALREADY_RUNNING !")

    exitreturn:

      if hSCManager:

        CloseServiceHandle(hSCManager);

}

4.删除释放的驱动文件并启动5个反调式线程(反调试线程具有控制开启驱动MD5功能作用,不能简单屏蔽处理)

.text:004015C5 lea     ecx, [esp+12Ch+loc_vmxdrv_sys_path]

.text:004015C9 push    ecx             ; lpFileName

.text:004015CA call    ds:DeleteFileA

.text:004015D0 push    5

.text:004015D2 mov     ecx, ebp

.text:004015D4 call    Hi_fork_1to20_ThreadsToCtrl_sys_TurnOnMD5SaltFlag_and_antidbg_sub_402250

Hi_fork_1to20_ThreadsToCtrl_sys_TurnOnMD5SaltFlag_and_antidbg_sub_402250(threadCount:5){

  check: 1 <= threadCount <= 20

  do{

    AfxBeginThread(Hi_control_sys_to_TurnOnTheMD5SaltFlag_and_clear_DebugPort_of_process_sub_4022A0,0,0,0,0,0);

    Sleep(100)

  }while(--threadCount)

}

上述并发5个驱动控制线程,线程函数为Hi_control_sys_to_TurnOnTheMD5SaltFlag_and_clear_DebugPort_of_process_sub_4022A0

其每3秒向驱动发出一个0x222004控制信号,vmxdrv.sys 控制响应函数参考随后的Hi_IRP_MJ_DEVICE_CONTROL_sub_1071A

并进一步影响后续写和读取驱动设备功能

Hi_control_sys_to_TurnOnTheMD5SaltFlag_and_clear_DebugPort_of_process_sub_4022A0{

  memset(&OutBuffer, 0, 0x100);

  BytesReturned = 0;

  while ( 1 )

  {

    if HANDLE_TYPE_INVALID == CreateFileA(FileName, 0xC0000000, 0, 0, 3u, 0x80u, 0);

      break;

    DeviceIoControl(v0, 0x222004u, 0, 0, &OutBuffer, 0x100u, &BytesReturned, 0);

    CloseHandle(v0);

    Sleep(3000);

  }

  return 0;

}

在vmxdrv.sys的 IRP_MJ_DEVICE_CONTROL响应函数 由用户进程反调试线程控制调用

int __stdcall Hi_IRP_MJ_DEVICE_CONTROL_sub_1071A(int a1, PIRP Irp)

{

  switch ( *(_DWORD *)(Irp->Tail.Overlay.PacketType + 12) )

  {

    case 0x222004:

      Hi_MD5SaltFlagSwitch_dword_114D8 = 1; //算法功能开关

      Hi_CurrentProcess_dword_114E0 = (int)IoGetCurrentProcess();

      Hi_clear_DebugPort_of_process_sub_10486(); //反调试功能函数调用

      break;

    case 0x222008:

      DbgPrint("%s\n", Irp->AssociatedIrp.IrpCount);

      break;

    case 0x22200C:

      DbgPrint("bye!\n");

      break;

    default:

      DbgPrint("unkonw code!\n");

      break;

  }

  Irp->IoStatus.Status = 0;

  Irp->IoStatus.Information = 0;

  IofCompleteRequest(Irp, 0);

  return 0;

}

在控制信号0x222004处理分支中:

(1)打开MD5正确加盐标记Hi_MD5SaltFlagSwitch_dword_114D8=1,此为算法功能开关

   (1.a)该开关决定驱动Hi_IRP_MJ_WRITE_sub_1061C响应时是否执行MD5哈希摘要处理

   (1.b)同时决定在MD5哈希摘要处理加盐时,对key[0]是否执行正确的"加1"加盐处理

(2)通过Hi_clear_DebugPort_of_process_sub_10486检测清除进程调试端口(参考后续分析)

(1.a)算法功能开关Hi_MD5SaltFlagSwitch_dword_114D8影响机制

(1.a.1)在写驱动设备响应函数 Hi_IRP_MJ_WRITE_sub_1061C 中,由客户进程WriteFile控制响应

判断Hi_MD5SaltFlagSwitch_dword_114D8开关是否打开而决定是否实现加盐摘要处理

   if ( Hi_MD5SaltFlagSwitch_dword_114D8 )

    {

      Hi_MD5salt_inkey_outDigest_sub_104B6(lpkey, (int)Hi_MD5Digest_byte_114C8);

      Hi_MD5DigestOK_dword_114DC = 1;

    }

(1.a.2)上述流程中,加盐MD5处理函数如下,

Hi_MD5salt_inkey_outDigest_sub_104B6(IN lpkey,OUT outMD5SaltDigest)

{

  char loc_keybuf[0x10];

  keylen = strlen(lpkey)

  if keylen <= 0x10:

    memcpy(loc_keybuf, lpkey, keylen);

    //Add Salt  [1,1,2,3,4,5,...]

    if ( Hi_MD5SaltFlagSwitch_dword_114D8 )

      loc_keybuf[0]++; //Hi_MD5SaltFlagSwitch_dword_114D8 开关决定首字节加1

    for(int i = 0,i < keylen,i++){//

      loc_keybuf[i]+=i

    }

    //

    Hi_MD5Init_sub_108B2(&loc_MD5ctx);


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回