作者:绿林科技
QQ:1473656864
大家调试的时候都可以知道,盛*大游戏(泡泡堂)的外壳是Themida,可以用OD创建进程,但不能附加游戏,本来想只用一个驱动文件来过保护的,但在Win 7的HOOK里判断进程名,会出问题,导致系统奔溃,而这在Win XP却不会,个人比较郁闷!
所以用了另一个办法来判断指定进程,就是进程的PID,这个比较唯一。Ring 3创建进程,并挂起进程,然后安装驱动,向驱动传递游戏的进程PID,然后驱动就执行HOOK,通过进程PID来判断是否要跳转。
功能,可以用CE打开进程,读取,修改进程,用CE的调试器附加进程。
最后说明下,这个必须在Win 7下,且你的SSDT表是干净的,没有腾讯的QQProtect.sys这个驱动文件在执行,SSDT没有被HOOK。可以用安全工具把QQProtect.sys由系统引导启动设置成手动启动。
下面贴上代码:
Ring 3层的MFC程序:
安装驱动的函数:
void CDriverProtectDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
InstallCWinThread = AfxBeginThread(InstallDriverThread,this);
}
安装驱动的函数:
BOOL CDriverProtectDlg::LoadNTDriver(wchar_t* lpszDriverName, wchar_t* lpszDriverPath)
{
wchar_t szDriverImagePath[256];
::GetFullPathNameW(strPath,256,szDriverImagePath,NULL);
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄
SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄
hServiceMgr = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if( hServiceMgr == NULL )
{
m_DriverINFORMATION.SetWindowTextW(L"打开服务失败!");
bRet = FALSE;
goto BeforeLeave;
}
else
{
OpenSCManager成功
m_DriverINFORMATION.SetWindowTextW(L"OpenSCManager成功!");
}
//创建驱动所对应的服务
hServiceDDK = CreateServiceW(hServiceMgr,lpszDriverName,lpszDriverName,SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_IGNORE,szDriverImagePath,NULL,NULL,NULL,NULL,NULL);
DWORD dwRtn;
//判断服务是否失败
if (hServiceDDK == NULL)
{
dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
{
//由于其他原因创建服务失败
strError.Format(L"%d",dwRtn);
m_DriverINFORMATION.SetWindowTextW(L"由于其他原因创建服务失败!" + strError);
bRet = FALSE;
goto BeforeLeave;
}
else
{
m_DriverINFORMATION.SetWindowTextW(L"服务创建失败,是由于服务已经创立过!");
}
// 驱动程序已经加载,只需要打开
hServiceDDK=::OpenServiceW(hServiceMgr,lpszDriverName, SERVICE_ALL_ACCESS);
if( hServiceDDK == NULL )
{
//如果打开服务也失败,则意味错误
dwRtn = GetLastError();
strError.Format(L"%d",dwRtn);
m_DriverINFORMATION.SetWindowTextW(L"如果打开服务也失败,则意味错误!" + strError);
bRet = FALSE;
goto BeforeLeave;
}
else
{
m_DriverINFORMATION.SetWindowTextW(L"打开服务成功!");
}
}
else
{
m_DriverINFORMATION.SetWindowTextW(L"创建服务成功!");
}
bRet= ::StartServiceW( hServiceDDK, NULL, NULL );
if( !bRet )
{
DWORD dwRtn = GetLastError();
if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING )
{
strError.Format(L"%d",dwRtn);
m_DriverINFORMATION.SetWindowTextW(L"开启此项服务失败!");
bRet = FALSE;
goto BeforeLeave;
}
else
{
if( dwRtn == ERROR_IO_PENDING )
{
//设备被挂住
m_DriverINFORMATION.SetWindowTextW(L"设备被挂住!");
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务已经开启
m_DriverINFORMATION.SetWindowTextW(L"打开服务失败,因为服务已经开启!");
bRet = TRUE;
goto BeforeLeave;
}
}
}
bRet = TRUE;
BeforeLeave:
//离开前关闭打开的句柄
if(hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if(hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
安装驱动的线程:
// 安装驱动的线程函数
UINT __cdecl CDriverProtectDlg::InstallDriverThread(LPVOID pParam)
{
CDriverProtectDlg* pDlg = NULL;
pDlg = (CDriverProtectDlg*)pParam;
pDlg->UpdateData(TRUE);
if (pDlg->strPath.IsEmpty())
{
AfxMessageBox(L"请选择驱动路径!");
return 0;
}
if (pDlg->strrGamePath.IsEmpty())
{
AfxMessageBox(L"请选择游戏路径!");
return 0;
}
if (!pDlg->LoadNTDriver(L"HelloDDK",pDlg->strPath.GetBuffer()))
{
pDlg->UnloadNTDriver(L"HelloDDK");
pDlg->LoadNTDriver(L"HelloDDK",pDlg->strPath.GetBuffer());
}
HANDLE hDevice =
::CreateFileW(L"\\\\.\\HelloDDK",
GENERIC_READ | GENERIC_WRITE,
0, // share mode none
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ); // no template
if (hDevice == INVALID_HANDLE_VALUE)
{
pDlg->m_DriverINFORMATION.SetWindowTextW(L"打开驱动错误!");
return 1;
}
DWORD Pid = pDlg->TransferProcessID(pDlg->strrGamePath.GetBuffer());
int a = (int)Pid;
UCHAR* InputBuffer = new UCHAR[a];
UCHAR* OutputBuffer= new UCHAR[a];
BOOL bRet;
DWORD dwOutput;
//输入缓冲区作为输入,输出缓冲区作为输出
bRet = DeviceIoControl(hDevice, IOCTL_TEST1, InputBuffer, a, OutputBuffer, a, &dwOutput, NULL);
if (bRet)
{
pDlg->m_DriverINFORMATION.SetWindowTextW(L"开启保护成功!");
}
CloseHandle(hDevice);
delete []InputBuffer;
delete []OutputBuffer;
//AfxEndThread(0);
ResumeThread(pDlg->ProcessMainThread);
pDlg = NULL;
return 0;
}
ring 0层的驱动函数的代码:
DeviceIoControl的派遣函数:
#pragma PAGEDCODE
NTSTATUS HelloDDKDeviceIOControl(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("Enter HelloDDKDeviceIOControl\n"));
//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
ULONG info = 0;
switch (code)
{ // process request
case IOCTL_TEST1:
{
KdPrint(("IOCTL_TEST1\n"));
GameProcessID = (int)cbin;
//KIRQL oldirql;
//ASSERT(KeGetCurrentIrql()<=DISPATCH_LEVEL);
//KeRaiseIrql(DISPATCH_LEVEL,&oldirql);
HookNtOpenProcess();
HookNtReadVirtualMemory();
HookNtWriteVirtualMemory();
HookNtClose();
HookNtProtectVirtualMemory();
HookPsSuspendThread();
HookNtGetContextThread();
HookNtQueryPerformanceCounter();
HookNtSetContextThread();
HookKiAttachProcess();
HookNtDeviceIoControlFile();
//KeLowerIrql(oldirql);
//缓冲区方式IOCTL
//显示输入缓冲区数据
UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
这个为全局变量 int GameProcessID;
下面是绕过的钩子函数(部分):
#include "HookNtDeviceIoControlFile.h"
#include "Function.h"
extern int GameProcessID;
int SrcNtDeviceIoControlFileAddr;
int OldCallNtDeviceIoControlFileAddr;
int NewCallNtDeviceIoControlFileAddr;
int JmpNtDeviceIoControlFileAddr;
int Index;
int NtDeviceIoControlFileAddr;
/*
0x81C7948C | 55 | push ebp |
0x81C7948D | 8BEC | mov ebp, esp |
0x81C7948F | 6A 01 | push 1 |
0x81C79491 | FF75 2C | push dword ptr [ebp+2C] |
0x81C79494 | FF75 28 | push dword ptr [ebp+28] |
0x81C79497 | FF75 24 | push dword ptr [ebp+24] |
0x81C7949A | FF75 20 | push dword ptr [ebp+20] |
0x81C7949D | FF75 1C | push dword ptr [ebp+1C] |
0x81C794A0 | FF75 18 | push dword ptr [ebp+18] |
0x81C794A3 | FF75 14 | push dword ptr [ebp+14] |
0x81C794A6 | FF75 10 | push dword ptr [ebp+10] |
0x81C794A9 | FF75 0C | push dword ptr [ebp+C] |
0x81C794AC | FF75 08 | push dword ptr [ebp+8] |
0x81C794AF | E8 BD90FBFF | call 81C32571 |
0x81C794B4 | 5D | pop ebp |
0x81C794B5 | C2 2800 | retn 28 |
*/
static __declspec(naked) void MyNtDeviceIoControlFile()
{
__asm
{
push ebp
mov ebp, esp
push 1
push dword ptr [ebp+0x2C]
push dword ptr [ebp+0x28]
push dword ptr [ebp+0x24]
push dword ptr [ebp+0x20]
push dword ptr [ebp+0x1C]
push dword ptr [ebp+0x18]
push dword ptr [ebp+0x14]
push dword ptr [ebp+0x10]
push dword ptr [ebp+0xC]
push dword ptr [ebp+0x8]
}
if (PanDuanProcessID()==GameProcessID)
{
__asm
{
jmp NewCallNtDeviceIoControlFileAddr
call OldCallNtDeviceIoControlFileAddr
jmp JmpNtDeviceIoControlFileAddr
}
}
else
{
__asm
{
call OldCallNtDeviceIoControlFileAddr
jmp JmpNtDeviceIoControlFileAddr
}
}
}
void HookNtDeviceIoControlFile()
{
char code[1]={(char)0xE8};
Index=GetFunctionIndex(ZwDeviceIoControlFile);
SrcNtDeviceIoControlFileAddr=SearchFeature(GetSSDTFunctionAddr(Index),code,1);
OldCallNtDeviceIoControlFileAddr=GetCallAddr(SrcNtDeviceIoControlFileAddr);
NewCallNtDeviceIoControlFileAddr=SrcNtDeviceIoControlFileAddr-1;
JmpNtDeviceIoControlFileAddr=SrcNtDeviceIoControlFileAddr+4;
NtDeviceIoControlFileAddr=SSDTHookEngine(Index,(int)MyNtDeviceIoControlFile);
}
void UnHookNtDeviceIoControlFile()
{
SSDTUnHookEngine(Index,NtDeviceIoControlFileAddr);
}
经研究KiAttachProcess没有检测
#include "HookKiAttachProcess.h"
#include "Function.h"
extern int GameProcessID;
int SrcKiAttachProcessAddr;
int KiMoveApcState;
int KiInSwapSingleProcess;
int HvlLongSpinCountMask;
int HvlEnlightenments;
int HvlNotifyLongSpinWait;
int _imp_KfLowerIrql;
int KiSwapProcess;
int SrcKeStackAttachProcessAddr;
int KeStackAttachProcessCall_1;
int KeStackAttachProcessCall_2;
int SrcKeAttachProcessAddr;
int KeAttachProcessCall_1;
static __declspec(naked) void MyKiAttachProcess()
{
// if (PanDuanProcessID()==GameProcessID)
// {
// __asm
// {
// mov edi,edi
// push ebp
// mov ebp,esp
// push ebx
// mov ebx,dword ptr [ebp+8]
// push esi
// mov esi,eax
// mov eax,dword ptr [ebp+10h]
// push edi
// lea edi,[esi+40h]
// push edi
// call KiMoveApcState
// mov ecx,dword ptr [ebp+10h]
// mov dword ptr [edi+4],edi
// mov dword ptr [edi],edi
// lea eax,[esi+48h]
// mov dword ptr [eax+4],eax
// mov dword ptr [eax],eax
// lea eax,[esi+170h]
// mov byte ptr [esi+54h],0
// mov byte ptr [esi+55h],0
// mov byte ptr [esi+56h],0
// cmp ecx,eax
// jne KiAttachProcess_52
//KiAttachProcess_3f:
// mov dword ptr [esi+168h],eax
// mov dword ptr [esi+16Ch],edi
// mov byte ptr [esi+134h],1
//KiAttachProcess_52:
// push 8
// pop eax
// lea edx,[ebx+74h]
// lock xadd dword ptr [edx],eax
// mov dword ptr [ebp+8],eax
// mov eax,dword ptr [ebp+8]
// test al,7
// jne KiAttachProcess_76
//KiAttachProcess_66:
// mov dword ptr [esi+50h],ebx
// xor eax,eax
// add esi,60h
// lock and dword ptr [esi],eax
// push dword ptr [ecx+10h]
// jmp KiAttachProcess_cb
//KiAttachProcess_76:
// lea edi,[esi+60h]
// xor eax,eax
// mov ecx,edi
// lock and dword ptr [ecx],eax
// mov eax,ebx
// call KiInSwapSingleProcess
// and dword ptr [ebp+8],0
// jmp KiAttachProcess_b4
//KiAttachProcess_8d:
// inc dword ptr [ebp+8]
// mov eax,dword ptr [ebp+8]
// test dword ptr [HvlLongSpinCountMask],eax
// jne KiAttachProcess_ac
//KiAttachProcess_9b:
// test byte ptr [HvlEnlightenments],40h
// je KiAttachProcess_ac
//KiAttachProcess_a4:
// push eax
// call HvlNotifyLongSpinWait
// jmp KiAttachProcess_ae
//KiAttachProcess_ac:
// pause
//KiAttachProcess_ae:
// mov eax,dword ptr [edi]
// test eax,eax
// jne KiAttachProcess_8d
//KiAttachProcess_b4:
// xor eax,eax
// mov ecx,edi
// inc eax
// xchg eax,dword ptr [ecx]
// test eax,eax
// jne KiAttachProcess_8d
//KiAttachProcess_bf:
// mov dword ptr [esi+50h],ebx
// lock and dword ptr [edi],eax
// mov eax,dword ptr [ebp+10h]
// push dword ptr [eax+10h]
//KiAttachProcess_cb:
// push ebx
// call KiSwapProcess
// mov cl,byte ptr [ebp+0Ch]
// call dword ptr [_imp_KfLowerIrql]
// pop edi
// pop esi
// pop ebx
// pop ebp
// ret 0Ch
// }
// }
// else
// {
// __asm
// {
// jmp SrcKiAttachProcessAddr
// }
// }
__asm
{
jmp SrcKiAttachProcessAddr
}
}
void HookKiAttachProcess()
{
char code[1]={(char)0xE8};
SrcKiAttachProcessAddr=Win7GetKiAttachProcessAddr();
KiMoveApcState = GetCallAddr(SearchFeature(SrcKiAttachProcessAddr,code,1));
//DbgPrint("SrcKiAttachProcessAddr = %x",SrcKiAttachProcessAddr);
//DbgPrint("KiMoveApcState = %x",KiMoveApcState);
KiInSwapSingleProcess = GetCallAddr(SearchFeature(SearchFeature(SrcKiAttachProcessAddr,code,1)+1,code,1));
//DbgPrint("KiInSwapSingleProcess = %x",KiInSwapSingleProcess);
HvlLongSpinCountMask=(*((int*)(SrcKiAttachProcessAddr+0x8D+8)));
//DbgPrint("HvlLongSpinCountMask = %x",HvlLongSpinCountMask);
HvlEnlightenments=(*((int*)(SrcKiAttachProcessAddr+0x9B+2)));
//DbgPrint("HvlEnlightenments = %x",HvlEnlightenments);
HvlNotifyLongSpinWait=GetCallAddr(SearchFeature(SearchFeature(SearchFeature(SrcKiAttachProcessAddr,code,1)+1,code,1)+1,code,1));
//DbgPrint("HvlNotifyLongSpinWait = %x",HvlNotifyLongSpinWait);
KiSwapProcess = GetCallAddr(SearchFeature(SearchFeature(SearchFeature(SearchFeature(SrcKiAttachProcessAddr,code,1)+1,code,1)+1,code,1)+1,code,1));
//DbgPrint("KiSwapProcess = %x",KiSwapProcess);
_imp_KfLowerIrql=(*((int*)(SrcKiAttachProcessAddr+0xCB+11)));
//DbgPrint("_imp_KfLowerIrql = %x",_imp_KfLowerIrql);
SrcKeStackAttachProcessAddr=GetFunCtionAddr(L"KeStackAttachProcess");
//DbgPrint("SrcKeStackAttachProcessAddr = %x",SrcKeStackAttachProcessAddr);
KeStackAttachProcessCall_1=SearchFeature(SearchFeature(SearchFeature(SrcKeStackAttachProcessAddr,code,1)+1,code,1)+1,code,1);
//DbgPrint("KeStackAttachProcessCall_1 = %x",KeStackAttachProcessCall_1);
KeStackAttachProcessCall_2=SearchFeature(SearchFeature(SearchFeature(SearchFeature(SrcKeStackAttachProcessAddr,code,1)+1,code,1)+1,code,1)+1,code,1);
//DbgPrint("KeStackAttachProcessCall_2 = %x",KeStackAttachProcessCall_2);
SrcKeAttachProcessAddr=GetFunCtionAddr(L"KeAttachProcess");
//DbgPrint("SrcKeAttachProcessAddr = %x",SrcKeAttachProcessAddr);
KeAttachProcessCall_1=SearchFeature(SearchFeature(SrcKeAttachProcessAddr,code,1)+1,code,1);
//DbgPrint("KeAttachProcessCall_1 = %x",KeAttachProcessCall_1);
CallHook(KeStackAttachProcessCall_1,(int)MyKiAttachProcess);
CallHook(KeStackAttachProcessCall_2,(int)MyKiAttachProcess);
CallHook(KeAttachProcessCall_1,(int)MyKiAttachProcess);
}
void UnHookKiAttachProcess()
{
CallHook(KeStackAttachProcessCall_1,SrcKiAttachProcessAddr);
CallHook(KeStackAttachProcessCall_2,SrcKiAttachProcessAddr);
CallHook(KeAttachProcessCall_1,SrcKiAttachProcessAddr);
}
工程在Win 7的VS2010编译环境测试通过,因为有硬编码,所以不适合Win XP,因为64位的系统的SSDT表不可写,所以不适合64位的系统。
Visual C/C++,汇编语言,驱动交流群:177822398、 177822108
附注:一直想找个合适的安全类编程开发测试的工作
下面贴上附件:
http://pan.baidu.com/share/link?shareid=3246773275&uk=3155594444
CALL HOOK了KeStackAttachProcess和KeAttachProcess内核函数
KiAttachProcess的反汇编代码:
nt!KiAttachProcess:
81875b9c 8bff mov edi,edi
81875b9e 55 push ebp
81875b9f 8bec mov ebp,esp
81875ba1 53 push ebx
81875ba2 8b5d08 mov ebx,dword ptr [ebp+8]
81875ba5 56 push esi
81875ba6 8bf0 mov esi,eax
81875ba8 8b4510 mov eax,dword ptr [ebp+10h]
81875bab 57 push edi
81875bac 8d7e40 lea edi,[esi+40h]
81875baf 57 push edi
81875bb0 e84d020000 call nt!KiMoveApcState (81875e02)
81875bb5 8b4d10 mov ecx,dword ptr [ebp+10h]
81875bb8 897f04 mov dword ptr [edi+4],edi
81875bbb 893f mov dword ptr [edi],edi
81875bbd 8d4648 lea eax,[esi+48h]
81875bc0 894004 mov dword ptr [eax+4],eax
81875bc3 8900 mov dword ptr [eax],eax
81875bc5 8d8670010000 lea eax,[esi+170h]
81875bcb c6465400 mov byte ptr [esi+54h],0
81875bcf c6465500 mov byte ptr [esi+55h],0
81875bd3 c6465600 mov byte ptr [esi+56h],0
81875bd7 3bc8 cmp ecx,eax
81875bd9 7513 jne nt!KiAttachProcess+0x52 (81875bee)
81875bdb 898668010000 mov dword ptr [esi+168h],eax
81875be1 89be6c010000 mov dword ptr [esi+16Ch],edi
81875be7 c6863401000001 mov byte ptr [esi+134h],1
81875bee 6a08 push 8
81875bf0 58 pop eax
81875bf1 8d5374 lea edx,[ebx+74h]
81875bf4 f00fc102 lock xadd dword ptr [edx],eax
81875bf8 894508 mov dword ptr [ebp+8],eax
81875bfb 8b4508 mov eax,dword ptr [ebp+8]
81875bfe a807 test al,7
81875c00 7510 jne nt!KiAttachProcess+0x76 (81875c12)
81875c02 895e50 mov dword ptr [esi+50h],ebx
81875c05 33c0 xor eax,eax
81875c07 83c660 add esi,60h
81875c0a f02106 lock and dword ptr [esi],eax
81875c0d ff7110 push dword ptr [ecx+10h]
81875c10 eb55 jmp nt!KiAttachProcess+0xcb (81875c67)
81875c12 8d7e60 lea edi,[esi+60h]
81875c15 33c0 xor eax,eax
81875c17 8bcf mov ecx,edi
81875c19 f02101 lock and dword ptr [ecx],eax
81875c1c 8bc3 mov eax,ebx
81875c1e e84f4f0500 call nt!KiInSwapSingleProcess (818cab72)
81875c23 83650800 and dword ptr [ebp+8],0
81875c27 eb27 jmp nt!KiAttachProcess+0xb4 (81875c50)
81875c29 ff4508 inc dword ptr [ebp+8]
81875c2c 8b4508 mov eax,dword ptr [ebp+8]
81875c2f 8505342b9781 test dword ptr [nt!HvlLongSpinCountMask (81972b34)],eax
81875c35 7511 jne nt!KiAttachProcess+0xac (81875c48)
81875c37 f6052c2b978140 test byte ptr [nt!HvlEnlightenments (81972b2c)],40h
81875c3e 7408 je nt!KiAttachProcess+0xac (81875c48)
81875c40 50 push eax
81875c41 e865840600 call nt!HvlNotifyLongSpinWait (818de0ab)
81875c46 eb02 jmp nt!KiAttachProcess+0xae (81875c4a)
81875c48 f390 pause
81875c4a 8b07 mov eax,dword ptr [edi]
81875c4c 85c0 test eax,eax
81875c4e 75d9 jne nt!KiAttachProcess+0x8d (81875c29)
81875c50 33c0 xor eax,eax
81875c52 8bcf mov ecx,edi
81875c54 40 inc eax
81875c55 8701 xchg eax,dword ptr [ecx]
81875c57 85c0 test eax,eax
81875c59 75ce jne nt!KiAttachProcess+0x8d (81875c29)
81875c5b 895e50 mov dword ptr [esi+50h],ebx
81875c5e f02107 lock and dword ptr [edi],eax
81875c61 8b4510 mov eax,dword ptr [ebp+10h]
81875c64 ff7010 push dword ptr [eax+10h]
81875c67 53 push ebx
81875c68 e89728ffff call nt!KiSwapProcess (81868504)
81875c6d 8a4d0c mov cl,byte ptr [ebp+0Ch]
81875c70 ff1558418181 call dword ptr [nt!_imp_KfLowerIrql (81814158)]
81875c76 5f pop edi
81875c77 5e pop esi
81875c78 5b pop ebx
81875c79 5d pop ebp
81875c7a c20c00 ret 0Ch
81875c7d 90 nop
81875c7e 90 nop
81875c7f 90 nop
81875c80 90 nop
81875c81 90 nop
可以下硬件断点