forked from 0vercl0k/windbg-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpolicybuffer.js
324 lines (282 loc) · 10.1 KB
/
policybuffer.js
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// Axel '0vercl0k' Souchet - 5 March 2019
// sandbox::PolicyBase::EvalPolicy
'use strict';
//
// Utility functions.
//
const Log = host.diagnostics.debugLog;
const Logln = p => host.diagnostics.debugLog(p + '\n');
const Hex = p => '0x' + p.toString(16);
const ReadWstring = p => host.memory.readWideString(p);
function ReadShort(Address) {
let Value = null;
try {
Value = host.memory.readMemoryValues(
Address, 1, 2
)[0];
} catch(e) {
}
return Value;
}
function ReadDword(Address) {
let Value = null;
try {
Value = host.memory.readMemoryValues(
Address, 1, 4
)[0];
} catch(e) {
}
return Value;
}
function ReadQword(Address) {
let Value = null;
try {
Value = host.memory.readMemoryValues(
Address, 1, 8
)[0];
} catch(e) {
}
return Value;
}
function initializeScript() {
return [
new host.apiVersionSupport(1, 3)
];
}
//
// Constants.
//
// The low-level policy is implemented using the concept of policy 'opcodes'.
// An opcode is a structure that contains enough information to perform one
// comparison against one single input parameter. For example, an opcode can
// encode just one of the following comparison:
//
// - Is input parameter 3 not equal to NULL?
// - Does input parameter 2 start with L"c:\\"?
// - Is input parameter 5, bit 3 is equal 1?
//
// Each opcode is in fact equivalent to a function invocation where all
// the parameters are known by the opcode except one. So say you have a
// function of this form:
// bool fn(a, b, c, d) with 4 arguments
//
// Then an opcode is:
// op(fn, b, c, d)
// Which stores the function to call and its 3 last arguments
//
// Then and opcode evaluation is:
// op.eval(a) ------------------------> fn(a,b,c,d)
// internally calls
//
// The idea is that complex policy rules can be split into streams of
// opcodes which are evaluated in sequence. The evaluation is done in
// groups of opcodes that have N comparison opcodes plus 1 action opcode:
//
// [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
// ----- evaluation order----------->
//
// Each opcode group encodes one high-level policy rule. The rule applies
// only if all the conditions on the group evaluate to true. The action
// opcode contains the policy outcome for that particular rule.
// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h#77
// The following are the implemented opcodes.
// enum OpcodeID {
// OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE).
// OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE).
// OP_NUMBER_MATCH, // Match a 32-bit integer as n == a.
// OP_NUMBER_MATCH_RANGE, // Match a 32-bit integer as a <= n <= b.
// OP_NUMBER_AND_MATCH, // Match using bitwise AND; as in: n & a != 0.
// OP_WSTRING_MATCH, // Match a string for equality.
// OP_ACTION // Evaluates to an action opcode.
// };
const OP_ALWAYS_FALSE = 0;
const OP_ALWAYS_TRUE = 1;
const OP_NUMBER_MATCH = 2;
const OP_NUMBER_MATCH_RANGE = 3;
const OP_NUMBER_AND_MATCH = 4;
const OP_WSTRING_MATCH = 5;
const OP_ACTION = 6;
const Opcodes = {
[OP_ALWAYS_FALSE] : 'OP_ALWAYS_FALSE',
[OP_ALWAYS_TRUE] : 'OP_ALWAYS_TRUE',
[OP_NUMBER_MATCH] : 'OP_NUMBER_MATCH',
[OP_NUMBER_MATCH_RANGE] : 'OP_NUMBER_MATCH_RANGE',
[OP_NUMBER_AND_MATCH] : 'OP_NUMBER_AND_MATCH',
[OP_WSTRING_MATCH] : 'OP_WSTRING_MATCH',
[OP_ACTION] : 'OP_ACTION'
};
// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h
// enum StringMatchOptions {
// CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
// CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
// EXACT_LENGHT = 2 // Don't do substring match. Do full string match.
// };
const MatchingOptions = {
0 : 'CASE_SENSITIVE',
1 : 'CASE_INSENSITIVE',
2 : 'EXACT_LENGTH',
3 : 'EXACT_LENGTH | CASE_INSENSITIVE'
};
// These are the possible policy outcomes. Note that some of them might
// not apply and can be removed. Also note that The following values only
// specify what to do, not how to do it and it is acceptable given specific
// cases to ignore the policy outcome.
// enum EvalResult {
// // Comparison opcode values:
// EVAL_TRUE, // Opcode condition evaluated true.
// EVAL_FALSE, // Opcode condition evaluated false.
// EVAL_ERROR, // Opcode condition generated an error while evaluating.
// // Action opcode values:
// ASK_BROKER, // The target must generate an IPC to the broker. On the broker
// // side, this means grant access to the resource.
// DENY_ACCESS, // No access granted to the resource.
// GIVE_READONLY, // Give readonly access to the resource.
// GIVE_ALLACCESS, // Give full access to the resource.
// GIVE_CACHED, // IPC is not required. Target can return a cached handle.
// GIVE_FIRST, // TODO(cpu)
// SIGNAL_ALARM, // Unusual activity. Generate an alarm.
// FAKE_SUCCESS, // Do not call original function. Just return 'success'.
// FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied'
// // and do not do IPC.
// TERMINATE_PROCESS, // Destroy target process. Do IPC as well.
// };
const Actions = {
3 : 'ASK_BROKER',
4 : 'DENY_ACCESS',
5 : 'GIVE_READONLY',
6 : 'GIVE_ALLACCESS',
7 : 'GIVE_CACHED',
8 : 'GIVE_FIRST',
9 : 'SIGNAL_ALARM',
10 : 'FAKE_SUCCESS',
11 : 'FACE_ACCESS_DENIED',
12 : 'TERMINATE_PROCESS'
};
// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/internal_types.h#19
// enum ArgType {
// INVALID_TYPE = 0,
// WCHAR_TYPE,
// UINT32_TYPE,
// UNISTR_TYPE,
// VOIDPTR_TYPE,
// INPTR_TYPE,
// INOUTPTR_TYPE,
// LAST_TYPE
// };
const ArgTypes = {
0 : 'INVALID_TYPE',
1 : 'WCHAR_TYPE',
2 : 'UINT32_TYPE',
3 : 'UNISTR_TYPE',
4 : 'VOIDPTR_TYPE',
5 : 'INPTR_TYPE',
6 : 'INOUTPTR_TYPE',
7 : 'LAST_TYPE'
};
// Options that apply to every opcode. They are specified when creating
// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
// Do nothing special.
const kPolNone = 0;
// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
// negated conditions such as if ( a && !b).
const kPolNegateEval = 1;
// Zero the MatchContext context structure. This happens after the opcode
// is evaluated.
const kPolClearContext = 2;
// Use OR when evaluating this set of opcodes. The policy evaluator by default
// uses AND when evaluating. Very helpful when
// used with kPolNegateEval. For example if you have a condition best expressed
// as if(! (a && b && c)), the use of this flags allows it to be expressed as
// if ((!a) || (!b) || (!c)).
const kPolUseOREval = 4;
// https://dxr.mozilla.org/mozilla-central/source/security/sandbox/chromium/sandbox/win/src/policy_params.h#36
const ParameterNames = {
'OpenFile' : [
'NAME', 'BROKER', 'ACCESS', 'DISPOSITION', 'OPTIONS'
],
};
//
// Code.
//
function DisassPolicyBuffer(PolicyBufferAddress, PolicyType) {
let Ptr = PolicyBufferAddress;
const PolicyBufferOpcodeCount = ReadQword(Ptr);
Ptr += 8;
for(let Idx = 0; Idx < PolicyBufferOpcodeCount; ++Idx) {
//
// Save off the current pointer as it is useful to compute
// where the stored string is in memory for the OP_WSTRING_MATCH
// opcode.
//
const OpcodePtr = Ptr;
//
// Unpack the opcode structure.
//
const OpcodeId = ReadDword(Ptr);
Ptr += 4;
const SelectedParameter = ReadShort(Ptr);
Ptr += 2;
const Options = ReadShort(Ptr);
Ptr += 2;
const Parameters = [];
for(let InnerIdx = 0; InnerIdx < 4; ++InnerIdx) {
Parameters.push(ReadQword(Ptr));
Ptr += 8;
}
//
// Once we dumped the opcode, let's prettify its parameters.
//
const Operands = [];
let FirstOperand = 'Param' + SelectedParameter;
if(ParameterNames[PolicyType] != undefined) {
FirstOperand = PolicyType + '::' + ParameterNames[PolicyType][SelectedParameter];
}
Operands.push(FirstOperand);
if(OpcodeId == OP_ALWAYS_TRUE || OpcodeId == OP_ALWAYS_FALSE) {
} else if(OpcodeId == OP_NUMBER_MATCH) {
const ArgType = ArgTypes[Parameters[1].asNumber()];
Operands.push(ArgType + '(' + Hex(Parameters[0]) + ')');
} else if(OpcodeId == OP_NUMBER_MATCH_RANGE) {
Operands.push('LowerBound(' + Hex(Parameters[0]) + ')');
Operands.push('UpperBound(' + Hex(Parameters[1]) + ')');
} else if(OpcodeId == OP_NUMBER_AND_MATCH) {
Operands.push(Hex(Parameters[0]));
}else if(OpcodeId == OP_WSTRING_MATCH) {
const Displacement = Parameters[0];
const StringAddress = OpcodePtr.add(Displacement);
Operands.push('"' + ReadWstring(StringAddress) + '"');
Operands.push('Length(' + Hex(Parameters[1]) + ')');
Operands.push('Offset(' + Hex(Parameters[2]) + ')');
const MatchingOption = Parameters[3].asNumber();
Operands.push(MatchingOptions[MatchingOption]);
} else if(OpcodeId == OP_ACTION) {
//
// The OP_ACTION is the only opcode that does not need a selected
// parameter.
//
const Action = Actions[Parameters[0].asNumber()];
Operands[0] = Action;
}
//
// Display the opcode and its operands.
//
const OpcodeIdStr = Opcodes[OpcodeId];
if(Options.bitwiseAnd(kPolNegateEval).compareTo(0) != 0) {
Logln('!' + OpcodeIdStr + '<' + Operands.join(', ') + '>');
} else {
Logln(OpcodeIdStr + '<' + Operands.join(', ') + '>');
}
if(OpcodeId == OP_ACTION) {
Logln('');
}
}
}
function initializeScript() {
return [
new host.apiVersionSupport(1, 3),
new host.functionAlias(
DisassPolicyBuffer,
'disasspolicy'
)
];
}