R3下用ZwQueryObject/ZwDuplicateObject关闭互斥体和解除文件占用

    不少程序在运行时会创建/打开全局Mutex,来限制用户多开。百度上搜一圈下来,他们的实现基本是这样:

int main(int argc, char* argv[])
{
	HANDLE hMtx = CreateMutex(NULL,false,"process"); //创建一个有名对象,可以在其他进程中访问 
	if(GetLastError() == ERROR_ALREADY_EXISTS)    //除了创建该对象的进程能进到else分支,其他进程都进入if分支,然后退出
	{
		ExitProcess(0);
	}
	else
	        //do something	
	return 0;
}
    饶过这类限制,我能想到2种方法:

    1).修改跳转发生时的条件,不管什么情况都进入else分支

     2).删除全局Mutex变量
先简单说下方式1)

	if(GetLastError() == ERROR_ALREADY_EXISTS)
00DC13D9  mov         esi,esp  
00DC13DB  call        dword ptr [__imp__GetLastError@0 (0DC81F0h)]  
00DC13E1  cmp         esi,esp  
00DC13E3  call        @ILT+295(__RTC_CheckEsp) (0DC112Ch)  
00DC13E8  cmp         eax,0B7h  //比较-判断发生在这,只要修改eax的值或者EFlags的值即可
00DC13ED  jne         main+7Dh (0DC141Dh) 
再看下方式2),a.主要思路是查找目标进程,b.找到后枚举进程所有打开的句柄,c.用ZwDuplicateObject复制句柄,能复制成功的基本是可用的句柄,d.先关闭上次调用ZwDuplicateObject时复制的句柄,然后已DUPLICATE_CLOSE_SOURCE的方式(复制后关闭原句柄)再次复制/关闭句柄。以此关闭限制多开的Mutex。

    罗列代码前,先看下这种方式是否具有可行性。首先不杀Mutex句柄,运行同一个进程两次的结果如下


用xuetr关闭进程中的mutex句柄,再次打开:




由上面几张图可知,杀掉全局Mutext后,可以实现进程多开。有了理论的支持,下面开始实际编码。

1.准备工作。代码进行枚举并打开进程,打开进程需要提权,使进程本身具有SE_PRIVILEGE_ENABLED权限。然后导出一堆Zw*函数用于枚举系统和进程的UnDocument API。读者可能知道进程EPROCESS结构中有进程句柄表,记录了进程打开的句柄信息,但是有了SE_PRIVILEGE_ENABLED权限和UnDocument API就能获得目标进程的句柄表了么?貌似也不是~那这不就无解了?在R3下的确很直白的方法,但是,有个很重要的事:进程句柄从4开始计数,每次往上加4,这个可以通过ARK工具查看验证。知道这个事就好办了,大不了在循环中慢慢找过去呗~虽然挨个找过去是一个办法,但是未必每个值为4N的整数就是进程内有效句柄啊~因此,需要用另一个UnDocument API----ZwDuplicateObject加以验证,如果调用这个函数成功就是一个可用的句柄,然后对这个句柄经行下一步处理。

2.摸排信息。光有进程句柄,同时知道这是一个有效的句柄作用不大。想想GUI进程一堆窗口,每个窗口一个句柄,不可能直接一棍打死吧?所以得摸清楚这个句柄的背景信息,通过调用ZwQueryObject可以获得该句柄的类型名和句柄名。通过字符串比较(wcsstr)找到Mutext对应的句柄。

3.暗杀。以DUPLICATE_CLOSE_SOURCE方式调用DuplicateHandle,意思很直白了,复制的时候把源句柄关闭。谁是源句柄?当然是创建Mutext的进程中的Mutext句柄。读者可以在调试时单步运行到DuplicateHandle,然后观察xuetr中句柄占用情况:调用前,创建这个Mutext的进程对这个句柄的引用数>0,调用成功后引用数=0,因此在创建进程中再也找不到这个句柄的信息了。最后在代码中调用CloseHandle,关闭复制到本进程中的Mutext句柄。当系统发现这个句柄的引用数==0,于是会删除对应的内核对象。当下次运行同一个进程并创建互斥体时,发现系统中并没有这个互斥体,于是顺利的通过if语句往下执行。

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winternl.h>

#define STATUS_SUCCESS 0x00UL
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

#define SystemHandleInformation 16
#define SE_DEBUG_PRIVILEGE 0x14

typedef enum _OBJECT_INFORMATION_CLASSEX {
    ObjBasicInformation = 0,
	ObjNameInformation,
    ObjTypeInformation,
} OBJECT_INFORMATION_CLASSEX;

typedef enum _PROCESSINFOCLASSEX
{
	ProcessHandleInformation=20,
}PROCESSINFOCLASSEX;

typedef struct _SYSTEM_HANDLE
{
	ULONG ProcessId;
	BYTE ObjectTypeNumber;
	BYTE Flags;
	USHORT Handle;
	PVOID Object;
	ACCESS_MASK GrantAccess;
}SYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
	DWORD HandleCount;
	SYSTEM_HANDLE Handles[1];
}SYSTEM_HANDLE_INFORMATION;

typedef struct _OBJECT_NAME_INFORMATION
{
	UNICODE_STRING ObjectName;
}OBJECT_NAME_INFORMATION;

typedef NTSTATUS (WINAPI *ZwQueryInformationProcessProc)(HANDLE,PROCESSINFOCLASSEX,LPVOID,DWORD,PDWORD);
ZwQueryInformationProcessProc ZwQueryInformationProcess;

typedef NTSTATUS (WINAPI *ZwQuerySystemInformationProc)(DWORD,PVOID,DWORD,DWORD*);
ZwQuerySystemInformationProc ZwQuerySystemInformation;

typedef NTSTATUS (WINAPI *ZwQueryObjectProc)(HANDLE,OBJECT_INFORMATION_CLASSEX,PVOID,ULONG,PULONG);
ZwQueryObjectProc ZwQueryObject;

typedef NTSTATUS (WINAPI *RtlAdjustPrivilegeProc)(DWORD,BOOL,BOOL,PDWORD);
RtlAdjustPrivilegeProc RtlAdjustPrivilege;

typedef DWORD (WINAPI *ZwSuspendProcessProc)(HANDLE);
ZwSuspendProcessProc ZwSuspendProcess;

typedef DWORD (WINAPI *ZwResumeProcessProc)(HANDLE);
ZwResumeProcessProc ZwResumeProcess;
//进程提权
BOOL ElevatePrivileges()
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;
	tkp.PrivilegeCount = 1;
	if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
		return FALSE;
	LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	if(!AdjustTokenPrivileges(hToken,FALSE,&tkp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
	{
		return FALSE;
	}

	return TRUE;
}
//获得未导出api的地址
BOOL GetUnDocumentAPI()
{
	ZwSuspendProcess = (ZwSuspendProcessProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwSuspendProcess");

	ZwQuerySystemInformation = (ZwQuerySystemInformationProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQuerySystemInformation");

	ZwQueryObject = (ZwQueryObjectProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryObject");

	ZwResumeProcess = (ZwResumeProcessProc)
		GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwResumeProcess");

	ZwQueryInformationProcess = (ZwQueryInformationProcessProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryInformationProcess");

	if((ZwSuspendProcess==NULL)||\
		(ZwQuerySystemInformation==NULL)||\
		(ZwQueryObject==NULL)||\
		(ZwResumeProcess==NULL)||\
		(ZwQueryInformationProcess==NULL))
		return FALSE;
	
	return TRUE;
}

int main(int argc, char* argv[])
{
	HANDLE duplicateHnd;
	HANDLE sourceHnd;
	DWORD procHndNum;
	ULONG pid;
	FILE* fp = fopen("./pid.txt","r+");
	fscanf(fp,"%d",&pid);
	fclose(fp);
	SYSTEM_HANDLE* currnetHnd;
	DWORD buffLen = 0x1000;
	NTSTATUS status;
	SYSTEM_HANDLE_INFORMATION* buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);
	
	if((ElevatePrivileges()==FALSE)||(GetUnDocumentAPI()==FALSE))
		ExitProcess(0);

	do
	{
                //初次运行时,不知道缓存区要多大,就试探性的调用该函数。函数返回<span style="font-family: Arial, Helvetica, sans-serif;">STATUS_INFO_LENGTH_MISMATCH</span>
<span style="font-family:Arial, Helvetica, sans-serif;">                                //意为缓存区不够大,然后在下次循环前扩大缓存空间再次调用函数
</span>		status = ZwQuerySystemInformation(SystemHandleInformation,buff,buffLen,&buffLen);
		if(status == STATUS_INFO_LENGTH_MISMATCH)
		{
			free(buff);
			buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);
		}
		else
			break;

	}while(1);

	OBJECT_NAME_INFORMATION* objNameInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);
	OBJECT_NAME_INFORMATION* objTypeInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);
	
	for(int idx=0;idx<buff->HandleCount;idx++)
	{
		currnetHnd = &(buff->Handles[idx]);

		if(currnetHnd->ProcessId == pid)
		{
			sourceHnd = OpenProcess(PROCESS_ALL_ACCESS|PROCESS_DUP_HANDLE|PROCESS_SUSPEND_RESUME,FALSE,pid);
			(ZwSuspendProcess)(sourceHnd);
			(ZwQueryInformationProcess)(sourceHnd,ProcessHandleInformation,&procHndNum,sizeof(DWORD),NULL);
			//进程有效句柄从4开始,每次以4递增
			unsigned short hndNum=4;
			for(int idx=0;idx<procHndNum;hndNum+=4)
			{
				//判断是否为有效句柄,返回TRUE,就是有效句柄
				if(!DuplicateHandle(sourceHnd,
									(HANDLE)hndNum,
									GetCurrentProcess(),
									&duplicateHnd,0,FALSE,DUPLICATE_SAME_ACCESS))
				{
					continue;
				}
				else
				{
					memset(objNameInfo,0,0x1000);
					memset(objTypeInfo,0,0x1000);
                                        //查询句柄类型名和句柄名
					ZwQueryObject((HANDLE)duplicateHnd,ObjNameInformation,objNameInfo,0x1000,NULL);
					ZwQueryObject((HANDLE)duplicateHnd,ObjTypeInformation,objTypeInfo,0x1000,NULL);

					//找到互斥体 比较名字
					if(wcsicmp(objTypeInfo->ObjectName.Buffer,L"mutant") == 0)
					{
						if(wcsstr(objNameInfo->ObjectName.Buffer,L"process") != 0)
						{
							CloseHandle(duplicateHnd);
							/*
							DUPLICATE_CLOSE_SOURCE
							Closes the source handle. This occurs regardless of any error status returned.
							DUPLICATE_SAME_ACCESS
							Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.

							也就是说当我们选择DUPLICATE_CLOSE_SOURCE时,源句柄就会自动关闭了
							*/
							if(DuplicateHandle(sourceHnd,
									(HANDLE)hndNum,
									GetCurrentProcess(),
									&duplicateHnd,0,FALSE,DUPLICATE_CLOSE_SOURCE))
							{
								CloseHandle(duplicateHnd);
								ExitProcess(0);
							}
						}
					}

					wprintf(L"idx:%03x\nType:%ls\nName:%ls\n",
						(idx+1)*4,
						objTypeInfo->ObjectName.Buffer,
						objNameInfo->ObjectName.Buffer);

					wprintf(L"\n\n");
					CloseHandle(duplicateHnd);
					idx++;
				}
			}
			

			
		}
	}

	(ZwResumeProcess)(sourceHnd);
	return 0;
}


这段代码做个变体,可以查找进程打开的文件句柄,然后关闭同时删除文件,unlocker的作者大熊猫候佩就是在R3下这么实现结束进程对文件的占用。具体可以搜索他的博客,我就简单罗列一下按他的思路实现的代码

#include <stdio.h>
#include <string.h>
#include <windows.h>
//#include <winternl.h>

#define STATUS_SUCCESS 0x00UL
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

#define SystemHandleInformation 16
#define SE_DEBUG_PRIVILEGE 0x14

typedef struct _IO_STATUS_BLOCK
{
	union
	{
		NTSTATUS Status;
		PVOID    Pointer;
	};
	ULONG_PTR Information;
}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef enum _FILE_INFORMATION_CLASS
{
	FileDirectoryInformation=1,
	FileFullDirectoryInformation,   // 2
	FileBothDirectoryInformation,   // 3
	FileBasicInformation,           // 4  wdm
	FileStandardInformation,        // 5  wdm
	FileInternalInformation,        // 6
	FileEaInformation,              // 7
	FileAccessInformation,          // 8
	FileNameInformation,            // 9
	FileRenameInformation,          // 10
	FileLinkInformation,            // 11
	FileNamesInformation,           // 12
	FileDispositionInformation,     // 13
	FilePositionInformation,        // 14 wdm
	FileFullEaInformation,          // 15
	FileModeInformation,            // 16
	FileAlignmentInformation,       // 17
	FileAllInformation,             // 18
	FileAllocationInformation,      // 19
	FileEndOfFileInformation,       // 20 wdm
	FileAlternateNameInformation,   // 21
	FileStreamInformation,          // 22
	FilePipeInformation,            // 23
	FilePipeLocalInformation,       // 24
	FilePipeRemoteInformation,      // 25
	FileMailslotQueryInformation,   // 26
	FileMailslotSetInformation,     // 27
	FileCompressionInformation,     // 28
	FileObjectIdInformation,        // 29
	FileCompletionInformation,      // 30
	FileMoveClusterInformation,     // 31
	FileQuotaInformation,           // 32
	FileReparsePointInformation,    // 33
	FileNetworkOpenInformation,     // 34
	FileAttributeTagInformation,    // 35
	FileTrackingInformation,        // 36
	FileIdBothDirectoryInformation, // 37
	FileIdFullDirectoryInformation, // 38
	FileValidDataLengthInformation, // 39
	FileShortNameInformation,       // 40
	FileMaximumInformation,
}FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

typedef struct _UNICODE_STRING
{
	USHORT Length;
	USHORT MaximumLength;
	PWSTR  Buffer;
}UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_NAME_INFORMATION
{
	UNICODE_STRING Name;
}OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

typedef enum _OBJECT_INFORMATION_CLASSEX {
    ObjBasicInformation = 0,
	ObjNameInformation,
    ObjTypeInformation,
} OBJECT_INFORMATION_CLASSEX;

typedef enum _PROCESSINFOCLASSEX
{
	ProcessHandleInformation=20,
}PROCESSINFOCLASSEX;

typedef struct _SYSTEM_HANDLE
{
	ULONG ProcessId;
	BYTE ObjectTypeNumber;
	BYTE Flags;
	USHORT Handle;
	PVOID Object;
	ACCESS_MASK GrantAccess;
}SYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
	DWORD HandleCount;
	SYSTEM_HANDLE Handles[1];
}SYSTEM_HANDLE_INFORMATION;

typedef NTSTATUS (NTAPI *ZwQueryInformationFileProc)(HANDLE,PIO_STATUS_BLOCK,PVOID,DWORD,FILE_INFORMATION_CLASS);
ZwQueryInformationFileProc ZwQueryInformationFile;

typedef NTSTATUS (WINAPI *ZwQueryInformationProcessProc)(HANDLE,PROCESSINFOCLASSEX,LPVOID,DWORD,PDWORD);
ZwQueryInformationProcessProc ZwQueryInformationProcess;

typedef NTSTATUS (WINAPI *ZwQuerySystemInformationProc)(DWORD,PVOID,DWORD,DWORD*);
ZwQuerySystemInformationProc ZwQuerySystemInformation;

typedef NTSTATUS (WINAPI *ZwQueryObjectProc)(HANDLE,OBJECT_INFORMATION_CLASSEX,PVOID,ULONG,PULONG);
ZwQueryObjectProc ZwQueryObject;

typedef NTSTATUS (WINAPI *RtlAdjustPrivilegeProc)(DWORD,BOOL,BOOL,PDWORD);
RtlAdjustPrivilegeProc RtlAdjustPrivilege;

typedef DWORD (WINAPI *ZwSuspendProcessProc)(HANDLE);
ZwSuspendProcessProc ZwSuspendProcess;

typedef DWORD (WINAPI *ZwResumeProcessProc)(HANDLE);
ZwResumeProcessProc ZwResumeProcess;

BOOL ElevatePrivileges()
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;
	tkp.PrivilegeCount = 1;
	if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
		return FALSE;
	LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	if(!AdjustTokenPrivileges(hToken,FALSE,&tkp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
	{
		return FALSE;
	}

	return TRUE;
}

BOOL GetUnDocumentAPI()
{
	ZwSuspendProcess = (ZwSuspendProcessProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwSuspendProcess");

	ZwQueryInformationFile = (ZwQueryInformationFileProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryInformationFile");
	
	ZwQuerySystemInformation = (ZwQuerySystemInformationProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQuerySystemInformation");

	ZwQueryObject = (ZwQueryObjectProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryObject");

	ZwResumeProcess = (ZwResumeProcessProc)
		GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwResumeProcess");

	ZwQueryInformationProcess = (ZwQueryInformationProcessProc)
							GetProcAddress(GetModuleHandle(L"ntdll.dll"),"ZwQueryInformationProcess");

	if((ZwSuspendProcess==NULL)||\
		(ZwQuerySystemInformation==NULL)||\
		(ZwQueryObject==NULL)||\
		(ZwResumeProcess==NULL)||\
		(ZwQueryInformationProcess==NULL))
		return FALSE;
	
	return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
	_tsetlocale(0, _T("chs"));
	HANDLE duplicateHnd;
	HANDLE sourceHnd;
	DWORD procHndNum;
	ULONG pid;
	FILE* fp = _wfopen(L"./pid.txt",L"r+");
	fwscanf(fp,L"%d",&pid);
	fclose(fp);
	SYSTEM_HANDLE* currnetHnd;
	DWORD buffLen = 0x1000;
	NTSTATUS status;
	SYSTEM_HANDLE_INFORMATION* buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);
	
	IO_STATUS_BLOCK IoStatus ={0};
	POBJECT_NAME_INFORMATION objName = NULL;
	DWORD allocSize = (DWORD)(sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH*sizeof(WCHAR));
	objName = (POBJECT_NAME_INFORMATION)malloc(allocSize);
	memset(objName,0,allocSize);

	if((ElevatePrivileges()==FALSE)||(GetUnDocumentAPI()==FALSE))
		ExitProcess(0);

	do
	{
		status = ZwQuerySystemInformation(SystemHandleInformation,buff,buffLen,&buffLen);
		if(status == STATUS_INFO_LENGTH_MISMATCH)
		{
			free(buff);
			buff = (SYSTEM_HANDLE_INFORMATION*)malloc(buffLen);
		}
		else
			break;

	}while(1);

	OBJECT_NAME_INFORMATION* objNameInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);
	OBJECT_NAME_INFORMATION* objTypeInfo = (OBJECT_NAME_INFORMATION*)malloc(0x1000);
	
	for(int idx=0;idx<buff->HandleCount;idx++)
	{
		currnetHnd = &(buff->Handles[idx]);

		if(currnetHnd->ProcessId == pid)
		{
			sourceHnd = OpenProcess(PROCESS_ALL_ACCESS|PROCESS_DUP_HANDLE|PROCESS_SUSPEND_RESUME,FALSE,pid);
			//(ZwSuspendProcess)(sourceHnd);
			(ZwQueryInformationProcess)(sourceHnd,ProcessHandleInformation,&procHndNum,sizeof(DWORD),NULL);
			//进程有效句柄从4开始,每次以4递增
			unsigned short hndNum=4;
			for(int idx=0;idx<procHndNum;hndNum+=4)
			{
				//判断是否为有效句柄,返回TRUE,就是有效句柄
				if(!DuplicateHandle(sourceHnd,
									(HANDLE)hndNum,
									GetCurrentProcess(),
									&duplicateHnd,0,FALSE,DUPLICATE_SAME_ACCESS))
				{
					continue;
				}
				else
				{

					memset(objNameInfo,0,0x1000);
					memset(objTypeInfo,0,0x1000);

					ZwQueryObject((HANDLE)duplicateHnd,ObjNameInformation,objNameInfo,0x1000,NULL);
					ZwQueryObject((HANDLE)duplicateHnd,ObjTypeInformation,objTypeInfo,0x1000,NULL);

					//找到互斥体 比较名字
					if(wcsicmp(objTypeInfo->Name.Buffer,L"file") == 0)
					{
						NTSTATUS status =-1;
						memset(objName,0,allocSize);
						status = ZwQueryInformationFile((HANDLE)duplicateHnd,&IoStatus,objName,allocSize,FileNameInformation);
						if(NT_SUCCESS(status))
						{
							if(wcsstr((wchar_t*)&(objName->Name.Buffer), L"unlock")!=0)
							{
								if(DuplicateHandle(sourceHnd,
										(HANDLE)hndNum,
										GetCurrentProcess(),
										&duplicateHnd,0,FALSE,DUPLICATE_CLOSE_SOURCE))
								{
									CloseHandle(duplicateHnd);
									//DeleteFile();
									ExitProcess(0);
								}
							}
							wprintf(L"%ls\n",&(objName->Name.Buffer));
						}
					}

					idx++;
				}
			}
		}
	}
	//(ZwResumeProcess)(sourceHnd);
	return 0;
}

参考的文档打包连接

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页