metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- 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
|
data/samples/dbghelp.rb
ADDED
@@ -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
|