不少程序在运行时会创建/打开全局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;
}
参考的文档打包连接