metasm 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- data/samples/rubstop.rb +0 -399
@@ -52,15 +52,17 @@ extern VALUE *rb_cObject __attribute__((import));
|
|
52
52
|
extern VALUE *rb_eRuntimeError __attribute__((import));
|
53
53
|
extern VALUE *rb_eArgError __attribute__((import));
|
54
54
|
|
55
|
-
#define Qfalse ((VALUE)0)
|
56
|
-
#define Qtrue ((VALUE)2)
|
57
|
-
#define Qnil ((VALUE)4)
|
58
|
-
|
59
55
|
// allows generating a ruby1.9 dynldr.so from ruby1.8
|
60
56
|
#ifndef DYNLDR_RUBY_19
|
61
57
|
#define DYNLDR_RUBY_19 #{RUBY_VERSION >= '1.9' ? 1 : 0}
|
62
58
|
#endif
|
63
59
|
|
60
|
+
#if #{RUBY_VERSION >= '2.0' ? 1 : 0}
|
61
|
+
// flonums. WHY?
|
62
|
+
// also breaks Qtrue/Qnil
|
63
|
+
#define rb_float_new rb_float_new_in_heap
|
64
|
+
#endif
|
65
|
+
|
64
66
|
#if DYNLDR_RUBY_19
|
65
67
|
#define T_STRING 0x05
|
66
68
|
#define T_ARRAY 0x07
|
@@ -90,7 +92,8 @@ VALUE rb_ull2inum(unsigned long long);
|
|
90
92
|
VALUE rb_num2ulong(VALUE);
|
91
93
|
unsigned long long rb_num2ull(VALUE);
|
92
94
|
VALUE rb_str_new(const char* ptr, long len); // alloc + memcpy + 0term
|
93
|
-
VALUE
|
95
|
+
VALUE rb_ary_new();
|
96
|
+
VALUE rb_ary_push(VALUE, VALUE);
|
94
97
|
VALUE rb_float_new(double);
|
95
98
|
|
96
99
|
VALUE rb_intern(char *);
|
@@ -163,7 +166,7 @@ static VALUE memory_write(VALUE self, VALUE addr, VALUE val)
|
|
163
166
|
static VALUE memory_write_int(VALUE self, VALUE addr, VALUE val)
|
164
167
|
{
|
165
168
|
*(uintptr_t *)VAL2INT(addr) = VAL2INT(val);
|
166
|
-
return
|
169
|
+
return 1;
|
167
170
|
}
|
168
171
|
|
169
172
|
static VALUE str_ptr(VALUE self, VALUE str)
|
@@ -200,7 +203,7 @@ static VALUE sym_addr(VALUE self, VALUE lib, VALUE func)
|
|
200
203
|
|
201
204
|
if (TYPE(func) != T_STRING && TYPE(func) != T_FIXNUM)
|
202
205
|
rb_raise(*rb_eArgError, "Invalid func");
|
203
|
-
|
206
|
+
|
204
207
|
if (TYPE(func) == T_FIXNUM)
|
205
208
|
p = os_load_sym_ord(h, VAL2INT(func));
|
206
209
|
else
|
@@ -224,7 +227,7 @@ static VALUE invoke(VALUE self, VALUE ptr, VALUE args, VALUE flags)
|
|
224
227
|
{
|
225
228
|
if (TYPE(args) != T_ARRAY || ARY_LEN(args) > 64)
|
226
229
|
rb_raise(*rb_eArgError, "bad args");
|
227
|
-
|
230
|
+
|
228
231
|
uintptr_t flags_v = VAL2INT(flags);
|
229
232
|
uintptr_t ptr_v = VAL2INT(ptr);
|
230
233
|
unsigned i, argsz;
|
@@ -241,7 +244,7 @@ static VALUE invoke(VALUE self, VALUE ptr, VALUE args, VALUE flags)
|
|
241
244
|
ret = do_invoke_stdcall(ptr_v, argsz, args_c);
|
242
245
|
else
|
243
246
|
ret = do_invoke(ptr_v, argsz, args_c);
|
244
|
-
|
247
|
+
|
245
248
|
if (flags_v & 4)
|
246
249
|
return rb_ull2inum((unsigned __int64)ret);
|
247
250
|
else if (flags_v & 8)
|
@@ -257,23 +260,27 @@ static VALUE invoke(VALUE self, VALUE ptr, VALUE args, VALUE flags)
|
|
257
260
|
// callback generated by callback_alloc
|
258
261
|
// heavy stack magick at work here !
|
259
262
|
// TODO float args / float retval / ret __int64
|
260
|
-
uintptr_t do_callback_handler(uintptr_t ori_retaddr, uintptr_t caller_id, uintptr_t arg0)
|
263
|
+
uintptr_t do_callback_handler(uintptr_t ori_retaddr, uintptr_t caller_id, uintptr_t arg0, uintptr_t arg_ecx __attribute__((register(ecx))), uintptr_t arg_edx __attribute__((register(edx))))
|
261
264
|
{
|
262
265
|
uintptr_t *addr = &arg0;
|
263
266
|
unsigned i, ret;
|
264
|
-
VALUE args =
|
267
|
+
VALUE args = rb_ary_new();
|
268
|
+
|
269
|
+
// __fastcall callback args
|
270
|
+
|
271
|
+
rb_ary_push(args, INT2VAL(arg_ecx));
|
272
|
+
rb_ary_push(args, INT2VAL(arg_edx));
|
265
273
|
|
266
274
|
// copy our args to a ruby-accessible buffer
|
267
|
-
for (i=
|
268
|
-
|
269
|
-
RArray(args)->len = 8U; // len == 8, no need to ARY_LEN/EMBED stuff
|
275
|
+
for (i=2U ; i<10U ; ++i)
|
276
|
+
rb_ary_push(args, INT2VAL(*addr++));
|
270
277
|
|
271
278
|
ret = rb_funcall(dynldr, rb_intern("callback_run"), 2, INT2VAL(caller_id), args);
|
272
279
|
|
273
280
|
// dynldr.callback will give us the arity (in bytes) of the callback in args[0]
|
274
281
|
// we just put the stack lifting offset in caller_id for the asm stub to use
|
275
282
|
caller_id = VAL2INT(ARY_PTR(args)[0]);
|
276
|
-
|
283
|
+
|
277
284
|
return VAL2INT(ret);
|
278
285
|
}
|
279
286
|
|
@@ -290,7 +297,7 @@ static VALUE invoke(VALUE self, VALUE ptr, VALUE args, VALUE flags)
|
|
290
297
|
{
|
291
298
|
if (TYPE(args) != T_ARRAY || ARY_LEN(args) > 16)
|
292
299
|
rb_raise(*rb_eArgError, "bad args");
|
293
|
-
|
300
|
+
|
294
301
|
uintptr_t flags_v = VAL2INT(flags);
|
295
302
|
uintptr_t ptr_v = VAL2INT(ptr);
|
296
303
|
int i, argsz;
|
@@ -312,7 +319,7 @@ static VALUE invoke(VALUE self, VALUE ptr, VALUE args, VALUE flags)
|
|
312
319
|
args_c[4], args_c[5], args_c[6], args_c[7],
|
313
320
|
args_c[8], args_c[9], args_c[10], args_c[11],
|
314
321
|
args_c[12], args_c[13], args_c[14], args_c[15]);
|
315
|
-
|
322
|
+
|
316
323
|
if (flags_v & 8)
|
317
324
|
return rb_float_new(fake_float());
|
318
325
|
|
@@ -324,18 +331,16 @@ uintptr_t do_callback_handler(uintptr_t cb_id __attribute__((register(rax))),
|
|
324
331
|
uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, uintptr_t arg7)
|
325
332
|
{
|
326
333
|
uintptr_t ret;
|
327
|
-
VALUE args =
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
ptr[6] = INT2VAL(arg6);
|
338
|
-
ptr[7] = INT2VAL(arg7);
|
334
|
+
VALUE args = rb_ary_new();
|
335
|
+
|
336
|
+
rb_ary_push(args, INT2VAL(arg0));
|
337
|
+
rb_ary_push(args, INT2VAL(arg1));
|
338
|
+
rb_ary_push(args, INT2VAL(arg2));
|
339
|
+
rb_ary_push(args, INT2VAL(arg3));
|
340
|
+
rb_ary_push(args, INT2VAL(arg4));
|
341
|
+
rb_ary_push(args, INT2VAL(arg5));
|
342
|
+
rb_ary_push(args, INT2VAL(arg6));
|
343
|
+
rb_ary_push(args, INT2VAL(arg7));
|
339
344
|
|
340
345
|
ret = rb_funcall(dynldr, rb_intern("callback_run"), 2, INT2VAL(cb_id), args);
|
341
346
|
|
@@ -379,8 +384,7 @@ static void *wstrcaseruby(short *s1, int len)
|
|
379
384
|
{
|
380
385
|
int i = 0;
|
381
386
|
int match = 0;
|
382
|
-
|
383
|
-
static char *want = "ruby"; // cant contain the same letter twice
|
387
|
+
char *want = "ruby"; // cant contain the same letter twice
|
384
388
|
|
385
389
|
while (i < len) {
|
386
390
|
if (want[match] == (s1[i] | 0x20)) { // downcase cmp
|
@@ -474,11 +478,11 @@ int load_ruby_imports(uintptr_t rbaddr)
|
|
474
478
|
if (rbaddr)
|
475
479
|
ruby_module = find_ruby_module_mem(rbaddr);
|
476
480
|
else
|
477
|
-
|
481
|
+
ruby_module = find_ruby_module_peb();
|
478
482
|
|
479
483
|
if (!ruby_module)
|
480
484
|
return 0;
|
481
|
-
|
485
|
+
|
482
486
|
ptr = &ruby_import_table;
|
483
487
|
table = (char*)ptr;
|
484
488
|
|
@@ -494,7 +498,7 @@ int load_ruby_imports(uintptr_t rbaddr)
|
|
494
498
|
|
495
499
|
#ifdef __x86_64__
|
496
500
|
#define DLL_PROCESS_ATTACH 1
|
497
|
-
|
501
|
+
int DllMain(void *handle, int reason, void *res)
|
498
502
|
{
|
499
503
|
if (reason == DLL_PROCESS_ATTACH)
|
500
504
|
return load_ruby_imports(0);
|
@@ -509,7 +513,7 @@ EOS
|
|
509
513
|
do_invoke_fastcall:
|
510
514
|
push ebp
|
511
515
|
mov ebp, esp
|
512
|
-
|
516
|
+
|
513
517
|
// load ecx/edx, fix arg/argcount
|
514
518
|
mov eax, [ebp+16]
|
515
519
|
mov ecx, [eax]
|
@@ -627,7 +631,7 @@ EOS
|
|
627
631
|
# save the shared library
|
628
632
|
bin.encode_file(modulename, :lib)
|
629
633
|
end
|
630
|
-
|
634
|
+
|
631
635
|
def self.compile_binary_module_hack(bin)
|
632
636
|
# this is a hack
|
633
637
|
# we need the module to use ruby symbols
|
@@ -720,8 +724,7 @@ EOS
|
|
720
724
|
# find the path of the binary module
|
721
725
|
# if none exists, create a path writeable by the current user
|
722
726
|
def self.find_bin_path
|
723
|
-
fname = ['dynldr', host_arch, host_cpu.shortname,
|
724
|
-
('19' if RUBY_VERSION >= '1.9')].compact.join('-') + '.so'
|
727
|
+
fname = ['dynldr', host_arch, host_cpu.shortname, RUBY_VERSION.gsub('.', '')].join('-') + '.so'
|
725
728
|
dir = File.dirname(__FILE__)
|
726
729
|
binmodule = File.join(dir, fname)
|
727
730
|
if not File.exists? binmodule or File.stat(binmodule).mtime < File.stat(__FILE__).mtime
|
@@ -765,7 +768,7 @@ EOS
|
|
765
768
|
else raise LoadError, "Unsupported host platform #{RUBY_PLATFORM}"
|
766
769
|
end
|
767
770
|
end
|
768
|
-
|
771
|
+
|
769
772
|
# returns whether we run on linux or windows
|
770
773
|
def self.host_arch
|
771
774
|
case RUBY_PLATFORM
|
@@ -788,16 +791,73 @@ EOS
|
|
788
791
|
cp.parse(src)
|
789
792
|
end
|
790
793
|
|
791
|
-
# compile a C fragment into a
|
794
|
+
# compile a C fragment into a Shellcode_RWX, honors the host ABI
|
792
795
|
def self.compile_c(src)
|
793
796
|
# XXX could we reuse self.cp ? (for its macros etc)
|
794
797
|
cp = C::Parser.new(host_exe.new(host_cpu))
|
795
798
|
cp.parse(src)
|
796
|
-
sc =
|
799
|
+
sc = Shellcode_RWX.new(host_cpu)
|
797
800
|
asm = host_cpu.new_ccompiler(cp, sc).compile
|
798
801
|
sc.assemble(asm)
|
799
802
|
end
|
800
803
|
|
804
|
+
# maps a Shellcode_RWX in memory, fixup stdlib relocations
|
805
|
+
# returns the Shellcode_RWX, with the base_r/w/x initialized to the allocated memory
|
806
|
+
def self.sc_map_resolve(sc)
|
807
|
+
sc_map_resolve_addthunks(sc)
|
808
|
+
|
809
|
+
sc.base_r = memory_alloc(sc.encoded_r.length) if sc.encoded_r.length > 0
|
810
|
+
sc.base_w = memory_alloc(sc.encoded_w.length) if sc.encoded_w.length > 0
|
811
|
+
sc.base_x = memory_alloc(sc.encoded_x.length) if sc.encoded_x.length > 0
|
812
|
+
|
813
|
+
locals = sc.encoded_r.export.keys | sc.encoded_w.export.keys | sc.encoded_x.export.keys
|
814
|
+
exts = sc.encoded_r.reloc_externals(locals) | sc.encoded_w.reloc_externals(locals) | sc.encoded_x.reloc_externals(locals)
|
815
|
+
bd = {}
|
816
|
+
exts.uniq.each { |ext| bd[ext] = sym_addr(lib_from_sym(ext), ext) or raise rescue raise "unknown symbol #{ext.inspect}" }
|
817
|
+
sc.fixup_check(bd)
|
818
|
+
|
819
|
+
memory_write sc.base_r, sc.encoded_r.data if sc.encoded_r.length > 0
|
820
|
+
memory_write sc.base_w, sc.encoded_w.data if sc.encoded_w.length > 0
|
821
|
+
memory_write sc.base_x, sc.encoded_x.data if sc.encoded_x.length > 0
|
822
|
+
|
823
|
+
memory_perm sc.base_r, sc.encoded_r.length, 'r' if sc.encoded_r.length > 0
|
824
|
+
memory_perm sc.base_w, sc.encoded_w.length, 'rw' if sc.encoded_w.length > 0
|
825
|
+
memory_perm sc.base_x, sc.encoded_x.length, 'rx' if sc.encoded_x.length > 0
|
826
|
+
|
827
|
+
sc
|
828
|
+
end
|
829
|
+
|
830
|
+
def self.sc_map_resolve_addthunks(sc)
|
831
|
+
case host_cpu.shortname
|
832
|
+
when 'x64'
|
833
|
+
# patch 'call moo' into 'call thunk; thunk: jmp qword [moo_ptr]'
|
834
|
+
# this is similar to ELF PLT section, allowing code to call
|
835
|
+
# into a library mapped more than 4G away
|
836
|
+
# XXX handles only 'call extern', not 'lea reg, extern' or anything else
|
837
|
+
# in this case, the linker will still raise an 'immediate overflow'
|
838
|
+
# during fixup_check in sc_map_resolve
|
839
|
+
[sc.encoded_r, sc.encoded_w, sc.encoded_x].each { |edata|
|
840
|
+
edata.reloc.dup.each { |off, rel|
|
841
|
+
# target only call extern / jmp.i32 extern
|
842
|
+
next if rel.type != :i32
|
843
|
+
next if rel.target.op != :-
|
844
|
+
next if edata.export[rel.target.rexpr] != off+4
|
845
|
+
next if edata.export[rel.target.lexpr]
|
846
|
+
opc = edata.data[off-1, 1].unpack('C')[0]
|
847
|
+
next if opc != 0xe8 and opc != 0xe9
|
848
|
+
|
849
|
+
thunk_sc = Shellcode.new(host_cpu).share_namespace(sc)
|
850
|
+
thunk = thunk_sc.assemble(<<EOS).encoded
|
851
|
+
1: jmp qword [rip]
|
852
|
+
dq #{rel.target.lexpr}
|
853
|
+
EOS
|
854
|
+
edata << thunk
|
855
|
+
rel.target.lexpr = thunk.inv_export[0]
|
856
|
+
}
|
857
|
+
}
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
801
861
|
# retrieve the library where a symbol is to be found (uses AutoImport)
|
802
862
|
def self.lib_from_sym(symname)
|
803
863
|
case host_arch
|
@@ -821,7 +881,7 @@ EOS
|
|
821
881
|
cp.toplevel.symbol.delete v.name
|
822
882
|
lib = fromlib || lib_from_sym(v.name)
|
823
883
|
addr = sym_addr(lib, v.name)
|
824
|
-
|
884
|
+
if addr == 0 or addr == -1 or addr == 0xffff_ffff or addr == 0xffffffff_ffffffff
|
825
885
|
api_not_found(lib, v)
|
826
886
|
next
|
827
887
|
end
|
@@ -912,11 +972,11 @@ EOS
|
|
912
972
|
flags |= 4 if proto.type.type.integral? and cp.sizeof(nil, proto.type.type) == 8
|
913
973
|
flags |= 8 if proto.type.type.float?
|
914
974
|
class << self ; self ; end.send(:define_method, name) { |*a|
|
915
|
-
raise ArgumentError, "bad arg count for #{name}: #{a.length} for #{proto.type.args.length}" if a.length != proto.type.args.length and not proto.type.varargs
|
975
|
+
raise ArgumentError, "bad arg count for #{name}: #{a.length} for #{proto.type.args.to_a.length}" if a.length != proto.type.args.to_a.length and not proto.type.varargs
|
916
976
|
|
917
977
|
# convert the arglist suitably for raw_invoke
|
918
978
|
auto_cb = [] # list of automatic C callbacks generated from lambdas
|
919
|
-
a = a.zip(proto.type.args).map { |ra, fa|
|
979
|
+
a = a.zip(proto.type.args.to_a).map { |ra, fa|
|
920
980
|
aa = convert_rb2c(fa, ra, :cb_list => auto_cb)
|
921
981
|
if fa and fa.type.integral? and cp.sizeof(fa) == 8 and host_cpu.size == 32
|
922
982
|
aa = [aa & 0xffff_ffff, (aa >> 32) & 0xffff_ffff]
|
@@ -965,6 +1025,10 @@ EOS
|
|
965
1025
|
raise "invalid callback #{'%x' % id} not in #{@@callback_table.keys.map { |c| c.to_s(16) }}" if not cb
|
966
1026
|
|
967
1027
|
rawargs = args.dup
|
1028
|
+
if host_cpu.shortname == 'ia32' and (not cb[:proto_ori] or not cb[:proto_ori].has_attribute('fastcall'))
|
1029
|
+
rawargs.shift
|
1030
|
+
rawargs.shift
|
1031
|
+
end
|
968
1032
|
ra = cb[:proto] ? cb[:proto].args.map { |fa| convert_cbargs_c2rb(fa, rawargs) } : []
|
969
1033
|
|
970
1034
|
# run it
|
@@ -995,6 +1059,7 @@ EOS
|
|
995
1059
|
# XXX val is an integer, how to decode Floats etc ? raw binary ptr ?
|
996
1060
|
def self.convert_c2rb(formal, val)
|
997
1061
|
formal = formal.type if formal.kind_of? C::Variable
|
1062
|
+
val &= (1 << 8*cp.sizeof(formal))-1 if formal.integral?
|
998
1063
|
val = Expression.make_signed(val, 8*cp.sizeof(formal)) if formal.integral? and formal.signed?
|
999
1064
|
val = nil if formal.pointer? and val == 0
|
1000
1065
|
val
|
@@ -1019,13 +1084,8 @@ EOS
|
|
1019
1084
|
if (v and v.initializer) or cp.toplevel.statements.find { |st| st.kind_of? C::Asm }
|
1020
1085
|
cp.toplevel.statements.delete_if { |st| st.kind_of? C::Asm }
|
1021
1086
|
cp.toplevel.symbol.delete v.name if v
|
1022
|
-
sc = compile_c(proto)
|
1023
|
-
|
1024
|
-
sc.base_addr = ptr
|
1025
|
-
# TODO fixup external calls
|
1026
|
-
memory_write ptr, sc.encode_string
|
1027
|
-
memory_perm ptr, sc.encoded.length, 'rwx'
|
1028
|
-
ptr
|
1087
|
+
sc = sc_map_resolve(compile_c(proto))
|
1088
|
+
sc.base_x
|
1029
1089
|
elsif not v
|
1030
1090
|
raise 'empty prototype'
|
1031
1091
|
else
|
@@ -1044,6 +1104,7 @@ EOS
|
|
1044
1104
|
cb[:id] = id
|
1045
1105
|
cb[:proc] = b
|
1046
1106
|
cb[:proto] = proto
|
1107
|
+
cb[:proto_ori] = ori
|
1047
1108
|
cb[:abi_stackfix] = proto.args.inject(0) { |s, a| s + [cp.sizeof(a), cp.typesize[:ptr]].max } if ori and ori.has_attribute('stdcall')
|
1048
1109
|
cb[:abi_stackfix] = proto.args[2..-1].to_a.inject(0) { |s, a| s + [cp.sizeof(a), cp.typesize[:ptr]].max } if ori and ori.has_attribute('fastcall') # supercedes stdcall
|
1049
1110
|
@@callback_table[id] = cb
|
@@ -1058,29 +1119,34 @@ EOS
|
|
1058
1119
|
# finds a free callback id, allocates a new page if needed
|
1059
1120
|
def self.callback_find_id
|
1060
1121
|
if not id = @@callback_addrs.find { |a| not @@callback_table[a] }
|
1061
|
-
|
1122
|
+
page_size = 4096
|
1123
|
+
cb_page = memory_alloc(page_size)
|
1062
1124
|
sc = Shellcode.new(host_cpu, cb_page)
|
1063
1125
|
case sc.cpu.shortname
|
1064
1126
|
when 'ia32'
|
1065
|
-
|
1066
|
-
nrcb = 128 # TODO should be 4096/5, but the parser/compiler is really too slow
|
1067
|
-
nrcb.times {
|
1068
|
-
@@callback_addrs << addr
|
1069
|
-
sc.parse "call #{CALLBACK_TARGET}"
|
1070
|
-
addr += 5
|
1071
|
-
}
|
1127
|
+
asm = "call #{CALLBACK_TARGET}"
|
1072
1128
|
when 'x64'
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1129
|
+
if (cb_page - CALLBACK_TARGET).abs >= 0x7fff_f000
|
1130
|
+
# cannot directly 'jmp CB_T'
|
1131
|
+
asm = "1: mov rax, #{CALLBACK_TARGET} push rax lea rax, [rip-$_+1b] ret"
|
1132
|
+
else
|
1133
|
+
asm = "1: lea rax, [rip-$_+1b] jmp #{CALLBACK_TARGET}"
|
1134
|
+
end
|
1135
|
+
else
|
1136
|
+
raise 'Who are you?'
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
# fill the page with valid callbacks
|
1140
|
+
loop do
|
1141
|
+
off = sc.encoded.length
|
1142
|
+
sc.assemble asm
|
1143
|
+
break if sc.encoded.length > page_size
|
1144
|
+
@@callback_addrs << (cb_page + off)
|
1080
1145
|
end
|
1081
|
-
|
1082
|
-
memory_write cb_page, sc.encode_string
|
1083
|
-
memory_perm cb_page,
|
1146
|
+
|
1147
|
+
memory_write cb_page, sc.encode_string[0, page_size]
|
1148
|
+
memory_perm cb_page, page_size, 'rx'
|
1149
|
+
|
1084
1150
|
raise 'callback_alloc bouh' if not id = @@callback_addrs.find { |a| not @@callback_table[a] }
|
1085
1151
|
end
|
1086
1152
|
id
|
@@ -1090,23 +1156,17 @@ EOS
|
|
1090
1156
|
# returns the raw pointer to the code page
|
1091
1157
|
# if given a block, run the block and then undefine all the C functions & free memory
|
1092
1158
|
def self.new_func_c(src)
|
1093
|
-
sc = compile_c(src)
|
1094
|
-
|
1095
|
-
sc.base_addr = ptr
|
1096
|
-
bd = sc.encoded.binding(ptr)
|
1097
|
-
sc.encoded.reloc_externals.uniq.each { |ext| bd[ext] = sym_addr(lib_from_sym(ext), ext) or raise "unknown symbol #{ext}" }
|
1098
|
-
sc.encoded.fixup(bd)
|
1099
|
-
memory_write ptr, sc.encode_string
|
1100
|
-
memory_perm ptr, sc.encoded.length, 'rwx'
|
1159
|
+
sc = sc_map_resolve(compile_c(src))
|
1160
|
+
|
1101
1161
|
parse_c(src) # XXX the Shellcode parser may have defined stuff / interpreted C another way...
|
1102
1162
|
defs = []
|
1103
1163
|
cp.toplevel.symbol.dup.each_value { |v|
|
1104
1164
|
next if not v.kind_of? C::Variable
|
1105
1165
|
cp.toplevel.symbol.delete v.name
|
1106
1166
|
next if not v.type.kind_of? C::Function or not v.initializer
|
1107
|
-
next if not off = sc.
|
1167
|
+
next if not off = sc.encoded_x.export[v.name]
|
1108
1168
|
rbname = c_func_name_to_rb(v.name)
|
1109
|
-
new_caller_for(v, rbname,
|
1169
|
+
new_caller_for(v, rbname, sc.base_x+off)
|
1110
1170
|
defs << rbname
|
1111
1171
|
}
|
1112
1172
|
if block_given?
|
@@ -1114,16 +1174,20 @@ EOS
|
|
1114
1174
|
yield
|
1115
1175
|
ensure
|
1116
1176
|
defs.each { |d| class << self ; self ; end.send(:remove_method, d) }
|
1117
|
-
memory_free
|
1177
|
+
memory_free sc.base_r if sc.base_r
|
1178
|
+
memory_free sc.base_w if sc.base_w
|
1179
|
+
memory_free sc.base_x if sc.base_x
|
1118
1180
|
end
|
1119
1181
|
else
|
1120
|
-
|
1182
|
+
sc.base_x
|
1121
1183
|
end
|
1122
1184
|
end
|
1123
1185
|
|
1124
1186
|
# compile an asm sequence, callable with the ABI of the C prototype given
|
1125
1187
|
# function name comes from the prototype
|
1126
|
-
|
1188
|
+
# the shellcode is mapped in read-only memory unless selfmodifyingcode is true
|
1189
|
+
# note that you can use a .data section for simple writable non-executable memory
|
1190
|
+
def self.new_func_asm(proto, asm, selfmodifyingcode=false)
|
1127
1191
|
proto += "\n;"
|
1128
1192
|
old = cp.toplevel.symbol.keys
|
1129
1193
|
parse_c(proto)
|
@@ -1133,24 +1197,24 @@ EOS
|
|
1133
1197
|
raise "invalid func proto #{proto}" if not f.name or not f.type.kind_of? C::Function or f.initializer
|
1134
1198
|
cp.toplevel.symbol.delete f.name
|
1135
1199
|
|
1136
|
-
sc =
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
memory_write ptr, sc.encode_string
|
1142
|
-
memory_perm ptr, sc.encoded.length, 'rwx'
|
1200
|
+
sc = Shellcode_RWX.assemble(host_cpu, asm)
|
1201
|
+
sc = sc_map_resolve(sc)
|
1202
|
+
if selfmodifyingcode
|
1203
|
+
memory_perm sc.base_x, sc.encoded_x.length, 'rwx'
|
1204
|
+
end
|
1143
1205
|
rbname = c_func_name_to_rb(f.name)
|
1144
|
-
new_caller_for(f, rbname,
|
1206
|
+
new_caller_for(f, rbname, sc.base_x)
|
1145
1207
|
if block_given?
|
1146
1208
|
begin
|
1147
1209
|
yield
|
1148
1210
|
ensure
|
1149
1211
|
class << self ; self ; end.send(:remove_method, rbname)
|
1150
|
-
memory_free
|
1212
|
+
memory_free sc.base_r if sc.base_r
|
1213
|
+
memory_free sc.base_w if sc.base_w
|
1214
|
+
memory_free sc.base_x
|
1151
1215
|
end
|
1152
1216
|
else
|
1153
|
-
|
1217
|
+
sc.base_x
|
1154
1218
|
end
|
1155
1219
|
end
|
1156
1220
|
|
@@ -1234,69 +1298,70 @@ EOS
|
|
1234
1298
|
when :windows
|
1235
1299
|
|
1236
1300
|
new_api_c <<EOS, 'kernel32'
|
1237
|
-
#define PAGE_NOACCESS 0x01
|
1238
|
-
#define PAGE_READONLY 0x02
|
1239
|
-
#define PAGE_READWRITE 0x04
|
1240
|
-
#define PAGE_WRITECOPY 0x08
|
1241
|
-
#define PAGE_EXECUTE 0x10
|
1242
|
-
#define PAGE_EXECUTE_READ 0x20
|
1243
|
-
#define PAGE_EXECUTE_READWRITE 0x40
|
1244
|
-
#define PAGE_EXECUTE_WRITECOPY 0x80
|
1245
|
-
#define PAGE_GUARD 0x100
|
1246
|
-
#define PAGE_NOCACHE 0x200
|
1247
|
-
#define PAGE_WRITECOMBINE 0x400
|
1248
|
-
|
1249
|
-
#define MEM_COMMIT 0x1000
|
1250
|
-
#define MEM_RESERVE 0x2000
|
1251
|
-
#define MEM_DECOMMIT 0x4000
|
1252
|
-
#define MEM_RELEASE 0x8000
|
1253
|
-
#define MEM_FREE 0x10000
|
1254
|
-
#define MEM_PRIVATE 0x20000
|
1255
|
-
#define MEM_MAPPED 0x40000
|
1256
|
-
#define MEM_RESET 0x80000
|
1257
|
-
#define MEM_TOP_DOWN 0x100000
|
1258
|
-
#define MEM_WRITE_WATCH 0x200000
|
1259
|
-
#define MEM_PHYSICAL 0x400000
|
1260
|
-
#define MEM_LARGE_PAGES 0x20000000
|
1261
|
-
#define MEM_4MB_PAGES 0x80000000
|
1301
|
+
#define PAGE_NOACCESS 0x01
|
1302
|
+
#define PAGE_READONLY 0x02
|
1303
|
+
#define PAGE_READWRITE 0x04
|
1304
|
+
#define PAGE_WRITECOPY 0x08
|
1305
|
+
#define PAGE_EXECUTE 0x10
|
1306
|
+
#define PAGE_EXECUTE_READ 0x20
|
1307
|
+
#define PAGE_EXECUTE_READWRITE 0x40
|
1308
|
+
#define PAGE_EXECUTE_WRITECOPY 0x80
|
1309
|
+
#define PAGE_GUARD 0x100
|
1310
|
+
#define PAGE_NOCACHE 0x200
|
1311
|
+
#define PAGE_WRITECOMBINE 0x400
|
1312
|
+
|
1313
|
+
#define MEM_COMMIT 0x1000
|
1314
|
+
#define MEM_RESERVE 0x2000
|
1315
|
+
#define MEM_DECOMMIT 0x4000
|
1316
|
+
#define MEM_RELEASE 0x8000
|
1317
|
+
#define MEM_FREE 0x10000
|
1318
|
+
#define MEM_PRIVATE 0x20000
|
1319
|
+
#define MEM_MAPPED 0x40000
|
1320
|
+
#define MEM_RESET 0x80000
|
1321
|
+
#define MEM_TOP_DOWN 0x100000
|
1322
|
+
#define MEM_WRITE_WATCH 0x200000
|
1323
|
+
#define MEM_PHYSICAL 0x400000
|
1324
|
+
#define MEM_LARGE_PAGES 0x20000000
|
1325
|
+
#define MEM_4MB_PAGES 0x80000000
|
1262
1326
|
|
1263
1327
|
__stdcall uintptr_t VirtualAlloc(uintptr_t addr, uintptr_t size, int type, int prot);
|
1264
1328
|
__stdcall uintptr_t VirtualFree(uintptr_t addr, uintptr_t size, int freetype);
|
1265
1329
|
__stdcall uintptr_t VirtualProtect(uintptr_t addr, uintptr_t size, int prot, int *oldprot);
|
1266
1330
|
EOS
|
1267
|
-
|
1331
|
+
|
1268
1332
|
# allocate some memory suitable for code allocation (ie VirtualAlloc)
|
1269
1333
|
def self.memory_alloc(sz)
|
1270
1334
|
virtualalloc(nil, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)
|
1271
1335
|
end
|
1272
|
-
|
1336
|
+
|
1273
1337
|
# free memory allocated through memory_alloc
|
1274
1338
|
def self.memory_free(addr)
|
1275
1339
|
virtualfree(addr, 0, MEM_RELEASE)
|
1276
1340
|
end
|
1277
|
-
|
1341
|
+
|
1278
1342
|
# change memory permissions - perm in [r rw rx rwx]
|
1279
1343
|
def self.memory_perm(addr, len, perm)
|
1280
1344
|
perm = { 'r' => PAGE_READONLY, 'rw' => PAGE_READWRITE, 'rx' => PAGE_EXECUTE_READ,
|
1281
1345
|
'rwx' => PAGE_EXECUTE_READWRITE }[perm.to_s.downcase]
|
1282
1346
|
virtualprotect(addr, len, perm, str_ptr([0].pack('C')*8))
|
1283
1347
|
end
|
1284
|
-
|
1348
|
+
|
1285
1349
|
when :linux
|
1286
|
-
|
1350
|
+
|
1287
1351
|
new_api_c <<EOS
|
1288
|
-
#define PROT_READ
|
1352
|
+
#define PROT_READ 0x1
|
1289
1353
|
#define PROT_WRITE 0x2
|
1290
|
-
#define PROT_EXEC
|
1354
|
+
#define PROT_EXEC 0x4
|
1291
1355
|
|
1292
|
-
#define MAP_PRIVATE
|
1356
|
+
#define MAP_PRIVATE 0x2
|
1357
|
+
#define MAP_FIXED 0x10
|
1293
1358
|
#define MAP_ANONYMOUS 0x20
|
1294
1359
|
|
1295
1360
|
uintptr_t mmap(uintptr_t addr, uintptr_t length, int prot, int flags, uintptr_t fd, uintptr_t offset);
|
1296
1361
|
uintptr_t munmap(uintptr_t addr, uintptr_t length);
|
1297
1362
|
uintptr_t mprotect(uintptr_t addr, uintptr_t len, int prot);
|
1298
1363
|
EOS
|
1299
|
-
|
1364
|
+
|
1300
1365
|
# allocate some memory suitable for code allocation (ie mmap)
|
1301
1366
|
def self.memory_alloc(sz)
|
1302
1367
|
@mmaps ||= {} # save size for mem_free
|
@@ -1304,26 +1369,48 @@ EOS
|
|
1304
1369
|
@mmaps[a] = sz
|
1305
1370
|
a
|
1306
1371
|
end
|
1307
|
-
|
1372
|
+
|
1308
1373
|
# free memory allocated through memory_alloc
|
1309
1374
|
def self.memory_free(addr)
|
1310
1375
|
munmap(addr, @mmaps[addr])
|
1311
1376
|
end
|
1312
|
-
|
1377
|
+
|
1313
1378
|
# change memory permissions - perm 'rwx'
|
1314
1379
|
# on PaX-enabled systems, this may need a non-mprotect-restricted ruby interpreter
|
1380
|
+
# if a mapping 'rx' is denied, will try to create a file and mmap() it rx in place
|
1315
1381
|
def self.memory_perm(addr, len, perm)
|
1316
1382
|
perm = perm.to_s.downcase
|
1317
1383
|
len += (addr & 0xfff) + 0xfff
|
1318
1384
|
len &= ~0xfff
|
1319
1385
|
addr &= ~0xfff
|
1386
|
+
|
1320
1387
|
p = 0
|
1321
|
-
p |= PROT_READ
|
1322
|
-
p |= PROT_WRITE if perm.include?
|
1323
|
-
p |= PROT_EXEC
|
1324
|
-
|
1388
|
+
p |= PROT_READ if perm.include?('r')
|
1389
|
+
p |= PROT_WRITE if perm.include?('w')
|
1390
|
+
p |= PROT_EXEC if perm.include?('x')
|
1391
|
+
|
1392
|
+
ret = mprotect(addr, len, p)
|
1393
|
+
|
1394
|
+
if ret != 0 and perm.include?('x') and not perm.include?('w') and len > 0 and @memory_perm_wd ||= find_write_dir
|
1395
|
+
# We are on a PaX-mprotected system. Try to use a file mapping to work aroud.
|
1396
|
+
Dir.chdir(@memory_perm_wd) {
|
1397
|
+
fname = 'tmp_mprot_%d_%x' % [Process.pid, addr]
|
1398
|
+
data = memory_read(addr, len)
|
1399
|
+
begin
|
1400
|
+
File.open(fname, 'w') { |fd| fd.write data }
|
1401
|
+
# reopen to ensure filesystem flush
|
1402
|
+
rret = File.open(fname, 'r') { |fd| mmap(addr, len, p, MAP_FIXED|MAP_PRIVATE, fd.fileno, 0) }
|
1403
|
+
raise 'hax' if data != memory_read(addr, len)
|
1404
|
+
ret = 0 if rret == addr
|
1405
|
+
ensure
|
1406
|
+
File.unlink(fname) rescue nil
|
1407
|
+
end
|
1408
|
+
}
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
ret
|
1325
1412
|
end
|
1326
|
-
|
1413
|
+
|
1327
1414
|
end
|
1328
1415
|
end
|
1329
1416
|
end
|