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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace Keystone
{
/// <summary>
/// Represents a Keystone engine.
/// </summary>
public sealed class Engine : IDisposable
{
private IntPtr engine = IntPtr.Zero;
private bool addedResolveSymbol;
private readonly ResolverInternal internalImpl;
private readonly List<Resolver> resolvers = new List<Resolver>();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool ResolverInternal(IntPtr symbol, ref ulong value);
/// <summary>
/// Gets or sets a value that represents whether a <see cref="KeystoneException" />
/// should be thrown on error.
/// </summary>
public bool ThrowOnError { get; set; }
/// <summary>
/// Delegate for defining symbol resolvers.
/// </summary>
/// <param name="symbol">Symbol to resolve.</param>
/// <param name="value">Address of taid symbol, if found.</param>
/// <returns>Whether the symbol was recognized.</returns>
public delegate bool Resolver(string symbol, ref ulong value);
/// <summary>
/// Event raised when keystone is resolving a symbol.
/// </summary>
/// <remarks>This event is only available on Keystone 0.9.2 or higher.</remarks>
public event Resolver ResolveSymbol
{
add
{
if (!addedResolveSymbol)
{
KeystoneError err = NativeInterop.SetOption(engine, (int)OptionType.SYM_RESOLVER, Marshal.GetFunctionPointerForDelegate(internalImpl));
if (err == KeystoneError.KS_ERR_OK)
addedResolveSymbol = true;
else
throw new KeystoneException("Could not add symbol resolver", err);
}
resolvers.Add(value);
}
remove
{
if (addedResolveSymbol && resolvers.Count == 0)
{
KeystoneError err = NativeInterop.SetOption(engine, (int)OptionType.SYM_RESOLVER, IntPtr.Zero);
if (err == KeystoneError.KS_ERR_OK)
addedResolveSymbol = false;
else
throw new KeystoneException("Could not remove symbol resolver", err);
}
resolvers.Remove(value);
}
}
/// <summary>
/// Method used for symbol resolving.
/// </summary>
/// <param name="symbolPtr">Pointer to the name of the symbol.</param>
/// <param name="value">Address of the symbol, if found.</param>
/// <returns>Whether the symbol could be recognized.</returns>
private bool ResolveSymbolInternal(IntPtr symbolPtr, ref ulong value)
{
string symbol = Marshal.PtrToStringAnsi(symbolPtr);
foreach (Resolver item in resolvers)
{
bool result = item(symbol, ref value);
if (result)
return true;
}
return false;
}
/// <summary>
/// Constructs the engine with a given architecture and a given mode.
/// </summary>
/// <param name="architecture">The target architecture.</param>
/// <param name="mode">The mode, i.e. endianness, word size etc.</param>
/// <remarks>
/// Some architectures are not supported.
/// Check with <see cref="IsArchitectureSupported(Architecture)"/> if the engine
/// supports the target architecture.
/// </remarks>
public Engine(Architecture architecture, Mode mode)
{
internalImpl = ResolveSymbolInternal;
var result = NativeInterop.Open(architecture, (int)mode, ref engine);
if (result != KeystoneError.KS_ERR_OK)
throw new KeystoneException("Error while initializing keystone", result);
}
/// <summary>
/// Sets an option in the engine.
/// </summary>
/// <param name="type">Type of the option.</param>
/// <param name="value">Value it the option.</param>
/// <returns>Whether the option was correctly set.</returns>
/// <exception cref="KeystoneException">An error encountered when setting the option.</exception>
public bool SetOption(OptionType type, uint value)
{
var result = NativeInterop.SetOption(engine, (int)type, (IntPtr)value);
if (result != KeystoneError.KS_ERR_OK)
{
if (ThrowOnError)
throw new KeystoneException("Error while setting option", result);
return false;
}
return true;
}
/// <summary>
/// Encodes the given statement(s).
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="size">Size of the buffer produced by the operation.</param>
/// <param name="statementCount">Number of statements found and encoded.</param>
/// <returns>Result of the operation, or <c>null</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public byte[] Assemble(string toEncode, ulong address, out int size, out int statementCount)
{
if (toEncode == null)
throw new ArgumentNullException(nameof(toEncode));
int result = NativeInterop.Assemble(engine,
toEncode,
address,
out IntPtr encoding,
out uint size_,
out uint statementCount_);
if (result != 0)
{
if (ThrowOnError)
throw new KeystoneException("Error while assembling instructions", GetLastKeystoneError());
size = statementCount = 0;
return null;
}
size = (int)size_;
statementCount = (int)statementCount_;
byte[] buffer = new byte[size];
Marshal.Copy(encoding, buffer, 0, size);
NativeInterop.Free(encoding);
return buffer;
}
/// <summary>
/// Encodes the given statement(s).
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <returns>Result of the operation, or <c>null</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public EncodedData Assemble(string toEncode, ulong address)
{
byte[] buffer = Assemble(toEncode, address, out int size, out int statementCount);
if (buffer == null)
return null;
return new EncodedData(buffer, statementCount, address);
}
/// <summary>
/// Encodes the given statement(s) into the given buffer.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="buffer">Buffer into which the data shall be written.</param>
/// <param name="index">Index into the buffer after which the data shall be written.</param>
/// <param name="statementCount">Number of statements found and encoded.</param>
/// <returns>Size of the data writen by the operation., or <c>0</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="ArgumentOutOfRangeException">The provided index is invalid.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public int Assemble(string toEncode, ulong address, byte[] buffer, int index, out int statementCount)
{
if (toEncode == null)
throw new ArgumentNullException(nameof(toEncode));
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
if (index < 0 || index >= buffer.Length)
throw new ArgumentOutOfRangeException(nameof(buffer));
int result = NativeInterop.Assemble(engine,
toEncode,
address,
out IntPtr encoding,
out uint size_,
out uint statementCount_);
int size = (int)size_;
statementCount = (int)statementCount_;
if (result != 0)
{
if (ThrowOnError)
throw new KeystoneException("Error while assembling instructions", GetLastKeystoneError());
return 0;
}
Marshal.Copy(encoding, buffer, index, size);
NativeInterop.Free(encoding);
return size;
}
/// <summary>
/// Encodes the given statement(s) into the given buffer.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="buffer">Buffer into which the data shall be written.</param>
/// <param name="index">Index into the buffer after which the data shall be written.</param>
/// <returns>Size of the data writen by the operation., or <c>0</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="ArgumentOutOfRangeException">The provided index is invalid.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public int Assemble(string toEncode, ulong address, byte[] buffer, int index)
{
return Assemble(toEncode, address, buffer, index, out _);
}
/// <summary>
/// Encodes the given statement(s) into the given stream.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="stream">Buffer into which the data shall be written.</param>
/// <param name="size">Size of the buffer produced by the operation.</param>
/// <param name="statementCount">Number of statements found and encoded.</param>
/// <returns><c>true</c> on success, or <c>false</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public bool Assemble(string toEncode, ulong address, Stream stream, out int size, out int statementCount)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
byte[] enc = Assemble(toEncode, address, out size, out statementCount);
if (enc == null)
return false;
stream.Write(enc, 0, size);
return true;
}
/// <summary>
/// Encodes the given statement(s) into the given stream.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="stream">Buffer into which the data shall be written.</param>
/// <param name="size">Size of the buffer produced by the operation.</param>
/// <returns><c>true</c> on success, or <c>false</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public bool Assemble(string toEncode, ulong address, Stream stream, out int size)
{
return Assemble(toEncode, address, stream, out size, out _);
}
/// <summary>
/// Encodes the given statement(s) into the given stream.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="stream">Buffer into which the data shall be written.</param>
/// <returns><c>true</c> on success, or <c>false</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public bool Assemble(string toEncode, ulong address, Stream stream)
{
return Assemble(toEncode, address, stream, out _, out _);
}
/// <summary>
/// Gets the last error for this instance.
/// </summary>
/// <returns>The last error code.</returns>
/// <remarks>
/// It might not retain its old error once accessed.
/// </remarks>
public KeystoneError GetLastKeystoneError()
{
return NativeInterop.GetLastKeystoneError(engine);
}
/// <summary>
/// Returns the string associated with a given error code.
/// </summary>
public static string ErrorToString(KeystoneError code)
{
IntPtr error = NativeInterop.ErrorToString(code);
if (error != IntPtr.Zero)
return Marshal.PtrToStringAnsi(error);
return string.Empty;
}
/// <summary>
/// Checks if the given architecture is supported.
/// </summary>
public static bool IsArchitectureSupported(Architecture architecture)
{
return NativeInterop.IsArchitectureSupported(architecture);
}
/// <summary>
/// Gets the version of the engine.
/// </summary>
/// <param name="major">Major version number.</param>
/// <param name="minor">Minor version number.</param>
/// <returns>Unique identifier for this version.</returns>
public static uint GetKeystoneVersion(ref uint major, ref uint minor)
{
return NativeInterop.Version(ref major, ref minor);
}
/// <summary>
/// Releases the engine.
/// </summary>
public void Dispose()
{
IntPtr currentEngine = Interlocked.Exchange(ref engine, IntPtr.Zero);
if (currentEngine != IntPtr.Zero)
NativeInterop.Close(currentEngine);
}
}
}