不少程式在運作時會建立/打開全局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;
}