metasm 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. 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 rb_ary_new2(int len);
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 Qtrue;
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 = rb_ary_new2(8);
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=0U ; i<8U ; ++i)
268
- ARY_PTR(args)[i] = INT2VAL(*addr++);
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 = rb_ary_new2(8);
328
- VALUE *ptr = ARY_PTR(args);
329
-
330
- RArray(args)->len = 8;
331
- ptr[0] = INT2VAL(arg0);
332
- ptr[1] = INT2VAL(arg1);
333
- ptr[2] = INT2VAL(arg2);
334
- ptr[3] = INT2VAL(arg3);
335
- ptr[4] = INT2VAL(arg4);
336
- ptr[5] = INT2VAL(arg5);
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
- ruby_module = find_ruby_module_peb();
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
- __stdcall int DllMain(void *handle, int reason, void *res)
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 Shellcode, honors the host ABI
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 = Shellcode.new(host_cpu)
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
- if addr == 0 or addr == -1 or addr == 0xffff_ffff or addr == 0xffffffff_ffffffff
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
- ptr = memory_alloc(sc.encoded.length)
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
- cb_page = memory_alloc(4096)
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
- addr = cb_page
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
- addr = cb_page
1074
- nrcb = 128 # same remark
1075
- nrcb.times {
1076
- @@callback_addrs << addr
1077
- sc.parse "1: lea rax, [rip-$_+1b] jmp #{CALLBACK_TARGET}"
1078
- addr += 12 # XXX approximative..
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
- sc.assemble
1082
- memory_write cb_page, sc.encode_string
1083
- memory_perm cb_page, 4096, 'rx'
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
- ptr = memory_alloc(sc.encoded.length)
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.encoded.export[v.name]
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, ptr+off)
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 ptr
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
- ptr
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
- def self.new_func_asm(proto, asm)
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 = Shellcode.assemble(host_cpu, asm)
1137
- ptr = memory_alloc(sc.encoded.length)
1138
- bd = sc.encoded.binding(ptr)
1139
- sc.encoded.reloc_externals.uniq.each { |ext| bd[ext] = sym_addr(lib_from_sym(ext), ext) or raise "unknown symbol #{ext}" }
1140
- sc.encoded.fixup(bd)
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, ptr)
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 ptr
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
- ptr
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 0x1
1352
+ #define PROT_READ 0x1
1289
1353
  #define PROT_WRITE 0x2
1290
- #define PROT_EXEC 0x4
1354
+ #define PROT_EXEC 0x4
1291
1355
 
1292
- #define MAP_PRIVATE 0x2
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 if perm.include? 'r'
1322
- p |= PROT_WRITE if perm.include? 'w'
1323
- p |= PROT_EXEC if perm.include? 'x'
1324
- mprotect(addr, len, p)
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