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.
- 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
|