不少程序在运行时会创建/打开全局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; }
#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; }