metasm 1.0.0

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 (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. metadata +255 -0
@@ -0,0 +1,228 @@
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
+
7
+ #
8
+ # This sample defines an ApiHook class, that you can subclass to easily hook functions
9
+ # in a debugged process. Your custom function will get called whenever an API function is,
10
+ # giving you access to the arguments, you can also take control just before control returns
11
+ # to the caller.
12
+ # See the example in the end for more details.
13
+ # As a standalone application, it hooks WriteFile in the 'notepad' process, and make it
14
+ # skip the first two bytes of the buffer.
15
+ #
16
+
17
+ require 'metasm'
18
+
19
+ class ApiHook
20
+ # rewrite this function to list the hooks you want
21
+ # return an array of hashes
22
+ def setup
23
+ #[{ :function => 'WriteFile', :abi => :stdcall }, # standard function hook
24
+ # { :module => 'Foo.dll', :rva => 0x2433, # arbitrary code hook
25
+ # :abi => :fastcall, :hookname => 'myhook' }] # hooks named pre_myhook/post_myhook
26
+ end
27
+
28
+ # initialized from a Debugger or a process description that will be debugged
29
+ # sets the hooks up, then run_forever
30
+ def initialize(dbg)
31
+ if not dbg.kind_of? Metasm::Debugger
32
+ process = Metasm::OS.current.find_process(dbg)
33
+ raise 'no such process' if not process
34
+ dbg = process.debugger
35
+ end
36
+ dbg.loadallsyms
37
+ @dbg = dbg
38
+ setup.each { |h| setup_hook(h) }
39
+ init_prerun if respond_to?(:init_prerun) # allow subclass to do stuff before main loop
40
+ @dbg.run_forever
41
+ end
42
+
43
+ # setup one function hook
44
+ def setup_hook(h)
45
+ pre = "pre_#{h[:hookname] || h[:function]}"
46
+ post = "post_#{h[:hookname] || h[:function]}"
47
+
48
+ @nargs = h[:nargs] || method(pre).arity if respond_to?(pre)
49
+
50
+ if target = h[:address]
51
+ elsif target = h[:rva]
52
+ modbase = @dbg.modulemap[h[:module]]
53
+ raise "cant find module #{h[:module]} in #{@dbg.modulemap.join(', ')}" if not modbase
54
+ target += modbase[0]
55
+ else
56
+ target = h[:function]
57
+ end
58
+
59
+ @dbg.bpx(target) {
60
+ catch(:finish) {
61
+ @cur_abi = h[:abi]
62
+ @ret_longlong = h[:ret_longlong]
63
+ if respond_to? pre
64
+ args = read_arglist
65
+ send pre, *args
66
+ end
67
+ if respond_to? post
68
+ @dbg.bpx(@dbg.func_retaddr, true) {
69
+ retval = read_ret
70
+ send post, retval, args
71
+ }
72
+ end
73
+ }
74
+ }
75
+ end
76
+
77
+ # retrieve the arglist at func entry, from @nargs & @cur_abi
78
+ def read_arglist
79
+ nr = @nargs
80
+ args = []
81
+
82
+ if (@cur_abi == :fastcall or @cur_abi == :thiscall) and nr > 0
83
+ args << @dbg.get_reg_value(:ecx)
84
+ nr -= 1
85
+ end
86
+
87
+ if @cur_abi == :fastcall and nr > 0
88
+ args << @dbg.get_reg_value(:edx)
89
+ nr -= 1
90
+ end
91
+
92
+ nr.times { |i| args << @dbg.func_arg(i) }
93
+
94
+ args
95
+ end
96
+
97
+ # retrieve the function returned value
98
+ def read_ret
99
+ ret = @dbg.func_retval
100
+ if @ret_longlong
101
+ ret = (ret & 0xffffffff) | (@dbg[:edx] << 32)
102
+ end
103
+ ret
104
+ end
105
+
106
+ # patch the value of an argument
107
+ # only valid in pre_hook
108
+ # nr starts at 0
109
+ def patch_arg(nr, value)
110
+ case @cur_abi
111
+ when :fastcall
112
+ case nr
113
+ when 0
114
+ @dbg.set_reg_value(:ecx, value)
115
+ return
116
+ when 1
117
+ @dbg.set_reg_value(:edx, value)
118
+ return
119
+ else
120
+ nr -= 2
121
+ end
122
+ when :thiscall
123
+ case nr
124
+ when 0
125
+ @dbg.set_reg_value(:ecx, value)
126
+ return
127
+ else
128
+ nr -= 1
129
+ end
130
+ end
131
+
132
+ @dbg.func_arg_set(nr, value)
133
+ end
134
+
135
+ # patch the function return value
136
+ # only valid post_hook
137
+ def patch_ret(val)
138
+ if @ret_longlong
139
+ @dbg.set_reg_value(:edx, (val >> 32) & 0xffffffff)
140
+ val &= 0xffffffff
141
+ end
142
+ @dbg.func_retval_set(val)
143
+ end
144
+
145
+ # skip the function call
146
+ # only valid in pre_hook
147
+ def finish(retval)
148
+ patch_ret(retval)
149
+ @dbg.ip = @dbg.func_retaddr
150
+ case @cur_abi
151
+ when :fastcall
152
+ @dbg[:esp] += 4*(@nargs-2) if @nargs > 2
153
+ when :thiscall
154
+ @dbg[:esp] += 4*(@nargs-1) if @nargs > 1
155
+ when :stdcall
156
+ @dbg[:esp] += 4*@nargs
157
+ end
158
+ @dbg.sp += @dbg.cpu.size/8
159
+ throw :finish
160
+ end
161
+ end
162
+
163
+
164
+
165
+ if __FILE__ == $0
166
+
167
+ # this is the class you have to define to hook
168
+ #
169
+ # setup() defines the list of hooks as an array of hashes
170
+ # for exported functions, simply use :function => function name
171
+ # for arbitrary hook, :module => 'module.dll', :rva => 0x1234, :hookname => 'myhook' (call pre_myhook/post_myhook)
172
+ # :abi can be :stdcall (windows standard export), :fastcall or :thiscall, leave empty for cdecl
173
+ # if pre_<function> is defined, it is called whenever the function is entered, via a bpx (int3)
174
+ # if post_<function> is defined, it is called whenever the function exists, with a bpx on the return address setup at func entry
175
+ # the pre_hook receives all arguments to the original function
176
+ # change them with patch_arg(argnr, newval)
177
+ # read memory with @dbg.memory_read_int(ptr), or @dbg.memory[ptr, length]
178
+ # skip the function call with finish(fake_retval) (!) needs a correct :abi & param count !
179
+ # the post_hook receives the function return value
180
+ # change it with patch_ret(newval)
181
+ class MyHook < ApiHook
182
+ def setup
183
+ [{ :function => 'WriteFile', :abi => :stdcall }]
184
+ end
185
+
186
+ def init_prerun
187
+ puts "hooks ready, save a file in notepad"
188
+ end
189
+
190
+ def pre_WriteFile(handle, pbuf, size, pwritten, overlap)
191
+ # we can skip the function call with this
192
+ #finish(28)
193
+
194
+ # spy on the api / trace calls
195
+ bufdata = @dbg.memory[pbuf, size]
196
+ puts "writing #{bufdata.inspect}"
197
+
198
+ # but we can also mess with the args
199
+ # ex: skip first 2 bytes of the buffer
200
+ patch_arg(1, pbuf+2)
201
+ patch_arg(2, size-2)
202
+ end
203
+
204
+ def post_WriteFile(retval, arglistcopy)
205
+ # we can patch the API return value with this
206
+ #patch_retval(42)
207
+
208
+ # finish messing with the args: fake the nrofbyteswritten
209
+ handle, pbuf, size, pwritten, overlap = arglistcopy
210
+ written = @dbg.memory_read_int(pwritten)
211
+ if written == size
212
+ # if written everything, patch the value so that the program dont detect our intervention
213
+ @dbg.memory_write_int(pwritten, written+2)
214
+ end
215
+
216
+ puts "write retval: #{retval}, written: #{written} bytes"
217
+ end
218
+ end
219
+
220
+ # name says it all
221
+ Metasm::WinOS.get_debug_privilege
222
+
223
+ # run our Hook engine on a running 'notepad' instance
224
+ MyHook.new('notepad')
225
+
226
+ # the script ends when notepad exits
227
+
228
+ end
@@ -0,0 +1,143 @@
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
+ # a preleminary attempt to use MS dbghelp.dll to retrieve PE symbols
7
+
8
+ require 'metasm'
9
+
10
+ dll = 'C:\\Program Files\\Debugging Tools For Windows (x86)\\dbghelp.dll'
11
+
12
+ Metasm::DynLdr.new_api_c <<EOS, dll
13
+ #define SYMOPT_CASE_INSENSITIVE 0x00000001
14
+ #define SYMOPT_UNDNAME 0x00000002
15
+ #define SYMOPT_DEFERRED_LOADS 0x00000004
16
+ #define SYMOPT_NO_CPP 0x00000008
17
+ #define SYMOPT_LOAD_LINES 0x00000010
18
+ #define SYMOPT_OMAP_FIND_NEAREST 0x00000020
19
+ #define SYMOPT_LOAD_ANYTHING 0x00000040
20
+ #define SYMOPT_IGNORE_CVREC 0x00000080
21
+ #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
22
+ #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
23
+ #define SYMOPT_EXACT_SYMBOLS 0x00000400
24
+ #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
25
+ #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
26
+ #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
27
+ #define SYMOPT_PUBLICS_ONLY 0x00004000
28
+ #define SYMOPT_NO_PUBLICS 0x00008000
29
+ #define SYMOPT_AUTO_PUBLICS 0x00010000
30
+ #define SYMOPT_NO_IMAGE_SEARCH 0x00020000
31
+ #define SYMOPT_SECURE 0x00040000
32
+ #define SYMOPT_NO_PROMPTS 0x00080000
33
+ #define SYMOPT_DEBUG 0x80000000
34
+
35
+ typedef int BOOL;
36
+ typedef char CHAR;
37
+ typedef unsigned long DWORD;
38
+ typedef unsigned __int64 DWORD64;
39
+ typedef void *HANDLE;
40
+ typedef unsigned __int64 *PDWORD64;
41
+ typedef void *PVOID;
42
+ typedef unsigned long ULONG;
43
+ typedef unsigned __int64 ULONG64;
44
+ typedef const CHAR *PCSTR;
45
+ typedef CHAR *PSTR;
46
+
47
+ struct _SYMBOL_INFO {
48
+ ULONG SizeOfStruct;
49
+ ULONG TypeIndex;
50
+ ULONG64 Reserved[2];
51
+ ULONG info;
52
+ ULONG Size;
53
+ ULONG64 ModBase;
54
+ ULONG Flags;
55
+ ULONG64 Value;
56
+ ULONG64 Address;
57
+ ULONG Register;
58
+ ULONG Scope;
59
+ ULONG Tag;
60
+ ULONG NameLen;
61
+ ULONG MaxNameLen;
62
+ CHAR Name[1];
63
+ };
64
+ typedef struct _SYMBOL_INFO *PSYMBOL_INFO;
65
+
66
+ typedef __stdcall BOOL (*PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext);
67
+
68
+ __stdcall DWORD SymGetOptions(void);
69
+ __stdcall DWORD SymSetOptions(DWORD SymOptions __attribute__((in)));
70
+ __stdcall BOOL SymInitialize(HANDLE hProcess __attribute__((in)), PSTR UserSearchPath __attribute__((in)), BOOL fInvadeProcess __attribute__((in)));
71
+ __stdcall DWORD64 SymLoadModule64(HANDLE hProcess __attribute__((in)), HANDLE hFile __attribute__((in)), PSTR ImageName __attribute__((in)), PSTR ModuleName __attribute__((in)), DWORD64 BaseOfDll __attribute__((in)), DWORD SizeOfDll __attribute__((in)));
72
+ __stdcall BOOL SymSetSearchPath(HANDLE hProcess __attribute__((in)), PSTR SearchPathA __attribute__((in)));
73
+ __stdcall BOOL SymFromAddr(HANDLE hProcess __attribute__((in)), DWORD64 Address __attribute__((in)), PDWORD64 Displacement __attribute__((out)), PSYMBOL_INFO Symbol __attribute__((in)) __attribute__((out)));
74
+ __stdcall BOOL SymEnumSymbols(HANDLE hProcess __attribute__((in)), ULONG64 BaseOfDll __attribute__((in)), PCSTR Mask __attribute__((in)), PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback __attribute__((in)), PVOID UserContext __attribute__((in)));
75
+ EOS
76
+
77
+ #SYMOPT = 4|0x80000 # defered_load no_prompt
78
+ #Metasm::WinAPI.new_api dll, 'SymInitialize', 'III I'
79
+ #Metasm::WinAPI.new_api dll, 'SymGetOptions', 'I'
80
+ #Metasm::WinAPI.new_api dll, 'SymSetOptions', 'I I'
81
+ #Metasm::WinAPI.new_api dll, 'SymSetSearchPath', 'IP I'
82
+ #Metasm::WinAPI.new_api dll, 'SymLoadModule64', 'IIPIIII I' # ???ull?
83
+ #Metasm::WinAPI.new_api dll, 'SymFromAddr', 'IIIPP I' # handle ull_addr poffset psym
84
+
85
+ class Tracer < Metasm::WinDbgAPI
86
+ def initialize(*a)
87
+ super(*a)
88
+ loop
89
+ puts 'finished'
90
+ end
91
+
92
+ def handler_newprocess(pid, tid, info)
93
+ puts "newprocess: init symsrv"
94
+
95
+ h = @hprocess[pid]
96
+ dl = Metasm::DynLdr
97
+ dl.syminitialize(h, 0, 0)
98
+ dl.symsetoptions(dl.symgetoptions|dl::SYMOPT_DEFERRED_LOADS|dl::SYMOPT_NO_PROMPTS)
99
+ sympath = ENV['_NT_SYMBOL_PATH'] || 'srv**symbols*http://msdl.microsoft.com/download/symbols'
100
+ dl.symsetsearchpath(h, sympath.dup) # dup cause ENV is frozen and make WinAPI raises
101
+
102
+ Metasm::WinAPI::DBG_CONTINUE
103
+ end
104
+
105
+ def handler_loaddll(pid, tid, info)
106
+ pe = Metasm::LoadedPE.load(@mem[pid][info.imagebase, 0x1000000])
107
+ pe.decode_header
108
+ pe.decode_exports
109
+ return if not pe.export
110
+ libname = pe.export.libname
111
+ puts "loaddll: #{libname} @#{'%x' % info.imagebase}"
112
+ h = @hprocess[pid]
113
+ dl = Metasm::DynLdr
114
+ dl.symloadmodule64(h, 0, libname, 0, info.imagebase, pe.optheader.image_size)
115
+ symstruct = [0x58].pack('L') + 0.chr*4*19 + [512].pack('L') # sizeofstruct, ..., nameszmax
116
+ text = pe.sections.find { |s| s.name == '.text' }
117
+ # XXX should SymEnumSymbols, but win32api callbacks sucks
118
+ text.rawsize.times { |o|
119
+ sym = symstruct + 0.chr*512 # name concat'ed after the struct
120
+ off = 0.chr*8
121
+ if dl.symfromaddr(h, info.imagebase+text.virtaddr+o, off, sym) and off.unpack('L').first == 0
122
+ symnamelen = sym[19*4, 4].unpack('L').first
123
+ puts ' %x %s' % [text.virtaddr+o, sym[0x54, symnamelen]]
124
+ end
125
+ puts ' %x/%x' % [o, text.rawsize] if $VERBOSE and o & 0xffff == 0
126
+ }
127
+ puts
128
+ end
129
+ end
130
+
131
+ if $0 == __FILE__
132
+ Metasm::WinOS.get_debug_privilege
133
+ if ARGV.empty?
134
+ # display list of running processes if no target found
135
+ puts Metasm::WinOS.list_processes.sort_by { |pr_| pr_.pid }
136
+ abort 'target needed'
137
+ end
138
+ pid = ARGV.shift.dup
139
+ if pr = Metasm::WinOS.find_process(pid)
140
+ pid = pr.pid
141
+ end
142
+ Tracer.new pid
143
+ end
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+ # This file is part of Metasm, the Ruby assembly manipulation suite
3
+ # Copyright (C) 2006-2009 Yoann GUILLOT
4
+ #
5
+ # Licence is LGPL, see LICENCE in the top-level directory
6
+
7
+
8
+ #
9
+ # this script disassembles an executable (elf/pe) using the GTK front-end
10
+ # use live:bla to open a running process whose filename contains 'bla'
11
+ #
12
+ # key binding (non exhaustive):
13
+ # Enter to follow a label (the current hilighted word)
14
+ # Esc to return to the previous position
15
+ # Space to switch between listing and graph views
16
+ # Tab to decompile (on already disassembled code)
17
+ # 'c' to start disassembling from the cursor position
18
+ # 'g' to go to a specific address (label/042h)
19
+ # 'l' to list known labels
20
+ # 'f' to list known functions
21
+ # 'x' to list xrefs to current address
22
+ # 'n' to rename a label (current word or current address)
23
+ # ctrl+'r' to run arbitrary ruby code in the context of the Gui objet (access to 'dasm', 'curaddr')
24
+ # ctrl+mousewheel to zoom in graph view ; also doubleclick on the background ('fit to window'/'reset zoom')
25
+ #
26
+
27
+ require 'metasm'
28
+ require 'optparse'
29
+
30
+ $VERBOSE = true
31
+
32
+ # parse arguments
33
+ opts = { :sc_cpu => 'Ia32' }
34
+ OptionParser.new { |opt|
35
+ opt.banner = 'Usage: disassemble-gtk.rb [options] <executable> [<entrypoints>]'
36
+ opt.on('--no-data-trace', 'do not backtrace memory read/write accesses') { opts[:nodatatrace] = true }
37
+ opt.on('--debug-backtrace', 'enable backtrace-related debug messages (very verbose)') { opts[:debugbacktrace] = true }
38
+ opt.on('-P <plugin>', '--plugin <plugin>', 'load a metasm disassembler/debugger plugin') { |h| (opts[:plugin] ||= []) << h }
39
+ opt.on('-e <code>', '--eval <code>', 'eval a ruby code') { |h| (opts[:hookstr] ||= []) << h }
40
+ opt.on('--map <mapfile>', 'load a map file (addr <-> name association)') { |f| opts[:map] = f }
41
+ opt.on('--fast', 'dasm cli args with disassemble_fast_deep') { opts[:fast] = true }
42
+ opt.on('--decompile') { opts[:decompile] = true }
43
+ opt.on('--gui <gtk|win32|qt>') { |g| require 'metasm/gui/' + g }
44
+ opt.on('--cpu <cpu>', 'the CPU class to use for a shellcode (Ia32, X64, ...)') { |c| opts[:sc_cpu] = c }
45
+ opt.on('--exe <exe_fmt>', 'the executable file format to use (PE, ELF, ...)') { |c| opts[:exe_fmt] = c }
46
+ opt.on('--rebase <addr>', 'rebase the loaded file to <addr>') { |a| opts[:rebase] = Integer(a) }
47
+ opt.on('-c <header>', '--c-header <header>', 'read C function prototypes (for external library functions)') { |h| opts[:cheader] = h }
48
+ opt.on('-a', '--autoload', 'loads all relevant files with same filename (.h, .map..)') { opts[:autoload] = true }
49
+ opt.on('-v', '--verbose') { $VERBOSE = true } # default
50
+ opt.on('-q', '--no-verbose') { $VERBOSE = false }
51
+ opt.on('-d', '--debug') { $DEBUG = $VERBOSE = true }
52
+ }.parse!(ARGV)
53
+
54
+ case exename = ARGV.shift
55
+ when /^live:(.*)/
56
+ t = $1
57
+ t = t.to_i if $1 =~ /^[0-9]+$/
58
+ os = Metasm::OS.current
59
+ raise 'no such target' if not target = os.find_process(t) || os.create_process(t)
60
+ p target if $VERBOSE
61
+ w = Metasm::Gui::DbgWindow.new(target.debugger, "#{target.pid}:#{target.modules[0].path rescue nil} - metasm debugger")
62
+ dbg = w.dbg_widget.dbg
63
+ when /^(tcp:|udp:)?..+:/
64
+ dbg = Metasm::GdbRemoteDebugger.new(exename, opts[:sc_cpu])
65
+ w = Metasm::Gui::DbgWindow.new(dbg, "remote - metasm debugger")
66
+ else
67
+ w = Metasm::Gui::DasmWindow.new("#{exename + ' - ' if exename}metasm disassembler")
68
+ if exename
69
+ exe = w.loadfile(exename, opts[:sc_cpu], opts[:exe_fmt])
70
+ exe.disassembler.rebase(opts[:rebase]) if opts[:rebase]
71
+ if opts[:autoload]
72
+ basename = exename.sub(/\.\w\w?\w?$/, '')
73
+ opts[:map] ||= basename + '.map' if File.exist?(basename + '.map')
74
+ opts[:cheader] ||= basename + '.h' if File.exist?(basename + '.h')
75
+ (opts[:plugin] ||= []) << (basename + '.rb') if File.exist?(basename + '.rb')
76
+ end
77
+ end
78
+ end
79
+
80
+ ep = ARGV.map { |arg| (?0..?9).include?(arg[0]) ? Integer(arg) : arg }
81
+
82
+ if exe
83
+ dasm = exe.init_disassembler
84
+
85
+ dasm.load_map opts[:map] if opts[:map]
86
+ dasm.parse_c_file opts[:cheader] if opts[:cheader]
87
+ dasm.backtrace_maxblocks_data = -1 if opts[:nodatatrace]
88
+ dasm.debug_backtrace = true if opts[:debugbacktrace]
89
+ dasm.disassemble_fast_deep(*ep) if opts[:fast]
90
+ dasm.callback_finished = lambda { w.dasm_widget.focus_addr w.dasm_widget.curaddr, :decompile ; dasm.decompiler.finalize } if opts[:decompile]
91
+ elsif dbg
92
+ dbg.load_map opts[:map] if opts[:map]
93
+ opts[:plugin].to_a.each { |p| dbg.load_plugin(p) }
94
+ end
95
+ if dasm
96
+ w.display(dasm, ep)
97
+ opts[:plugin].to_a.each { |p| dasm.load_plugin(p) }
98
+ end
99
+
100
+ opts[:hookstr].to_a.each { |f| eval f }
101
+
102
+ Metasm::Gui.main