今天和大家分享一個小心得,想必很多高手已經玩膩了~飄過吧!
最近接觸了不少遊戲保護,它們或多或少的都有一個特製就是在被調試機上運行遊戲以後調試機上的WINDBG就接受不到信息了。起初我也困惑的很,而且在驅動當中設置int 3斷點會藍屏。後來在一個應用程序中添加了
__asm int 3這個應用程序就崩潰了。得到結論它們都是用了KdDisableDebugger函數來禁止操作系統調試了。想對應的有一個函數是 KdEnableDebugger 可以允許調試,但是很多遊戲仍然很操蛋。及時你使用了KdEnableDebugger 也無法調試,它們都是不停的 禁止調試。
有一天我躺在床上思考,與其利用IDA分析它們的邏輯還不如直接對操作系統進行閹割算了。
假設我們系統上的KdDisableDebugger函數徹底失效了,那麼任何人調用也就不可能禁用調試模式了吧。說幹就幹,首先在MSDN上找到了KdDisableDebugger函數的定義
NTSTATUS KdDisableDebugger(void);
看到這個函數定義以後欣喜若狂,隱約有一種感覺告訴我。我的設想完全可以實現的
首先它沒有參數,在堆棧的控制上非常容易處理。其次它有一個返回值,也就是說執行的成功與否肯定有一個值來說明。
果然在下面看到它的返回值說明,執行成功則返回STATUS_SUCCESS。還有2個分別是權限不夠和調試端口不存在。我們假設不管誰調用了 KdDisableDebugger這個函數我們都在函數的起始處返回STATUS_SUCCESS(根據DDKntstatus.h的定義這個值是 0),不就廢除了這個函數的功能了嗎。
WINDBG裡面先看一下這個函數的反彙編情況
51421
這就足夠了。下面貼上代碼,大家就會一目瞭然了。代碼的邏輯也非常簡單
就不用多費口舌了

#define FAILED_TO_OBTAIN_FUNCTION_ADDRESSES 0x00000001 //獲取函數地址失敗
//////////////////////////////////////////////////////////////////////
// 名稱: MyGetFunAddress
// 功能: 獲取函數地址
// 參數: 函數名稱字符串指針
// 返回: 函數地址
//////////////////////////////////////////////////////////////////////
ULONG MyGetFunAddress( IN PCWSTR FunctionName)
{
UNICODE_STRING UniCodeFunctionName;
RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );
}
//執行卸載
VOID DriverUnload(IN PDRIVER_OBJECT pDriverObject)
{

KdPrint(("Enter DriverUnload\n"));
}
//////////////////////////////////////////////////////////////////////
// 名稱: WPOFF
// 功能: 清除CR0
// 參數:
// 返回:
//////////////////////////////////////////////////////////////////////
VOID WPOFF()
{
__asm
{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}

//////////////////////////////////////////////////////////////////////
// 名稱: WPON
// 功能: 恢復CR0
// 參數:
// 返回:
//////////////////////////////////////////////////////////////////////
VOID WPON()
{
__asm
{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
//////////////////////////////////////////////////////////////////////
// 名稱: MyHook_KdDisableDebugger
// 功能: 修改KdDisableDebugger函數起始處導致所有的調用者都返回0
// 參數:
// 返回: 狀態
//////////////////////////////////////////////////////////////////////
NTSTATUS MyHook_KdDisableDebugger()
{
KIRQL Irql;
BYTE *KdDisableDebuggerAddress = NULL;
BYTE No1Code[2] = {0x33,0xc0}; //xor eax,eax 這句彙編語句的機器碼
BYTE No2Code[1] = {0xc3}; //retn 的機器碼

//獲取KdDisableDebugger地址
KdDisableDebuggerAddress = (BYTE*)MyGetFunAddress(L"KdDisableDebugger");
if (KdDisableDebuggerAddress == NULL)
return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES;
//KdPrint(("%0X\n\n",KdDisableDebuggerAddress));
WPOFF(); //清除CR0
//提升IRQL中斷級
Irql=KeRaiseIrqlToDpcLevel();
//寫入
RtlCopyMemory(KdDisableDebuggerAddress,No1Code,2);
RtlCopyMemory(KdDisableDebuggerAddress+2,No2Code,1);
//恢復Irql
KeLowerIrql(Irql);
WPON(); //恢復CR0

return STATUS_SUCCESS;
}
//////////////////////////////////////////////////////////////////////
// 名稱: DriverEntry
// 功能: 入口函數
// 參數: DriverObject:驅動對象
// RegistryPath:設備服務鍵名稱(註冊表)
// 返回: 狀態
//////////////////////////////////////////////////////////////////////
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
NTSTATUS status;
//設置卸載函數
DriverObject->DriverUnload = DriverUnload;

status = MyHook_KdDisableDebugger();
if (status == FAILED_TO_OBTAIN_FUNCTION_ADDRESSES)
KdPrint(("獲取函數地址失敗!\n\n"));

//status = KdDisableDebugger();
//KdPrint(("======%0X\n",(ULONG)status));

return STATUS_SUCCESS;
}

執行完畢以後效果如下
51422
大家可以將入口函數當中被註釋的兩行代碼開啟,測試說明雖然禁止調試失敗,但是仍然返回0
我覺得過不了多久,很多遊戲的保護程序就應該判斷很多函數的頭部是否被HOOK了。
但是鬥爭依然繼續著,升級著……

 

一直是直接在windbg中用ed KdDisableDebugger c3 的飄過……

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 殘月影 的頭像
    殘月影

    Rootkit --逆向工程技術--

    殘月影 發表在 痞客邦 留言(0) 人氣()