一、進程自我保護:
HOOK windows 用戶層api (用戶層保護)
內核層api(驅動層保護)
1:用戶層hook(簡單例子)
Hook
OpenProcess(....,....,DWORD dwProcessId)
攔截函數
DWORD MyOpenProcess(...,....,DWORD dwProcessId)
{
// MydwProcessId 被保護進程id
If(dwProcessId==MydwProcessId)
{
return ERROR_ACCESS_DENIED; //過濾攔截
}
else
{
return OpenProcess(....,....,dwProcessId); //正常操作
}
}
2:驅動層hook
首先看一下OpenProcess 整個操作流程:
用戶層可以
HOOK:OpenProcess NtOpenProcess
驅動層可以
替換SSTD表對應服務的函數地址,
HOOK: NtOpenProcess,PspOpenProcess,ObOpenObjectByPointer
HOOK SSTD表對應服務的函數地址(簡單例子):
某某SSDT 自我保護進程:
正常OpenProcess 會出現訪問拒絕。
但通過驅動去獲取,便原型必現
二.暢想:
這只是基本的開發模型。主要方向反病毒軟件。
近期搜集了一些資料,喜歡鉆研的可以聊聊。
游戲輔助軟件開發,過保護一直以來都是關鍵的一步驟,今天我們來再談SSDT HOOK驅動保護原理(主要分為以下四部分):初識內核進程相關結構;內核函數PsGetCurrentProcess;進程保護原理;實例測試。具體代碼等可以和小編聊聊(私信:游戲輔助),最近也收集了一些資料。
看一個實例(代碼已經寫好了)
將 mini_ddk.sys 和 test_exe.exe 復制到 虛擬機,載入驅動
以一個記事本進程為例
啟動 WinDBG
ZC: WinDBG 中 打印了很多信息...
∵ 被hook了之后,系統的速度變慢了,可以看到 虛擬機中的CPU已經被占滿了
ZC: 看這個顯現,貌似有點死循環的意思啊...不像變慢啊...【575】WinDBG中也看到信息一直在打印出來,個人判斷,肯定是寫的代碼有問題,貌似是 無休止的調用某函數的緣故。
我們把調試去掉(ZC: WinDBG里面打了 命令"g",貌似這個操作與"去掉調試"沒有關系吧...它說的"把調試去掉"應該是指把test_exe.exe關掉/mini_ddk.sys卸載掉?)
ZC: 貌似虛擬機重啟過了
此時,再用 CE 來附加 記事本進程,CE報錯"打開進程錯誤",它已經被保護起來了
并非說 記事本進程 就絕對的 能夠得到很好的保護了,如 OD里面有一些過保護的插件,它就可以過這個保護
打開 OD1.1(郁金香OD2010.exe),此時(OD-->文件-->附加)找不到 記事本的進程
OD-->插件-->StrongOD-->Options-->"*KernelMode"勾上 -->關閉OD
再次打開 OD 【990】此時(OD-->文件-->附加) 能看到 記事本進程,也能正常的附加
OD-->反匯編窗口-->右擊-->StrongOD-->Detach --> 關閉OD
只是一個簡單的演示
CE5.4 里面 也有一個 內核模式: CE-->設置-->其他 --> 隱藏模式(內核模式),但是這個是 調試模式,不知道這個有沒有用。∵ 虛擬機里面測試非常的不方便,就不再做再多的演示了,有興趣自己測試
看一下原理
對 函數NtOpenProcess 做了一個 SSDT的 HOOK,它不是一個內聯的(ZC: 講的是"內聯的"嗎?)
一般來說比較強的保護的話,它都是一個內聯的更深層次的保護,今天我們就不說內聯HOOK了,說SSDT的HOOK
內核里面的 NtOpenProcess 如下:
“
NTSTATUS NtOpenProcess(
__out PHANDLE ProcessHandle, // 【1360】這個是用來回傳參數的
__in ACCESS_MASK DesiredAccess, // 【1410】訪問權限的掩碼
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in_opt PCLIENT_ID ClientId
);
”
MSDN里面查看看 OpenProcess,返回值 對應 函數NtOpenProcess的參數ProcessHandle
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 【1485】對應函數NtOpenProcess的參數DesiredAccess
BOOL bInheritHandle, // 【1680】對應函數NtOpenProcess的參數ObjectAttributes
DWORD dwProcessId
);
OpenProcess的參數dwProcessId 對應的不是 函數NtOpenProcess的參數ClientId
包含這樣一個結構 dt _CLIENT_ID 這個結構,在OD里面也能看到,在WInDBG里面能夠看到(ZC: 到底 OD里能不能看到?)
vs2003里面看到 ntddk.h中 有該結構的定義
“
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;//進程PID
HANDLE UniqueThread; //線程PID
} CLIENT_ID;
”
根據傳入的值,這個值(CLIENT_ID.UniqueProcess)如果是我們要打開的進程,做一個簡單的過濾,就直接返回 函數NtOpenProcess的參數ProcessHandle 的值 為空。我們對 OpenProcess 進行SSDT的HOOK 進入我們自己的函數,自己構建一個假的函數 MyNtOpenProcess()
“
MyNtOpenProcess()
{
if 要打開進程是我們要保護的
{
ProcessHandle=NULL
}
else
{
ProcessHandle=NtOpenProcess()//HANDLE hProcess
}
}
”
這樣就起到一個簡單的保護作用
MSDN查看 WriteProcessMemory
也就是說 我們的戰場就是在 OpenProcess這里
看一下這個結構 "dt _eprocess"
打開虛擬機,啟動 WinDBG
∵ 進程相關的結構,在程序(vs2003)里面是看不到的,∵該結構是一個未公開的一個結構
VS2003中 _EPROCESS 【2565】此時 提示“未定義符號"_EPROCESS"”
ZC: 根據 應用層的結構-->得到內核未公開的結構名-->在WinDBG中用"dt"命令 一點一點往下挖
WinDBG 命令"dt _eprocess"
_EPROCESS.DebugPort 比較重要,常用來 反調試
_EPROCESS.ImageFileName 進程名。也可以通過過濾進程名來保護進程
WinDBG 命令"dt _CLIENT_ID"
看一下 大致的代碼
“
// 定義一下NtOpenProcess的原型
extern "C" typedef NTSTATUS (__stdcall NTOPENPROCESS)
(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK AccessMask,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId
);
NTOPENPROCESS * RealNtOpenProcess; // 用于存放真實的 NtOpenProcess函數 的地址
”
dd nt!KeServiceDescriptorTable
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;//進程PID
HANDLE UniqueThread; //線程PID
} CLIENT_ID;
dt _CLIENT_ID
dt _eprocess
dt -v -r _eprocess (加上-v -r顯示詳細結構)
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <NTDDK.h> //這里包含需要用C方式編譯的頭文件
#ifdef __cplusplus
}
#endif
#include <windef.h>
bool ssdthook_flag=false;
extern "C" extern PServiceDescriptorTable KeServiceDescriptorTable;
//extern "C" NTSTATUS __stdcall PsLookupProcessByProcessId( IN ULONG ulProcId, OUT PEPROCESS *pEProcess );
PEPROCESS EP;
// 真實的函數地址
ULONG RealServiceAddress;
HANDLE MyPID; //要保護的進程ID
// 自定義的NtOpenProcess函數
#pragma PAGECODE
//************************************
extern "C" NTSTATUS __stdcall MyNtOpenProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId )
{
NTSTATUS rc;
HANDLE PID;
KdPrint(("++++++++++++Entry MyNtOpenProcess int ++++++++++++++\n"));
rc=(NTSTATUS)RealNtOpenProcess( ProcessHandle, DesiredAccess, ObjectAttributes, ClientId );
if( (ClientId !=NULL) )
{
PID=ClientId->UniqueProcess;
KdPrint(( "------------------------- PID=%d--------------\n",(int*)PID ));
// 如果是被保護的PID,直接返回權限不足,并將句柄設置為空
if(PID==MyPID)
{
KdPrint(( "++++++++++++++++++++++++++++被保護進程 MyPID=%d++++++++++++++++++++++++\n",MyPID ));
ProcessHandle=NULL; //這個是關鍵
rc=STATUS_ACCESS_DENIED;
//PsLookupProcessByProcessId((ULONG)PID,&EP);
EP=PsGetCurrentProcess();
KdPrint(("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA ACESS Process Name --:%s-- AAAAAAAAAAAAAAAAAA \n",(PTSTR)((ULONG)EP+0x174)));
KdPrint(( "++++++++++++++++++++++++++++=======================================================+++++++++++++++++++++++\n" ));
}
}
return rc;
}
#pragma PAGECODE
VOID Hook()
{
KdPrint(("++++++++++++++++++++HOOK START +++++++++++++++++++++++++++++++ ---------------------------------\n"));
LONG *SSDT_Adr,SSDT_NtOpenProcess_Cur_Addr,t_addr;
KdPrint(("驅動成功被加載中.............................\n"));
//讀取SSDT表中索引值為0x7A的函數
//poi(poi(KeServiceDescriptorTable)+0x7a*4)
t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;
KdPrint(("當前ServiceTableBase地址為%x \n",t_addr));
SSDT_Adr=(PLONG)(t_addr+0x7A*4);
KdPrint(("當前t_addr+0x7A*4=%x \n",SSDT_Adr));
SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr;
RealServiceAddress=*SSDT_Adr;
RealNtOpenProcess=( NTOPENPROCESS *)RealServiceAddress;
KdPrint(( "真實的NtOpenProcess地址: %x\n",(int) RealServiceAddress ));
KdPrint((" 偽造NTOpenProcess地址: %x\n", (int)MyNtOpenProcess ));
__asm //去掉頁面保護
{
cli
mov eax,cr0
and eax,not 10000h //and eax,0FFFEFFFFh
mov cr0,eax
}
*SSDT_Adr=(ULONG)MyNtOpenProcess;
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
return;
}
//////////////////////////////////////////////////////
#pragma PAGECODE
VOID UnHook()
{
ULONG Old_ssdt;
Old_ssdt=(ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
if (ssdthook_flag)
{
ssdthook_flag=false;
__asm
{
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
}
// 還原SSDT
*((ULONG*)Old_ssdt)=(ULONG)RealServiceAddress;
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
KdPrint(("UnHook還原SSDT OK \n"));
}
return;
}
#define hook_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER,FILE_ANY_ACCESS)
#define unhook_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER,FILE_ANY_ACCESS)
小編搜集了一些資料,有興趣的可以私信:游戲輔助,了解些下