天天看點

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句柄,運作同一個程序兩次的結果如下

R3下用ZwQueryObject/ZwDuplicateObject關閉互斥體和解除檔案占用

用xuetr關閉程序中的mutex句柄,再次打開:

R3下用ZwQueryObject/ZwDuplicateObject關閉互斥體和解除檔案占用
R3下用ZwQueryObject/ZwDuplicateObject關閉互斥體和解除檔案占用
R3下用ZwQueryObject/ZwDuplicateObject關閉互斥體和解除檔案占用

由上面幾張圖可知,殺掉全局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;
}