forked from ufrisk/MemProcFS
-
Notifications
You must be signed in to change notification settings - Fork 0
/
m_fc_registry.c
208 lines (199 loc) · 8.15 KB
/
m_fc_registry.c
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// m_fc_registry.c : registry forensic module.
//
// REQUIRE: FORENSIC SUB-SYSTEM INIT.
//
// NB! module generate forensic data only - no file system presence!
//
// (c) Ulf Frisk, 2020-2022
// Author: Ulf Frisk, [email protected]
//
#include "fc.h"
#include "vmm.h"
#include "vmmwinreg.h"
#include "pluginmanager.h"
#include "charutil.h"
#include "util.h"
#include "version.h"
static LPSTR FC_SQL_SCHEMA_REGISTRY =
"DROP VIEW IF EXISTS v_registry; " \
"DROP TABLE IF EXISTS registry; " \
"CREATE TABLE registry ( id INTEGER PRIMARY KEY AUTOINCREMENT, id_str INTEGER, hive INTEGER, cell INTEGER, cell_parent INTEGER, time INTEGER ); " \
"CREATE VIEW v_registry AS SELECT * FROM registry, str WHERE registry.id_str = str.id; ";
static LPCSTR MFCREGISTRY_TYPE_NAMES[] = {
"REG_NONE",
"REG_SZ",
"REG_EXPAND_SZ",
"REG_BINARY",
"REG_DWORD",
"REG_DWORD_BIG_ENDIAN",
"REG_LINK",
"REG_MULTI_SZ",
"REG_RESOURCE_LIST",
"REG_FULL_RESOURCE_DESCRIPTOR",
"REG_RESOURCE_REQUIREMENTS_LIST",
"REG_QWORD"
};
VOID MFcRegistry_JsonKeyCB(_In_ VMM_HANDLE H, _Inout_ PVMMWINREG_FORENSIC_CONTEXT ctx, _In_z_ LPSTR uszPathName, _In_ QWORD ftLastWrite)
{
PFC_CONTEXT ctxFc = H->fc;
SIZE_T o = 0;
CHAR szKeyLastWrite[21];
// 1: create json 'base/prefix' to re-use with values:
o += snprintf(ctx->szjBase, sizeof(ctx->szjBase) - o,
"{\"class\":\"REG\",\"ver\":\"%i.%i\",\"sys\":\"%s\",\"key\":\"",
VERSION_MAJOR, VERSION_MINOR,
H->vmm.szSystemUniqueTag
);
CharUtil_UtoJ(uszPathName, -1, ctx->szjBase + o, (DWORD)(sizeof(ctx->szjBase) - o), NULL, NULL, CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR | CHARUTIL_FLAG_STR_BUFONLY);
// 2: write key json line
Util_FileTime2JSON(ftLastWrite, szKeyLastWrite);
snprintf(ctx->sz, sizeof(ctx->sz),
"%s\",\"type\":\"key\",\"lastwrite\":\"%s\"}\n",
ctx->szjBase,
szKeyLastWrite
);
ObMemFile_AppendString(ctxFc->FileJSON.pReg, ctx->sz);
}
VOID MFcRegistry_JsonValueCB(_In_ VMM_HANDLE H, _Inout_ PVMMWINREG_FORENSIC_CONTEXT ctx)
{
QWORD i;
DWORD cch;
LPSTR szj;
CharUtil_UtoJ(ctx->value.info.uszName, -1, ctx->value.szjName, sizeof(ctx->value.szjName), NULL, NULL, CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR | CHARUTIL_FLAG_STR_BUFONLY);
if(ctx->value.info.dwType >= sizeof(MFCREGISTRY_TYPE_NAMES) / sizeof(LPCSTR)) { ctx->value.info.dwType = 0; }
ctx->value.szjValue[0] = 0;
switch(ctx->value.info.dwType) {
case REG_NONE:
case REG_BINARY:
case REG_RESOURCE_LIST:
case REG_FULL_RESOURCE_DESCRIPTOR:
case REG_RESOURCE_REQUIREMENTS_LIST:
cch = _countof(ctx->value.sz);
Util_FillHexAscii(ctx->value.pb, min(0x100, ctx->value.cb), 0, ctx->value.sz, &cch);
CharUtil_AtoJ(
ctx->value.sz,
ctx->value.cb,
ctx->value.szjValue,
sizeof(ctx->value.szjValue),
&szj,
NULL,
CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR);
break;
case REG_DWORD:
case REG_DWORD_BIG_ENDIAN:
snprintf(ctx->value.szjValue, 0x20, "%08x", *(PDWORD)ctx->value.pb);
break;
case REG_QWORD:
snprintf(ctx->value.szjValue, 0x20, "%016llx", *(PQWORD)ctx->value.pb);
break;
case REG_MULTI_SZ:
for(i = 0; (ctx->value.cb >= 6) && (i < ctx->value.cb - 4); i += 2) { // replace NULL WCHAR between strings with newline
if(!*(LPWSTR)(ctx->value.pb + i)) {
*(LPWSTR)(ctx->value.pb + i) = '\n';
}
}
// fall-through
case REG_SZ:
case REG_EXPAND_SZ:
CharUtil_WtoJ(
(LPWSTR)ctx->value.pb,
(ctx->value.cb / 2),
(PBYTE)ctx->value.szjValue,
sizeof(ctx->value.szjValue),
&szj,
NULL,
CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR);
break;
}
snprintf(ctx->sz, sizeof(ctx->sz),
"%s\",\"type\":\"value\",\"value\":{\"name\":\"%s\",\"type\":\"%s\",\"size\":%i,\"data\":\"%s\"}}\n",
ctx->szjBase,
ctx->value.szjName,
MFCREGISTRY_TYPE_NAMES[ctx->value.info.dwType],
ctx->value.info.cbData,
ctx->value.szjValue
);
ObMemFile_AppendString(H->fc->FileJSON.pReg, ctx->sz);
}
/*
* Callback for registry key information destined for forensic database.
*/
VOID MFcRegistry_KeyCB(_In_ VMM_HANDLE H, _In_ HANDLE hCallback1, _In_ HANDLE hCallback2, _In_ LPSTR uszPathName, _In_ QWORD vaHive, _In_ DWORD dwCell, _In_ DWORD dwCellParent, _In_ QWORD ftLastWrite)
{
FCSQL_INSERTSTRTABLE SqlStrInsert;
sqlite3_stmt *hStmt = (sqlite3_stmt *)hCallback1;
sqlite3_stmt *hStmtStr = (sqlite3_stmt *)hCallback2;
// build and insert string data into 'str' table.
if(!Fc_SqlInsertStr(H, hStmtStr, uszPathName, &SqlStrInsert)) { return; }
// insert into 'process' table.
sqlite3_reset(hStmt);
Fc_SqlBindMultiInt64(hStmt, 1, 5,
SqlStrInsert.id,
vaHive,
(QWORD)dwCell,
(QWORD)dwCellParent,
ftLastWrite
);
sqlite3_step(hStmt);
}
/*
* Forensic initialization function called when the forensic sub-system is initializing.
*/
PVOID M_FcRegistry_FcInitialize(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
POB_REGISTRY_HIVE pObHive = NULL;
sqlite3 *hSql = NULL;
sqlite3_stmt *hStmt = NULL, *hStmtStr = NULL;
if(SQLITE_OK != Fc_SqlExec(H, FC_SQL_SCHEMA_REGISTRY)) { goto fail; }
if(!(hSql = Fc_SqlReserve(H))) { goto fail; }
if(SQLITE_OK != sqlite3_prepare_v2(hSql, "INSERT INTO registry (id_str, hive, cell, cell_parent, time) VALUES (?, ?, ?, ?, ?);", -1, &hStmt, NULL)) { goto fail; }
if(SQLITE_OK != sqlite3_prepare_v2(hSql, szFC_SQL_STR_INSERT, -1, &hStmtStr, NULL)) { goto fail; }
sqlite3_exec(hSql, "BEGIN TRANSACTION", NULL, NULL, NULL);
while((pObHive = VmmWinReg_HiveGetNext(H, pObHive))) {
VmmWinReg_ForensicGetAllKeysAndValues(H, pObHive, hStmt, hStmtStr, MFcRegistry_KeyCB, MFcRegistry_JsonKeyCB, MFcRegistry_JsonValueCB);
}
sqlite3_exec(hSql, "COMMIT TRANSACTION", NULL, NULL, NULL);
fail:
sqlite3_finalize(hStmt);
sqlite3_finalize(hStmtStr);
Fc_SqlReserveReturn(H, hSql);
return NULL;
}
/*
* Timeline data by executing a partial SQL query on pre-existing data.
* -- H
* -- ctxfc
* -- hTimeline
* -- pfnAddEntry
* -- pfnEntryAddBySql
*/
VOID M_FcRegistry_FcTimeline(
_In_ VMM_HANDLE H,
_In_opt_ PVOID ctxfc,
_In_ HANDLE hTimeline,
_In_ VOID(*pfnAddEntry)(_In_ VMM_HANDLE H, _In_ HANDLE hTimeline, _In_ QWORD ft, _In_ DWORD dwAction, _In_ DWORD dwPID, _In_ DWORD dwData32, _In_ QWORD qwData64, _In_ LPSTR uszText),
_In_ VOID(*pfnEntryAddBySql)(_In_ VMM_HANDLE H, _In_ HANDLE hTimeline, _In_ DWORD cEntrySql, _In_ LPSTR *pszEntrySql)
) {
LPSTR pszSql[] = {
"id_str, time, "STRINGIZE(FC_TIMELINE_ACTION_MODIFY)", 0, 0, 0 FROM registry WHERE time > 0;"
};
pfnEntryAddBySql(H, hTimeline, sizeof(pszSql) / sizeof(LPSTR), pszSql);
}
/*
* Plugin initialization / registration function called by the plugin manager.
* -- H
* -- pRI
*/
VOID M_FcRegistry_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI)
{
if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; }
if((pRI->tpSystem != VMM_SYSTEM_WINDOWS_X64) && (pRI->tpSystem != VMM_SYSTEM_WINDOWS_X86)) { return; }
strcpy_s(pRI->reg_info.uszPathName, 128, "\\forensic\\hidden\\registry"); // module name
pRI->reg_info.fRootModule = TRUE; // module shows in root directory
pRI->reg_info.fRootModuleHidden = TRUE; // module hidden by default
pRI->reg_fnfc.pfnInitialize = M_FcRegistry_FcInitialize; // Forensic initialize function supported
pRI->reg_fnfc.pfnTimeline = M_FcRegistry_FcTimeline; // Forensic timelining supported
memcpy(pRI->reg_info.sTimelineNameShort, "REG", 4);
strncpy_s(pRI->reg_info.uszTimelineFile, 32, "timeline_registry", _TRUNCATE);
pRI->pfnPluginManager_Register(H, pRI);
}