metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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