因为最近有需求写windows平台下的发外挂模块,需要实现对进程的监控,其中一个线程需要匹配进程名,那么问题来了,这就需要获取所有进程名称。
在用户层ring3下,枚举进程的方法主要有:
1.CreateToolhelp32Snapshot 通过快照枚举,x86和x64,winxp-win10 均可获取到进程名称。
2.通过 Psapi.dll,LoadLibrary(“PSAPI.DLL”),调用其EnumProcesses()枚举进程,x86和x64,winxp-win10 均可获取到进程名称,但是这个方法会OpenProcess打开进程,在ring3层打开进程,很大概率遇到访问权限问题,所以慎用。
3.通过Wtsapi32.dll, WTSOpenServer()和WTSEnumerateProcess()函数来枚举进程,但是这个函数需要首先传入NetBios名称,而且要打开服务来执行,就怕遇到获取不到NetBios名称或者服务打开失败的情况,所以定时枚举循环进程的话慎用。
4.就是最稳定也最常用的方法了,通过ntdll.dll 导出的ZwQuerySystemInformation实现进程枚举。当然这是在ring3层,需要注意宽字符转换ANSI, 建议使用这种方法。
以上都是ring3层,用户层枚举进程的方法。
可是我们的游戏保护,不能在用户层,不然随便一个SSDT表修改或者Hook jmp 跳转,就可以绕过,必须使用驱动来保护游戏,不过驱动保护涉及到驱动签名的问题,不过有国外的大牛写了驱动伪造签名的工具,我们伪造微软的驱动签名,然后就可以悄无声息的保护游戏了。不废话 vs2010 vc++ 驱动枚举进程。
DriverProtect.h
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
pragma pack(1)
/*SSDT表*/
typedef struct ServiceDescriptorEntry
{
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
}ServiceDescriptorEntry_t, *PServiceDescriptorEntry;
pragma pack()
typedef struct _PROCESS_INFO
{
ULONG_PTR eprocess;
ULONG pid;
ULONG ppid;
UNICODE_STRING pathName;
UNICODE_STRING ImageFileName;
}PROCESSINFO,*PPROCESSINFO;
typedef struct _SYSTEM_THREADS
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientID;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
ULONG ThreadState;
KWAIT_REASON WaitReason;
ULONG Reserved; //Add
}SYSTEM_THREADS,*PSYSTEM_THREADS;
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;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters;
} _SYSTEM_PROCESSES,*PSYSTEM_PROCESSES;
typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation, // 0 Y N
SystemProcessorInformation, // 1 Y N
SystemPerformanceInformation, // 2 Y N
SystemTimeOfDayInformation, // 3 Y N
SystemNotImplemented1, // 4 Y N
SystemProcessesAndThreadsInformation, // 5 Y N
SystemCallCounts, // 6 Y N
SystemConfigurationInformation, // 7 Y N
SystemProcessorTimes, // 8 Y N
SystemGlobalFlag, // 9 Y Y
SystemNotImplemented2, // 10 Y N
SystemModuleInformation, // 11 Y N
SystemLockInformation, // 12 Y N
SystemNotImplemented3, // 13 Y N
SystemNotImplemented4, // 14 Y N
SystemNotImplemented5, // 15 Y N
SystemHandleInformation, // 16 Y N
SystemObjectInformation, // 17 Y N
SystemPagefileInformation, // 18 Y N
SystemInstructionEmulationCounts, // 19 Y N
SystemInvalidInfoClass1, // 20
SystemCacheInformation, // 21 Y Y
SystemPoolTagInformation, // 22 Y N
SystemProcessorStatistics, // 23 Y N
SystemDpcInformation, // 24 Y Y
SystemNotImplemented6, // 25 Y N
SystemLoadImage, // 26 N Y
SystemUnloadImage, // 27 N Y
SystemTimeAdjustment, // 28 Y Y
SystemNotImplemented7, // 29 Y N
SystemNotImplemented8, // 30 Y N
SystemNotImplemented9, // 31 Y N
SystemCrashDumpInformation, // 32 Y N
SystemExceptionInformation, // 33 Y N
SystemCrashDumpStateInformation, // 34 Y Y/N
SystemKernelDebuggerInformation, // 35 Y N
SystemContextSwitchInformation, // 36 Y N
SystemRegistryQuotaInformation, // 37 Y Y
SystemLoadAndCallImage, // 38 N Y
SystemPrioritySeparation, // 39 N Y
SystemNotImplemented10, // 40 Y N
SystemNotImplemented11, // 41 Y N
SystemInvalidInfoClass2, // 42
SystemInvalidInfoClass3, // 43
SystemTimeZoneInformation, // 44 Y N
SystemLookasideInformation, // 45 Y N
SystemSetTimeSlipEvent, // 46 N Y
SystemCreateSession, // 47 N Y
SystemDeleteSession, // 48 N Y
SystemInvalidInfoClass4, // 49
SystemRangeStartInformation, // 50 Y N
SystemVerifierInformation, // 51 Y Y
SystemAddVerifier, // 52 N Y
SystemSessionProcessesInformation // 53 Y N
} SYSTEM_INFORMATION_CLASS;
DriverProtect.cpp
#include "ntddk.h"
#include "ntdef.h"
#include "DriverProtect.h"
#define SystemProcessesAndThreadsInformation 5
extern "C" NTSTATUS ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
VOID UnLoadDriver(PDRIVER_OBJECT DriverObject)
{
KdPrint(("驱动成功卸载!"));
}
VOID EnmuProcess()
{
ULONG cbBuffer = 0x8000;
PVOID pBuffer = NULL;
NTSTATUS rc;
LPWSTR pszProcessName;
PSYSTEM_PROCESSES pInfo;
do
{
pBuffer = ExAllocatePool (NonPagedPool, cbBuffer);
if (pBuffer == NULL)
{
return;
}
rc = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, pBuffer, cbBuffer, NULL);
if ( rc == STATUS_INFO_LENGTH_MISMATCH) //缓冲区不足
{
ExFreePool(pBuffer);
cbBuffer *= 2;
}
else if (!NT_SUCCESS(rc))
{
ExFreePool(pBuffer);
return;
}
}while (rc == STATUS_INFO_LENGTH_MISMATCH);
pInfo = (PSYSTEM_PROCESSES)pBuffer;
while (1)
{
pszProcessName = pInfo->ProcessName.Buffer;
if (pszProcessName == NULL)
{
pszProcessName = L"NULL";
}
if (pInfo->ProcessId == 0)
{
DbgPrint("PID %5d System Idle Process", pInfo->ProcessId);
}
else
{
DbgPrint("PID %5d %ws/r/n", pInfo->ProcessId, pInfo->ProcessName.Buffer);
}
if (pInfo->NextEntryDelta == 0)
{
break;
}
pInfo = (PSYSTEM_PROCESSES)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
}
ExFreePool(pBuffer);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = UnLoadDriver;
EnmuProcess();
return STATUS_SUCCESS;
}
生成了驱动,我们跑到虚拟机上,复制驱动文件和符号到虚拟机
我们使用的 visualDDK + windbg 通过虚拟机调试
内核映射
打开windbg , 然后远程连接虚拟机。
如果windbg 显示 Debuggee is running…,表示没有断点,内核是跑起来的,此时我们加载刚才生成的驱动,instdrv,加载,启动。
此时观察windbg, DbgPrint打印信息就是枚举驱动的pid和进程名。
既然已经进入内核了,而且也获取到进程名称和PID了,此时操作内核和杀软是在同一个级别,所以杀软没有任何提示的,当然你就可以进行模块枚举或者hook 内存读写函数或者进程打开函数,然后保护你想保护的进程。