-
-
[原创]看雪 2017 CTF 第五题 DebugPort MD5
-
发表于: 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直播授课