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
@@ -0,0 +1,128 @@
1
+ typedef uintptr_t VALUE;
2
+ static VALUE const_WindowsHeap;
3
+ VALUE rb_ary_new(void);
4
+ VALUE rb_ary_push(VALUE, VALUE);
5
+ extern VALUE *rb_cObject __attribute__((import));
6
+ VALUE rb_const_get(VALUE, VALUE);
7
+ void rb_define_method(VALUE, char*, VALUE(*)(), int);
8
+ VALUE rb_funcall(VALUE recv, unsigned int id, int nargs, ...);
9
+ VALUE rb_intern(char*);
10
+ VALUE rb_iv_get(VALUE, char*);
11
+ VALUE rb_hash_aset(VALUE, VALUE, VALUE);
12
+ VALUE rb_hash_aref(VALUE, VALUE);
13
+ VALUE rb_uint2inum(VALUE);
14
+ VALUE rb_num2ulong(VALUE);
15
+ char *rb_string_value_ptr(VALUE*);
16
+ VALUE rb_gc_enable(void);
17
+ VALUE rb_gc_disable(void);
18
+
19
+ #include "winheap.h"
20
+
21
+ #define INT2FIX(i) (((i) << 1) | 1)
22
+
23
+ static VALUE m_WindowsHeap23scan_heap_segment(VALUE self, VALUE vfirst, VALUE vlen)
24
+ {
25
+ char *heapcpy;
26
+ struct _HEAP_ENTRY *he;
27
+ VALUE chunks;
28
+ VALUE first = rb_num2ulong(vfirst);
29
+ VALUE len = vlen >> 1;
30
+ VALUE off;
31
+ VALUE page;
32
+ VALUE sz;
33
+
34
+ chunks = rb_iv_get(self, "@chunks");
35
+ page = rb_funcall(self, rb_intern("pagecache"), 2, vfirst, INT2FIX(len));
36
+ heapcpy = rb_string_value_ptr(&page);
37
+
38
+ rb_gc_disable();
39
+ off = 0;
40
+ while (off < len) {
41
+ he = heapcpy + off;
42
+ if (he->Flags & 1) {
43
+ sz = (VALUE)he->Size*8;
44
+ if (sz > he->UnusedBytes)
45
+ sz -= he->UnusedBytes;
46
+ else
47
+ sz = 0;
48
+ rb_hash_aset(chunks, rb_uint2inum(first+off+sizeof(*he)), INT2FIX(sz));
49
+ }
50
+ off += he->Size*8;
51
+ }
52
+ rb_gc_enable();
53
+
54
+ return 4;
55
+ }
56
+
57
+ static VALUE m_WindowsHeap23scan_heap_segment_xr(VALUE self, VALUE vfirst, VALUE vlen)
58
+ {
59
+ char *heapcpy;
60
+ struct _HEAP_ENTRY *he;
61
+ VALUE chunks;
62
+ VALUE first = rb_num2ulong(vfirst);
63
+ VALUE len = vlen >> 1;
64
+ VALUE off;
65
+ VALUE page;
66
+ VALUE xrchunksto = rb_iv_get(self, "@xrchunksto");
67
+ VALUE xrchunksfrom = rb_iv_get(self, "@xrchunksfrom");
68
+
69
+ chunks = rb_iv_get(self, "@chunks");
70
+ page = rb_funcall(self, rb_intern("pagecache"), 2, vfirst, INT2FIX(len));
71
+ heapcpy = rb_string_value_ptr(&page);
72
+
73
+ rb_gc_disable();
74
+ off = 0;
75
+ VALUE *ptr0, base, cklen;
76
+ while (off < len) {
77
+ he = heapcpy + off;
78
+ // address of the chunk
79
+ base = first + off + sizeof(*he);
80
+ if ((he->Flags & 1) &&
81
+ (((cklen = rb_hash_aref(chunks, rb_uint2inum(base)))|4) != 4)) {
82
+ cklen /= 2*sizeof(void*); // /2 == FIX2INT
83
+ // pointer to the data for the chunk in our copy of the heap from pagecache
84
+ ptr0 = (VALUE*)(heapcpy + off + sizeof(*he));
85
+ VALUE tabto = 0;
86
+ VALUE tabfrom;
87
+ while (cklen--) {
88
+ VALUE p = *ptr0++;
89
+ //if (p == base) // ignore self-references
90
+ // continue;
91
+ if ((rb_hash_aref(chunks, rb_uint2inum(p))|4) != 4) {
92
+ if (!tabto) {
93
+ tabto = rb_ary_new();
94
+ rb_hash_aset(xrchunksto, rb_uint2inum(base), tabto);
95
+ }
96
+ rb_ary_push(tabto, rb_uint2inum(p));
97
+
98
+ tabfrom = rb_hash_aref(xrchunksfrom, rb_uint2inum(p));
99
+ if ((tabfrom|4) == 4) {
100
+ tabfrom = rb_ary_new();
101
+ rb_hash_aset(xrchunksfrom, rb_uint2inum(p), tabfrom);
102
+ }
103
+ rb_ary_push(tabfrom, rb_uint2inum(base));
104
+ }
105
+ }
106
+ }
107
+ if (!he->Size)
108
+ break;
109
+ off += he->Size*8;
110
+ }
111
+ rb_gc_enable();
112
+
113
+ return 4;
114
+ }
115
+
116
+ static void do_init_once(void)
117
+ {
118
+ const_WindowsHeap = rb_const_get(*rb_cObject, rb_intern("Metasm"));
119
+ const_WindowsHeap = rb_const_get(const_WindowsHeap, rb_intern("WindowsHeap"));
120
+ rb_define_method(const_WindowsHeap, "scan_heap_segment", m_WindowsHeap23scan_heap_segment, 2);
121
+ rb_define_method(const_WindowsHeap, "scan_heap_segment_xr", m_WindowsHeap23scan_heap_segment_xr, 2);
122
+ }
123
+
124
+ int Init_compiled_heapscan_win __attribute__((export))(void)
125
+ {
126
+ do_init_once();
127
+ return 0;
128
+ }
@@ -0,0 +1,616 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+ module ::Metasm
7
+ module Gui
8
+ class GraphHeapWidget < GraphViewWidget
9
+ attr_accessor :heap, :addr_struct, :snapped
10
+ # addr_struct = 0x234 => AllocCStruct
11
+
12
+ def set_color_arrow(b1, b2)
13
+ if b1 == @caret_box or b2 == @caret_box
14
+ draw_color :arrow_hl
15
+ else
16
+ draw_color :arrow_cond
17
+ end
18
+ end
19
+
20
+ def setup_contextmenu(b, m)
21
+ addsubmenu(m, '_follow pointer') {
22
+ next if not lm = b[:line_member][@caret_y]
23
+ addr = b[:line_struct][@caret_y][lm]
24
+ next if not @heap.chunks[addr]
25
+ if lm.kind_of?(::Integer)
26
+ t = b[:line_struct][@caret_y].struct.type
27
+ else
28
+ t = lm.type
29
+ end
30
+ if t.pointer? and t.pointed.untypedef.kind_of? C::Union
31
+ @heap.chunk_struct[addr] ||= t.pointed.untypedef
32
+ end
33
+ st = @heap.chunk_struct[addr] || create_struct(addr)
34
+ ed = @dasm.get_edata_at(addr)
35
+ @addr_struct[addr] = @heap.cp.decode_c_struct(st.name, ed.data, ed.ptr)
36
+ gui_update
37
+ }
38
+ addsubmenu(m, '_hide box') {
39
+ @selected_boxes.each { |sb|
40
+ @addr_struct.delete sb.id if @addr_struct.length > 1
41
+ }
42
+ @curcontext.root_addrs = struct_find_roots(@addr_struct.keys.first)
43
+ gui_update
44
+ }
45
+ super(b, m)
46
+ end
47
+
48
+ def keypress(k)
49
+ case k
50
+ when ?u
51
+ # update display (refresh struct member values)
52
+ @parent_widget.parent_widget.console.commands['refresh'][]
53
+ gui_update
54
+ when ?t
55
+ # change struct field type
56
+ if @selected_boxes.length > 1
57
+ # mass-retype chunks
58
+ st = @addr_struct[@selected_boxes[0].id].struct
59
+ inputbox("replacement struct for selected chunks", :text => st.name) { |n|
60
+ next if not nst = @heap.cp.toplevel.struct[n]
61
+ @selected_boxes.each { |sb|
62
+ as = @addr_struct[sb.id]
63
+ @heap.chunk_struct[sb.id] = nst
64
+ @addr_struct[sb.id] = @heap.cp.decode_c_struct(n, as.str, as.stroff)
65
+ }
66
+ gui_update
67
+ }
68
+ elsif b = @caret_box
69
+ if @caret_y == 0
70
+ as = @addr_struct[b.id]
71
+ st = as.struct
72
+ inputbox("replacement struct for #{st.name}", :text => st.name) { |n|
73
+ next if not nst = @heap.cp.toplevel.struct[n]
74
+ @heap.chunk_struct[b.id] = nst
75
+ @addr_struct[b.id] = @heap.cp.decode_c_struct(n, as.str, as.stroff)
76
+ gui_update
77
+ }
78
+ elsif m = b[:line_member][@caret_y]
79
+ if m.kind_of?(Integer)
80
+ # XXX Array, need to find the outer struct
81
+ mn = b[:line_text_col][@caret_y].map { |l, c| l }.join[/(\S*)\[/, 1]
82
+ ar = b[:line_struct][@caret_y]
83
+ st = b[:line_struct][0...@caret_y].reverse.compact.find { |st_| st_.struct.kind_of?(C::Struct) and st_[mn].struct == ar.struct }
84
+ raise '?' if not st
85
+ st = st.struct
86
+ m = st.fldlist[mn]
87
+ else
88
+ st = b[:line_struct][@caret_y].struct
89
+ end
90
+ inputbox("new type for #{m.name}", :text => m.dump_def(@heap.cp.toplevel)[0].join(' ')) { |nn|
91
+ nil while @heap.cp.readtok
92
+ @heap.cp.lexer.feed nn
93
+ if not v = C::Variable.parse_type(@heap.cp, @heap.cp.toplevel, true)
94
+ nil while @heap.cp.readtok
95
+ raise 'bad type'
96
+ end
97
+ v.parse_declarator(@heap.cp, @heap.cp.toplevel)
98
+ nt = v.type
99
+ nsz = @heap.cp.sizeof(nt)
100
+ osz = @heap.cp.sizeof(m)
101
+ if nsz > osz and st.kind_of?(C::Struct)
102
+ idx = st.members.index(m)
103
+ # eat next members
104
+ while nsz > osz
105
+ break if idx+1 >= st.members.length
106
+ sz = @heap.cp.sizeof(st.members.delete_at(idx+1))
107
+ osz += sz
108
+ end
109
+ end
110
+ if nsz < osz and st.kind_of?(C::Struct)
111
+ idx = st.members.index(m)
112
+ pos = st.offsetof(@heap.cp, m)
113
+ # fill gap with bytes
114
+ idx += 1
115
+ while nsz < osz
116
+ st.members[idx, 0] = [C::Variable.new(('unk_%x' % (pos+nsz)), C::BaseType.new(:__int8, :unsigned))]
117
+ idx += 1
118
+ nsz += 1
119
+ end
120
+ end
121
+ m.type = nt
122
+ st.update_member_cache(@heap.cp)
123
+ gui_update
124
+ }
125
+
126
+ end
127
+ end
128
+ when ?n
129
+ # rename struct field
130
+ if b = @caret_box
131
+ if @caret_y == 0
132
+ st = @addr_struct[b.id].struct
133
+ inputbox("new name for #{st.name}", :text => st.name) { |nn|
134
+ raise "struct #{nn} already exists (try 't')" if @heap.cp.toplevel.struct[nn]
135
+ @heap.cp.toplevel.struct[nn] = @heap.cp.toplevel.struct.delete(st.name)
136
+ st.name = nn
137
+ gui_update
138
+ }
139
+ elsif m = b[:line_member][@caret_y]
140
+ if m.kind_of?(Integer)
141
+ mn = b[:line_text_col][@caret_y].map { |l, c| l }.join[/(\S*)\[/, 1]
142
+ ar = b[:line_struct][@caret_y]
143
+ st = b[:line_struct][0...@caret_y].reverse.compact.find { |st_| st_.struct.kind_of?(C::Struct) and st_[mn].struct == ar.struct }
144
+ raise '?' if not st
145
+ st = st.struct
146
+ m = st.fldlist[mn]
147
+ else
148
+ st = b[:line_struct][@caret_y].struct
149
+ end
150
+ inputbox("new name for #{m.name}", :text => m.name) { |nn|
151
+ m.name = nn
152
+ st.update_member_cache(@heap.cp)
153
+ gui_update
154
+ }
155
+ end
156
+ end
157
+ when ?e
158
+ # edit struct field value under the cursor
159
+ if b = @caret_box
160
+ # TODO b[:struct][line], b.[:member][line] (int for Arrays)
161
+ st = b[:line_struct][@caret_y]
162
+ mb = b[:line_member][@caret_y]
163
+ if st and mb
164
+ if mb.kind_of?(C::Variable) and mb.type.kind_of?(C::Array) and mb.type.type.kind_of?(C::BaseType) and mb.type.type.name == :char
165
+ defval = st[mb].to_array.pack('C*').gsub(/\0*$/, '').gsub(/[^\x20-\x7e]/, '.')
166
+ string = true
167
+ else
168
+ defval = st[mb]
169
+ string = false
170
+ end
171
+ inputbox("new value for #{mb.respond_to?(:name) ? mb.name : mb}", :text => defval.to_s) { |nn|
172
+ if string
173
+ am = st[mb]
174
+ (nn.unpack('C*') + [0]).each_with_index { |b_, i| am[i] = b_ }
175
+ else
176
+ st[mb] = Expression.parse_string(nn).reduce
177
+ end
178
+ gui_update
179
+ }
180
+ end
181
+ end
182
+ when ?x
183
+ # show heap xrefs to the hilighted chunk
184
+ if b = @caret_box
185
+ list = [['address', 'size']]
186
+ @heap.xrchunksfrom[b.id].to_a.each { |a|
187
+ list << [Expression[a], Expression[@heap.chunks[a]]]
188
+ }
189
+ if list.length == 1
190
+ messagebox "no xref to #{Expression[b.id]}"
191
+ else
192
+ listwindow("heap xrefs to #{Expression[b.id]}", list) { |i| @parent_widget.focus_addr(i[0], nil, true) }
193
+ end
194
+ end
195
+ when ?I
196
+ # insert new field in struct
197
+ if b = @caret_box
198
+ if m = b[:line_member][@caret_y]
199
+ if m.kind_of?(Integer)
200
+ # XXX Array, need to find the outer struct
201
+ mn = b[:line_text_col][@caret_y].map { |l, c| l }.join[/(\S*)\[/, 1]
202
+ ar = b[:line_struct][@caret_y]
203
+ st = b[:line_struct][0...@caret_y].reverse.compact.find { |st_| st_.struct.kind_of?(C::Struct) and st_[mn].struct == ar.struct }
204
+ raise '?' if not st
205
+ st = st.struct
206
+ m = st.fldlist[mn]
207
+ else
208
+ st = b[:line_struct][@caret_y].struct
209
+ end
210
+ inputbox("new type to insert before #{m.name}", :text => m.dump_def(@heap.cp.toplevel)[0].join(' ')) { |nn|
211
+ nil while @heap.cp.readtok
212
+ @heap.cp.lexer.feed nn
213
+ if not v = C::Variable.parse_type(@heap.cp, @heap.cp.toplevel, true)
214
+ nil while @heap.cp.readtok
215
+ raise 'bad type'
216
+ end
217
+ v.parse_declarator(@heap.cp, @heap.cp.toplevel)
218
+ nt = v.type
219
+ idx = st.members.index(m)
220
+ pos = st.offsetof(@heap.cp, m)
221
+ name = oname = v.name || ('unk_%x_new' % pos)
222
+ cntr = 0
223
+ while st.members.find { |m_| m_.name == name }
224
+ name = oname + "_#{cntr+=1}"
225
+ end
226
+ st.members[idx, 0] = [C::Variable.new(name, nt)]
227
+ st.update_member_cache(@heap.cp)
228
+ gui_update
229
+ }
230
+
231
+ end
232
+ end
233
+ when ?S
234
+ # delete structure field
235
+ if b = @caret_box
236
+ if m = b[:line_member][@caret_y]
237
+ if m.kind_of?(Integer)
238
+ # XXX Array, need to find the outer struct
239
+ mn = b[:line_text_col][@caret_y].map { |l, c| l }.join[/(\S*)\[/, 1]
240
+ ar = b[:line_struct][@caret_y]
241
+ st = b[:line_struct][0...@caret_y].reverse.compact.find { |st_| st_.struct.kind_of?(C::Struct) and st_[mn].struct == ar.struct }
242
+ raise '?' if not st
243
+ st = st.struct
244
+ m = st.fldlist[mn]
245
+ else
246
+ st = b[:line_struct][@caret_y].struct
247
+ end
248
+ inputbox("delete #{m.name} ?") { |nn|
249
+ idx = st.members.index(m)
250
+ st.members.delete_at(idx)
251
+ st.update_member_cache(@heap.cp)
252
+ gui_update
253
+ }
254
+
255
+ end
256
+ end
257
+ when ?+
258
+ # append blocks linked from the currently shown blocks to the display
259
+ @addr_struct.keys.each { |ak|
260
+ @heap.xrchunksto[ak].to_a.each { |nt|
261
+ next if @addr_struct[nt]
262
+ # TODO check if the pointer is a some_struct*
263
+ st = @heap.chunk_struct[nt] || create_struct(nt)
264
+ ed = @dasm.get_edata_at(nt)
265
+ @addr_struct[nt] = @heap.cp.decode_c_struct(st.name, ed.data, ed.ptr)
266
+ }
267
+ }
268
+ gui_update
269
+ when ?-
270
+ # remove graph leaves in an attempt to undo ?+
271
+ unk = @addr_struct.keys.find_all { |ak|
272
+ (@heap.xrchunksto[ak].to_a & @addr_struct.keys).empty?
273
+ }
274
+ unk.each { |ak| @addr_struct.delete ak if @addr_struct.length > 1 }
275
+ gui_update
276
+ else return super(k)
277
+ end
278
+ true
279
+ end
280
+
281
+ # create the graph objects in ctx
282
+ def build_ctx(ctx)
283
+ # create boxes
284
+ todo = ctx.root_addrs.dup & @addr_struct.keys
285
+ todo << @addr_struct.keys.first if todo.empty?
286
+ done = []
287
+ while a = todo.shift
288
+ next if done.include? a
289
+ done << a
290
+ ctx.new_box a, :line_text_col => [], :line_address => [], :line_struct => [], :line_member => []
291
+ todo.concat @heap.xrchunksto[a].to_a & @addr_struct.keys
292
+ end
293
+
294
+ # link boxes
295
+ if (@heap.xrchunksto[ctx.box.first.id].to_a & @addr_struct.keys).length == ctx.box.length - 1
296
+ ot = ctx.box[0].id
297
+ ctx.box[1..-1].each { |b_|
298
+ ctx.link_boxes(ot, b_.id)
299
+ }
300
+ else
301
+ ctx.box.each { |b|
302
+ @heap.xrchunksto[b.id].to_a.each { |t|
303
+ ctx.link_boxes(b.id, t) if @addr_struct[t]
304
+ }
305
+ }
306
+ end
307
+
308
+ if snapped
309
+ @datadiff = {}
310
+ end
311
+
312
+ # calc box dimensions/text
313
+ ctx.box.each { |b|
314
+ colstr = []
315
+ curaddr = b.id
316
+ curst = @addr_struct[b.id]
317
+ curmb = nil
318
+ margin = ''
319
+ start_addr = curaddr
320
+ if snapped
321
+ ghosts = snapped[curaddr]
322
+ end
323
+ line = 0
324
+ render = lambda { |str, col| colstr << [str, col] }
325
+ nl = lambda {
326
+ b[:line_address][line] = curaddr
327
+ b[:line_text_col][line] = colstr
328
+ b[:line_struct][line] = curst
329
+ b[:line_member][line] = curmb
330
+ colstr = []
331
+ line += 1
332
+ }
333
+ render_val = lambda { |v|
334
+ if v.kind_of?(::Integer)
335
+ if v > 0x100
336
+ render['0x%X' % v, :text]
337
+ elsif v < -0x100
338
+ render['-0x%X' % -v, :text]
339
+ else
340
+ render[v.to_s, :text]
341
+ end
342
+ elsif not v
343
+ render['NULL', :text]
344
+ else
345
+ render[v.to_s, :text]
346
+ end
347
+ }
348
+ render_st = nil
349
+ render_st_ar = lambda { |ast, m|
350
+ elemt = m.type.untypedef.type.untypedef
351
+ if elemt.kind_of?(C::BaseType) and elemt.name == :char
352
+ render[margin, :text]
353
+ render["#{m.type.type.to_s[1...-1]} #{m.name}[#{m.type.length}] = #{ast[m].to_array.pack('C*').sub(/\0.*$/m, '').inspect}", :text]
354
+ nl[]
355
+ curaddr += ast.cp.sizeof(m)
356
+ else
357
+ t = m.type.type.to_s[1...-1]
358
+ tsz = ast.cp.sizeof(m.type.type)
359
+ fust = curst
360
+ fumb = curmb
361
+ curst = ast[m]
362
+ ast[m].to_array.each_with_index { |v, i|
363
+ curmb = i
364
+ render[margin, :text]
365
+ if elemt.kind_of?(C::Union)
366
+ if m.type.untypedef.type.kind_of?(C::Union)
367
+ render[elemt.kind_of?(C::Struct) ? 'struct ' : 'union ', :text]
368
+ render["#{elemt.name} ", :text] if elemt.name
369
+ else # typedef
370
+ render["#{elemt.to_s[1...-1]} ", :text]
371
+ end
372
+ render_st[v]
373
+ render[" #{m.name}[#{i}]", :text]
374
+ else
375
+ render["#{t} #{m.name}[#{i}] = ", :text]
376
+ render_val[v]
377
+ @datadiff[curaddr] = true if ghosts and ghosts.all? { |g| g[curaddr-start_addr, tsz] == ghosts[0][curaddr-start_addr, tsz] } and ghosts[0][curaddr-start_addr, tsz] != ast.str[curaddr, tsz].to_str
378
+ end
379
+ render[';', :text]
380
+ nl[]
381
+ curaddr += tsz
382
+ }
383
+ curst = fust
384
+ curmb = fumb
385
+ end
386
+ }
387
+ render_st = lambda { |ast|
388
+ st_addr = curaddr
389
+ oldst = curst
390
+ oldmb = curmb
391
+ oldmargin = margin
392
+ render['{', :text]
393
+ nl[]
394
+ margin += ' '
395
+ curst = ast
396
+ ast.struct.members.each { |m|
397
+ curmb = m
398
+ curaddr = st_addr + ast.struct.offsetof(@heap.cp, m)
399
+
400
+ if bo = ast.struct.bitoffsetof(@heap.cp, m)
401
+ # float curaddr to make ghost hilight work on bitfields
402
+ curaddr += (1+bo[0])/1000.0
403
+ end
404
+
405
+ if m.type.untypedef.kind_of?(C::Array)
406
+ render_st_ar[ast, m]
407
+ elsif m.type.untypedef.kind_of?(C::Union)
408
+ render[margin, :text]
409
+ if m.type.kind_of?(C::Union)
410
+ render[m.type.kind_of?(C::Struct) ? 'struct ' : 'union ', :text]
411
+ render["#{m.type.name} ", :text] if m.type.name
412
+ else # typedef
413
+ render["#{m.type.to_s[1...-1]} ", :text]
414
+ end
415
+ oca = curaddr
416
+ render_st[ast[m]]
417
+ nca = curaddr
418
+ curaddr = oca
419
+ render[" #{m.name if m.name};", :text]
420
+ nl[]
421
+ curaddr = nca
422
+ else
423
+ render[margin, :text]
424
+ render["#{m.type.to_s[1...-1]} ", :text]
425
+ render["#{m.name} = ", :text]
426
+ render_val[ast[m]]
427
+ tsz = ast.cp.sizeof(m)
428
+ # TODO bit-level multighosting
429
+ if ghosts and ghosts.all? { |g| g[curaddr.to_i-start_addr, tsz] == ghosts[0][curaddr.to_i-start_addr, tsz] } and ghosts[0][curaddr.to_i-start_addr, tsz] != ast.str[curaddr.to_i, tsz].to_str
430
+ if bo
431
+ ft = C::BaseType.new((bo[0] + bo[1] > 32) ? :__int64 : :__int32)
432
+ v1 = @heap.cp.decode_c_value(ghosts[0][curaddr.to_i-start_addr, tsz], ft, 0)
433
+ v2 = @heap.cp.decode_c_value(ast.str[curaddr.to_i, tsz], ft, 0)
434
+ @datadiff[curaddr] = true if (v1 >> bo[0]) & ((1 << bo[1])-1) != (v2 >> bo[0]) & ((1 << bo[1])-1)
435
+ else
436
+ @datadiff[curaddr] = true
437
+ end
438
+ end
439
+ render[';', :text]
440
+
441
+ if m.type.kind_of?(C::Pointer) and m.type.type.kind_of?(C::BaseType) and m.type.type.name == :char
442
+ if s = @dasm.decode_strz(ast[m], 32)
443
+ render[" // #{s.inspect}", :comment]
444
+ end
445
+ end
446
+ nl[]
447
+ curaddr += tsz
448
+ curaddr = curaddr.to_i if bo
449
+ end
450
+ }
451
+ margin = oldmargin
452
+ curst = oldst
453
+ curmb = oldmb
454
+ render[margin, :text]
455
+ render['}', :text]
456
+ }
457
+ ast = @addr_struct[curaddr]
458
+ render["struct #{ast.struct.name} *#{'0x%X' % curaddr} = ", :text]
459
+ render_st[ast]
460
+ render[';', :text]
461
+ nl[]
462
+
463
+ b.w = b[:line_text_col].map { |strc| strc.map { |s, c| s }.join.length }.max.to_i * @font_width + 2
464
+ b.w += 1 if b.w % 2 == 0
465
+ b.h = line * @font_height
466
+ }
467
+ end
468
+
469
+ def struct_find_roots(addr)
470
+ addr = @addr_struct.keys.find { |a| addr >= a and addr < a+@addr_struct[a].sizeof } if not @addr_struct[addr]
471
+
472
+ todo = [addr]
473
+ done = []
474
+ roots = []
475
+ default_root = nil
476
+ while a = todo.shift
477
+ if done.include?(a) # cycle
478
+ default_root ||= a
479
+ next
480
+ end
481
+ done << a
482
+ newf = @heap.xrchunksfrom[a].to_a & @addr_struct.keys
483
+ if newf.empty?
484
+ roots << a
485
+ else
486
+ todo.concat newf
487
+ end
488
+ end
489
+ roots << default_root if roots.empty? and default_root
490
+
491
+ roots
492
+ end
493
+
494
+ def focus_addr(addr, fu=nil)
495
+ return if @parent_widget and not addr = @parent_widget.normalize(addr)
496
+
497
+ # move window / change curcontext
498
+ if b = @curcontext.box.find { |b_| b_[:line_address].index(addr) }
499
+ @caret_box, @caret_x, @caret_y = b, 0, b[:line_address].rindex(addr)
500
+ @curcontext.view_x += (width/2 / @zoom - width/2)
501
+ @curcontext.view_y += (height/2 / @zoom - height/2)
502
+ @zoom = 1.0
503
+
504
+ focus_xy(b.x, b.y + @caret_y*@font_height)
505
+ update_caret
506
+ elsif addr_struct and (@addr_struct[addr] or @addr_struct.find { |a, s| addr >= a and addr < a+s.sizeof })
507
+ @curcontext = Graph.new 'testic'
508
+ @curcontext.root_addrs = struct_find_roots(addr)
509
+ @want_focus_addr = addr
510
+ gui_update
511
+ elsif @heap.chunks[addr]
512
+ @want_focus_addr = addr
513
+ do_focus_addr(addr)
514
+ else
515
+ return
516
+ end
517
+ true
518
+ end
519
+
520
+ def do_focus_addr(addr)
521
+ st = @heap.chunk_struct[addr] || create_struct(addr)
522
+
523
+ ed = @dasm.get_edata_at(addr)
524
+ @addr_struct = { addr => @heap.cp.decode_c_struct(st.name, ed.data, ed.ptr) }
525
+ gui_update
526
+ end
527
+
528
+ # create the struct chunk_<addr>, register it in @heap.chunk_struct
529
+ def create_struct(addr)
530
+ raise "no chunk here" if not @heap.chunks[addr]
531
+
532
+ ptsz = @dasm.cpu.size/8
533
+
534
+ # check if this is a c++ object with RTTI info
535
+ vptr = @dasm.decode_dword(addr)
536
+ rtti = @dasm.decode_dword(vptr-ptsz)
537
+ case OS.shortname
538
+ when 'winos'
539
+ typeinfo = @dasm.decode_dword(rtti+3*ptsz) if rtti
540
+ if typeinfo and s = @dasm.decode_strz(typeinfo+3*ptsz)
541
+ rtti_name = s[/^(.*)@@$/, 1] # remove trailing @@
542
+ end
543
+ when 'linos'
544
+ typeinfo = @dasm.decode_dword(rtti+ptsz) if rtti
545
+ if typeinfo and s = @dasm.decode_strz(typeinfo)
546
+ rtti_name = s[/^[0-9]+(.*)$/, 1] # remove leading number
547
+ end
548
+ end
549
+
550
+ if rtti_name and st = @heap.cp.toplevel.struct[rtti_name]
551
+ return @heap.chunk_struct[addr] = st
552
+ end
553
+
554
+ st = C::Struct.new
555
+ st.name = rtti_name || "chunk_#{'%x' % addr}"
556
+ st.members = []
557
+ li = 0
558
+ (@heap.chunks[addr] / ptsz).times { |i|
559
+ n = 'unk_%x' % (ptsz*i)
560
+ v = @dasm.decode_dword(addr+ptsz*i)
561
+ if i == 0 and rtti_name
562
+ t = C::Pointer.new(C::Pointer.new(C::BaseType.new(:void)))
563
+ n = 'vtable'
564
+ elsif @heap.chunks[v]
565
+ t = C::Pointer.new(C::BaseType.new(:void))
566
+ else
567
+ t = C::BaseType.new("__int#{ptsz*8}".to_sym, :unsigned)
568
+ end
569
+ st.members << C::Variable.new(n, t)
570
+ li = i+1
571
+ }
572
+ (@heap.chunks[addr] % ptsz).times { |i|
573
+ n = 'unk_%x' % (ptsz*li+i)
574
+ t = C::BaseType.new(:char, :unsigned)
575
+ st.members << C::Variable.new(n, t)
576
+ }
577
+ @heap.cp.toplevel.struct[st.name] = st
578
+ @heap.chunk_struct[addr] = st
579
+ end
580
+
581
+ def snap
582
+ if not snapped
583
+ @datadiff = {}
584
+ ocb = @parent_widget.bg_color_callback
585
+ @parent_widget.bg_color_callback = lambda { |a|
586
+ if @datadiff[a]
587
+ 'f88'
588
+ elsif ocb
589
+ ocb[a]
590
+ end
591
+ }
592
+ end
593
+ @snapped = {}
594
+ @addr_struct.each { |a, ast|
595
+ @snapped[a] = [ast.str[ast.stroff, ast.sizeof].to_str]
596
+ }
597
+ end
598
+
599
+ def snap_add
600
+ return snap if not snapped
601
+ @addr_struct.each { |a, ast|
602
+ (@snapped[a] ||= []) << ast.str[ast.stroff, ast.sizeof].to_str
603
+ }
604
+ end
605
+
606
+ def get_cursor_pos
607
+ [super, addr_struct]
608
+ end
609
+
610
+ def set_cursor_pos(p)
611
+ s, @addr_struct = p
612
+ super(s)
613
+ end
614
+ end
615
+ end
616
+ end