天天看点

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;
}