一、项目说明
这次项目是这样设计的,首先 inline hook NtTerminateProcess 函数,使其他进程不能关闭受保护的进程,这里选择记事本。
然后编写一个普通的程序,调用 TerminateProcess 函数关闭记事本,如无意外是关不掉的。
然后编写驱动,从文件系统中加载一份新内核到内存,拉伸PE,修复重定位表,IAT表,SSDT表;HOOK KiFastCallEntry 函数,让我们的程序系统调用走新内核,这样就可以关掉记事本了。
二、前置任务
首先把保护记事本和关闭记事本的程序给出,这两个没什么难度,不想解释。
效果就是加载保护驱动后,无法 TerminateProcess 关闭记事本。
调用 TerminateProcess 关闭记事本的程序
// KillNotepad.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
// 提权函数:提升为DEBUG权限
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
BOOL fOk=FALSE;
if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount=1;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid);
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
fOk=(GetLastError()==ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOk;
}
int _tmain(int argc, _TCHAR* argv[])
{
EnableDebugPrivilege();
HWND hWnd = FindWindowA(NULL, "无标题 - 记事本");
DWORD dwPid = -1;
GetWindowThreadProcessId(hWnd, &dwPid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
TerminateProcess(hProcess,0);
return 0;
}
保护记事本的驱动
//#include <ntddk.h>
#include <ntifs.h>
#include <ntimage.h>
//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
UINT16 LoadCount;
UINT16 TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
ULONG CheckSum;
ULONG TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
typedef NTSTATUS (*NTTERMINATEPROCESS) (HANDLE ProcessHandle, NTSTATUS ExitStatus);
// 系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表
PULONG ServiceCounterTableBase; // SSDT 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
// SSDT表
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);
VOID DriverUnload(PDRIVER_OBJECT driver);
VOID PageProtectOff();
VOID PageProtectOn();
VOID GetKernelBase(PDRIVER_OBJECT driver, PVOID *pKrnlBase, PULONG uKrnlImageSize);
PVOID MemorySearch(PVOID bytecode, ULONG bytecodeLen, PVOID pBeginAddress, PVOID pEndAddress);
VOID HookNtTerminateProcess();
VOID UnhookNtTerminateProcess();
VOID HbgNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus);
//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
PDRIVER_OBJECT g_Driver;
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable; // 内核导出的全局变量
PVOID pNtTerminateProcess;
PVOID pNtTerminateProcessHookRet;
// NtTerminateProcess 系统调用号
#define NTTERMINATEPROCESS_INDEX 0x101
//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
g_Driver = driver;
driver->DriverUnload = DriverUnload;
HookNtTerminateProcess();
return STATUS_SUCCESS;
}
// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT driver)
{
UnhookNtTerminateProcess();
DbgPrint("Kernel Unload Successfully.\n");
}
// 关闭页保护
VOID PageProtectOff()
{
__asm
{
cli; // 关闭中断
mov eax, cr0;
and eax, not 0x10000; // WP位置0
mov cr0, eax;
}
}
// 开启页保护
VOID PageProtectOn()
{
__asm
{
mov eax, cr0;
or eax, 0x10000; // WP位置1
mov cr0, eax;
sti; // 恢复中断
}
}
// 获取内核基址,大小
VOID GetKernelBase(PDRIVER_OBJECT driver, PVOID *pKrnlBase, PULONG uKrnlImageSize)
{
PLDR_DATA_TABLE_ENTRY pLdteHead; // 内核模块链表头
PLDR_DATA_TABLE_ENTRY pLdteCur; // 遍历指针
UNICODE_STRING usKrnlBaseDllName; // 内核模块名
RtlInitUnicodeString(&usKrnlBaseDllName,L"ntoskrnl.exe");
pLdteHead = (PLDR_DATA_TABLE_ENTRY)driver->DriverSection;
pLdteCur = pLdteHead;
do
{
PLDR_DATA_TABLE_ENTRY pLdte = CONTAINING_RECORD(pLdteCur, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
//DbgPrint("DllBase: %p, SizeOfImage: %08X %wZ\n", pLdteCur->DllBase, pLdteCur->SizeOfImage, &(pLdteCur->FullDllName));
if (RtlCompareUnicodeString(&pLdteCur->BaseDllName, &usKrnlBaseDllName, TRUE) == 0)
{
*pKrnlBase = pLdteCur->DllBase;
*uKrnlImageSize = pLdteCur->SizeOfImage;
return;
}
pLdteCur = (PLDR_DATA_TABLE_ENTRY)pLdteCur->InLoadOrderLinks.Flink;
} while (pLdteHead != pLdteCur);
return;
}
// 特征码搜索
PVOID MemorySearch(PVOID bytecode, ULONG bytecodeLen, PVOID pBeginAddress, PVOID pEndAddress)
{
PVOID pCur = pBeginAddress;
while (pCur != pEndAddress)
{
if (RtlCompareMemory(bytecode,pCur,bytecodeLen) == bytecodeLen)
{
return pCur;
}
((ULONG)pCur)++;
}
return 0;
}
// hook NtTerminateProcess
VOID HookNtTerminateProcess()
{
pNtTerminateProcess = (PVOID)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_INDEX];
PageProtectOff();
// hook NtTerminateProcess ,采取短跳+长跳的方式
*(PUCHAR)((ULONG)pNtTerminateProcess - 5) = 0xE9;
*(PULONG)((ULONG)pNtTerminateProcess - 4) = (ULONG)HbgNtTerminateProcess - (ULONG)pNtTerminateProcess;
*(PUSHORT)pNtTerminateProcess = (USHORT)0xF9EB;
PageProtectOn();
}
// unhook NtTerminateProcess
VOID UnhookNtTerminateProcess()
{
*(PUSHORT)pNtTerminateProcess = (USHORT)0xFF8B;
*(PUCHAR)((ULONG)pNtTerminateProcess - 5) = 0xCC;
*(PULONG)((ULONG)pNtTerminateProcess - 4) = 0xCCCCCCCC;
}
// 被修改的 NtTerminateProcess 函数,简单打印参数
__declspec(naked) VOID HbgNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
PEPROCESS pEprocess;
NTSTATUS status;
PCHAR ImageFileName;
__asm
{
mov edi,edi;
push ebp;
mov ebp,esp;
pushad;
pushfd;
sub esp,0x20;
}
status = ObReferenceObjectByHandle(ProcessHandle,FILE_ANY_ACCESS,*PsProcessType,KernelMode,&pEprocess,NULL);
if (!NT_SUCCESS(status))
{
goto ReturnToNtTerminateProcess;
}
// 根据镜像文件名判断是不是要保护的进程,字符串最大长度是16,超过就会截断,所以不用担心越界
ImageFileName = (PCHAR)pEprocess + 0x174;
if (strcmp(ImageFileName, "notepad.exe") == 0)
{
if (ProcessHandle == (HANDLE)0xFFFFFFFF)
{
// 通过关闭按钮关闭
goto ReturnToNtTerminateProcess;
}
else
{
// 通过任务管理器或其他进程调用TerminateProcess关闭
//DbgPrint("Terminate denied. %s: NtTerminateProcess(%x, %x)\n", ImageFileName, ProcessHandle, ExitStatus);
goto ReturnAccessDenied;
}
}
ReturnToNtTerminateProcess:
pNtTerminateProcessHookRet = (PVOID)((ULONG)pNtTerminateProcess + 5);
__asm
{
add esp,0x20;
popfd;
popad;
jmp pNtTerminateProcessHookRet;
}
ReturnAccessDenied:
__asm
{
add esp,0x20;
popfd;
popad;
leave;
mov eax,0xC0000022; // STATUS_ACCESS_DENIED
retn 0x08;
}
}
三、内核重载
写不出来,一是能力不足,二是动机不足。
动机方面,后面会学自建调用框架,完美替代内核重载。
能力方面,目前主要存在以下疑问:
- MDL 是什么,不清楚,ExAllocatePool 申请出来的内存似乎还不能执行?
- 修复 IAT 的方式?是否从当前内核内存镜像中复制过来?
- 修复重定位的方式?用老办法修复,还是从当前内核镜像中拷贝?是否必须共用一份全局变量?
- 修复SSDT表,是否是拷贝现有的SSDT表,然后减去新旧内核镜像的地址差?
下面给出目前唯一的成果,0环读写文件的API。
// 读取文件到内存中,返回读取的字节数;读取失败返回0
ULONG FileToMemory(LPCWSTR lpszFile, PVOID *pFileBuffer)
{
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iostatus;
HANDLE hfile;
UNICODE_STRING KernelFileUnicodeString;
NTSTATUS ntStatus;
FILE_STANDARD_INFORMATION fsi;
PUCHAR pBuffer;
//初始化UNICODE_STRING字符串
//RtlInitUnicodeString( &KernelFileUnicodeString, L"\\??\\C:\\Windows\\System32\\ntkrnlpa.exe");
RtlInitUnicodeString( &KernelFileUnicodeString, lpszFile);
//初始化objectAttributes
InitializeObjectAttributes(&objectAttributes,
&KernelFileUnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
//创建文件
ntStatus = ZwOpenFile( &hfile,
GENERIC_ALL,
&objectAttributes,
&iostatus,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(ntStatus))
{
KdPrint(("The file is not exist!\n"));
return 0;
}
//读取文件长度
ntStatus = ZwQueryInformationFile(hfile,
&iostatus,
&fsi,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
//KdPrint(("The program want to read %d bytes\n",fsi.EndOfFile.QuadPart));
//为读取的文件分配缓冲区
pBuffer = (PUCHAR)ExAllocatePool(PagedPool,
(LONG)fsi.EndOfFile.QuadPart);
//读取文件
ZwReadFile(hfile,NULL,
NULL,NULL,
&iostatus,
pBuffer,
(LONG)fsi.EndOfFile.QuadPart,
NULL,NULL);
//KdPrint(("The program really read %d bytes\n",iostatus.Information));
//释放缓冲区
//ExFreePool(pBuffer);
*pFileBuffer = pBuffer;
//关闭文件句柄
ZwClose(hfile);
return (ULONG)fsi.EndOfFile.QuadPart;
}
// 内存数据写入文件
VOID MemoryToFile(PVOID pMemBuffer, ULONG ulSize, LPCWSTR lpszFile)
{
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iostatus;
HANDLE hfile;
UNICODE_STRING logFileUnicodeString;
NTSTATUS ntStatus;
//初始化UNICODE_STRING字符串
RtlInitUnicodeString( &logFileUnicodeString, lpszFile);
//初始化objectAttributes
InitializeObjectAttributes(&objectAttributes,
&logFileUnicodeString,
OBJ_CASE_INSENSITIVE,//对大小写敏感
NULL,
NULL );
//创建文件
ntStatus = ZwCreateFile( &hfile,
GENERIC_WRITE,
&objectAttributes,
&iostatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE,
FILE_OPEN_IF,//即使存在该文件,也创建
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0 );
ZwWriteFile(hfile,NULL,NULL,NULL,&iostatus,pMemBuffer,ulSize,NULL,NULL);
KdPrint(("The program really wrote %d bytes\n",iostatus.Information));
//关闭文件句柄
ZwClose(hfile);
}
---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/110072446
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件