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,46 @@
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 reads a list of elf files, and lists its dependencies recursively
10
+ # libraries are searched in LD_LIBRARY_PATH, /usr/lib and /lib
11
+ # includes the elf interpreter
12
+ # can be useful when chrooting a binary
13
+ #
14
+
15
+ require 'metasm'
16
+
17
+
18
+ paths = ENV['LD_LIBRARY_PATH'].to_s.split(':') + %w[/usr/lib /lib]
19
+ todo = ARGV.map { |file| (file[0] == ?/) ? file : "./#{file}" }
20
+ done = []
21
+ while src = todo.shift
22
+ puts src
23
+ # could do a simple ELF.decode_file, but this is quicker
24
+ elf = Metasm::ELF.decode_file_header(src)
25
+
26
+ if s = elf.segments.find { |s_| s_.type == 'INTERP' }
27
+ interp = elf.encoded[s.offset, s.filesz].data.chomp("\0")
28
+ if not done.include? interp
29
+ puts interp
30
+ done << interp
31
+ end
32
+ end
33
+
34
+ elf.decode_tags
35
+ elf.decode_segments_tags_interpret
36
+ deps = elf.tag['NEEDED'].to_a - done
37
+ done.concat deps
38
+
39
+ deps.each { |dep|
40
+ if not path = paths.find { |path_| File.exist? File.join(path_, dep) }
41
+ $stderr.puts "cannot find #{dep} for #{src}"
42
+ else
43
+ todo << File.join(path, dep)
44
+ end
45
+ }
46
+ end
@@ -0,0 +1,33 @@
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
+ # this script takes a list of dll filenames as arguments, and outputs each lib export
8
+ # libname, followed by the list of the exported symbol names, in a format usable
9
+ # by the Elf class autoimport functionnality (see metasm/os/linux.rb)
10
+ #
11
+
12
+ require 'metasm'
13
+
14
+ bd = 'GLOBAL'
15
+ bd = 'WEAK' if ARGV.delete '--weak'
16
+ obj = true if ARGV.delete '--obj'
17
+
18
+ ARGV.each { |f|
19
+ e = Metasm::ELF.decode_file(f)
20
+ next if not e.tag['SONAME']
21
+ puts e.tag['SONAME']
22
+ line = ''
23
+ e.symbols.find_all { |s|
24
+ s.name and (obj ? s.type != 'FUNC' : s.type == 'FUNC') and s.shndx != 'UNDEF' and s.bind == bd
25
+ }.map { |s| ' ' << s.name }.sort.each { |s|
26
+ if line.length + s.length >= 160
27
+ puts line
28
+ line = ''
29
+ end
30
+ line << s
31
+ }
32
+ puts line if not line.empty?
33
+ }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is part of Metasm, the Ruby assembly manipulation suite
4
+ # Copyright (C) 2006-2009 Yoann GUILLOT
5
+ #
6
+ # Licence is LGPL, see LICENCE in the top-level directory
7
+
8
+ require 'metasm'
9
+ $opts = { :execlass => Metasm::ELF, :exetype => :lib }
10
+ load File.join(File.dirname(__FILE__), 'exeencode.rb')
11
+
12
+ __END__
13
+ .pt_gnu_stack rw
14
+ // .nointerp // to disable the dynamic section, eg for stuff with int80 only
15
+ .text
16
+ .entrypoint
17
+ push bla
18
+ push fmt
19
+ call printf
20
+ push 0
21
+ call exit
22
+
23
+ .data
24
+ bla db "world", 0
25
+ fmt db "Hello, %s !\n", 0
@@ -0,0 +1,128 @@
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
+ # this sample shows how to compile an executable file from source
9
+ # use --exe PE to compile a PE/ELF/MachO etc
10
+ # use --cpu MIPS/--16/--be to change the CPU
11
+ # the arg is a source file (c or asm) (some arch may not yet support C compiling)
12
+ # defaults to encoding a shellcode, use --exe to override (or the scripts samples/{elf,pe}encode)
13
+ # to compile a shellcode to a cstring, use --cstring
14
+ #
15
+
16
+ require 'metasm'
17
+ require 'optparse'
18
+
19
+ $opts ||= {}
20
+ $opts = {
21
+ :execlass => Metasm::Shellcode,
22
+ :cpu => Metasm::Ia32.new,
23
+ :exetype => :bin,
24
+ :macros => {}
25
+ }.merge($opts)
26
+
27
+ OptionParser.new { |opt|
28
+ opt.on('-o file', 'output filename') { |f| $opts[:outfilename] = f }
29
+ opt.on('-f') { $opts[:overwrite_outfile] = true }
30
+ opt.on('--c', 'parse source as a C file') { $opts[:srctype] = 'c' }
31
+ opt.on('--asm', 'parse asm as an ASM file') { $opts[:srctype] = 'asm' }
32
+ opt.on('--stdin', 'parse source on stdin') { ARGV << '-' }
33
+ opt.on('-v', '-W', 'verbose') { $VERBOSE=true }
34
+ opt.on('-d', 'debug') { $DEBUG=$VERBOSE=true }
35
+ opt.on('-D var=val', 'define a preprocessor macro') { |v| v0, v1 = v.split('=', 2) ; $opts[:macros][v0] = v1 }
36
+ opt.on('--cstring', 'encode output as a C string') { $opts[:to_string] = :c }
37
+ opt.on('--jsstring', 'encode output as a js string') { $opts[:to_string] = :js }
38
+ opt.on('--string', 'encode output as a string to stdout') { $opts[:to_string] = :inspect }
39
+ opt.on('--varname name', 'the variable name for string output') { |v| $opts[:varname] = v }
40
+ opt.on('-e class', '--exe class', 'use a specific ExeFormat class') { |c| $opts[:execlass] = Metasm.const_get(c) }
41
+ opt.on('--cpu cpu', 'use a specific CPU class') { |c| $opts[:cpu] = Metasm.const_get(c).new }
42
+ # must come after --cpu in commandline
43
+ opt.on('--16', 'set cpu in 16bit mode') { $opts[:cpu].size = 16 }
44
+ opt.on('--le', 'set cpu in little-endian mode') { $opts[:cpu].endianness = :little }
45
+ opt.on('--be', 'set cpu in big-endian mode') { $opts[:cpu].endianness = :big }
46
+ opt.on('--fno-pic', 'generate position-dependant code') { $opts[:cpu].generate_PIC = false }
47
+ opt.on('--shared', 'generate shared library') { $opts[:exetype] = :lib }
48
+ opt.on('--ruby-module-hack', 'use the dynldr module hack to use any ruby lib available for ruby symbols') { $opts[:dldrhack] = true }
49
+ }.parse!
50
+
51
+ src = $opts[:macros].map { |k, v| "#define #{k} #{v}\n" }.join
52
+
53
+ if file = ARGV.shift
54
+ $opts[:srctype] ||= 'c' if file =~ /\.c$/
55
+ if file == '-'
56
+ src << $stdin.read
57
+ else
58
+ src << File.read(file)
59
+ end
60
+ else
61
+ $opts[:srctype] ||= $opts[:srctype_data]
62
+ src << DATA.read # the text after __END__ in this file
63
+ end
64
+
65
+ if $opts[:outfilename] and not $opts[:overwrite_outfile] and File.exist?($opts[:outfilename])
66
+ abort "Error: target file exists !"
67
+ end
68
+
69
+ if $opts[:srctype] == 'c'
70
+ exe = $opts[:execlass].compile_c($opts[:cpu], src, file)
71
+ else
72
+ exe = $opts[:execlass].assemble($opts[:cpu], src, file)
73
+ end
74
+
75
+ if $opts[:to_string]
76
+ str = exe.encode_string
77
+
78
+ $opts[:varname] ||= File.basename(file.to_s)[/^\w+/] || 'sc' # derive varname from filename
79
+ case $opts[:to_string]
80
+ when :inspect
81
+ str = "#{$opts[:varname]} = #{str.inspect}"
82
+ when :c
83
+ str = ["unsigned char #{$opts[:varname]}[#{str.length}] = "] + str.scan(/.{1,19}/m).map { |l|
84
+ '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"'
85
+ }
86
+ str.last << ?;
87
+ when :js
88
+ str << 0 if str.length & 1 != 0
89
+ str = ["#{$opts[:varname]} = "] + str.scan(/.{2,20}/m).map { |l|
90
+ '"' + l.unpack($opts[:cpu].endianness == :little ? 'v*' : 'n*').map { |c| '%%u%04x' % c }.join + '"+'
91
+ }
92
+ str.last[-1] = ?;
93
+ end
94
+
95
+ if of = $opts[:outfilename]
96
+ abort "Error: target file #{of.inspect} exists !" if File.exists? of and not $opts[:overwrite_outfile]
97
+ File.open(of, 'w') { |fd| fd.puts str }
98
+ puts "saved to file #{of.inspect}"
99
+ else
100
+ puts str
101
+ end
102
+ else
103
+ of = $opts[:outfilename] ||= 'a.out'
104
+ abort "Error: target file #{of.inspect} exists !" if File.exists? of and not $opts[:overwrite_outfile]
105
+ Metasm::DynLdr.compile_binary_module_hack(exe) if $opts[:dldrhack]
106
+ exe.encode_file(of, $opts[:exetype])
107
+ puts "saved to file #{of.inspect}"
108
+ end
109
+
110
+ __END__
111
+ #include <asm/unistd.h>
112
+ jmp getip
113
+ gotip:
114
+ mov eax, __NR_write
115
+ mov ebx, 1
116
+ pop ecx
117
+ mov edx, strend-str
118
+ int 80h
119
+
120
+ mov eax, __NR_exit
121
+ mov ebx, 1
122
+ int 80h
123
+
124
+ getip:
125
+ call gotip
126
+
127
+ str db "Hello, world!", 0xa
128
+ strend:
@@ -0,0 +1,77 @@
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
+ # this exemple illustrates the use of the cparser/preprocessor #factorize functionnality:
8
+ # it generates code that references to the functions imported by an ELF executable
9
+ # usage: factorize-imports.rb <exe> [<path to include dir>] [<additional func names>... !<func to exclude>]
10
+ #
11
+
12
+ require 'metasm'
13
+ include Metasm
14
+
15
+ require 'optparse'
16
+ opts = { :hdrs => [], :defs => {}, :path => [] }
17
+ OptionParser.new { |opt|
18
+ opt.on('-o outfile') { |f| opts[:outfile] = f }
19
+ opt.on('-H additional_header') { |f| opts[:hdrs] << f }
20
+ opt.on('--exe executable') { |f| opts[:exe] = f }
21
+ opt.on('-I path', '--includepath path') { |f| opts[:path] << f }
22
+ opt.on('-D var') { |f| k, v = f.split('=', 2) ; opts[:defs].update k => (v || '') }
23
+ opt.on('--gcc') { opts[:gcc] = true }
24
+ opt.on('--vs', '--visualstudio') { opts[:vs] = true }
25
+ }.parse!(ARGV)
26
+
27
+ exe = AutoExe.decode_file_header(opts[:exe] || ARGV.shift)
28
+ opts[:path] ||= [ARGV.shift] if not ARGV.empty?
29
+
30
+ case exe
31
+ when PE
32
+ exe.decode_imports
33
+ funcnames = exe.imports.map { |id| id.imports.map { |i| i.name } }
34
+ when ELF
35
+ exe.decode_segments_dynamic
36
+ funcnames = exe.symbols.map { |s| s.name if s.shndx == 'UNDEF' and s.type == 'FUNC' }
37
+ opts[:hdrs] << 'stdio.h' << 'stdlib.h' << 'unistd.h'
38
+ opts[:gcc] = true if not opts[:vs]
39
+ else raise "unsupported #{exe.class}"
40
+ end
41
+
42
+ funcnames = funcnames.flatten.compact.uniq.sort
43
+
44
+ ARGV.each { |n|
45
+ if n[0] == ?!
46
+ funcnames.delete n[1..-1]
47
+ else
48
+ funcnames |= [n]
49
+ end
50
+ }
51
+
52
+ src = opts[:hdrs].map { |h| "#include <#{h}>" }.join("\n")
53
+
54
+ parser = Ia32.new.new_cparser
55
+ parser.prepare_gcc if opts[:gcc]
56
+ parser.prepare_visualstudio if opts[:vs]
57
+ pp = parser.lexer
58
+ pp.warn_redefinition = false
59
+ pp.include_search_path[0, 0] = opts[:path]
60
+ opts[:defs].each { |k, v| pp.define k, v }
61
+ parser.factorize_init
62
+ parser.parse src
63
+
64
+
65
+ # delete imports not present in the header files
66
+ funcnames.delete_if { |f|
67
+ if not parser.toplevel.symbol[f]
68
+ puts "// #{f.inspect} is not defined in the headers"
69
+ true
70
+ end
71
+ }
72
+
73
+ parser.parse "void *fnptr[] = { #{funcnames.map { |f| '&'+f }.join(', ')} };"
74
+
75
+ outfd = (opts[:outfile] ? File.open(opts[:outfile], 'w') : $stdout)
76
+ outfd.puts parser.factorize_final
77
+ outfd.close
@@ -0,0 +1,109 @@
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
+ # this exemple illustrates the use of the cparser/preprocessor #factorize functionnality:
8
+ # it generates code that references to the functions imported by a windows executable
9
+ # usage: factorize-imports.rb <exe> <exe2> <path to visual studio installation> [<additional func names>... ^<func to exclude>]
10
+ #
11
+
12
+ require 'metasm'
13
+ include Metasm
14
+
15
+ require 'optparse'
16
+ opts = { :hdrs => [], :defs => {}, :path => [], :exe => [] }
17
+ OptionParser.new { |opt|
18
+ opt.on('-o outfile') { |f| opts[:outfile] = f }
19
+ opt.on('-H additional_header') { |f| opts[:hdrs] << f }
20
+ opt.on('-e exe', '--exe executable') { |f| opts[:exe] << f }
21
+ opt.on('-I path', '--includepath path') { |f| opts[:path] << f }
22
+ opt.on('-D var') { |f| k, v = f.split('=', 2) ; opts[:defs].update k => (v || '') }
23
+ opt.on('--ddk') { opts[:ddk] = true }
24
+ opt.on('--vspath path') { |f| opts[:vspath] = f }
25
+ }.parse!(ARGV)
26
+
27
+ ARGV.delete_if { |e|
28
+ next if not File.file? e
29
+ opts[:exe] << e
30
+ }
31
+
32
+ if opts[:vspath] ||= ARGV.shift
33
+ opts[:vspath] = opts[:vspath].tr('\\', '/')
34
+ opts[:vspath] = opts[:vspath].chop if opts[:vspath][-1] == ?/
35
+ if opts[:ddk]
36
+ opts[:path] << (opts[:vspath]+'/ddk') << (opts[:vspath]+'/api') << (opts[:vspath]+'/crt')
37
+ else
38
+ opts[:vspath] = opts[:vspath][0...-3] if opts[:vspath][-3..-1] == '/VC'
39
+ opts[:path] << (opts[:vspath]+'/VC/platformsdk/include') << (opts[:vspath]+'/VC/include')
40
+ end
41
+ end
42
+
43
+ funcnames = opts[:exe].map { |e|
44
+ pe = PE.decode_file_header(e)
45
+ pe.decode_imports
46
+ if not pe.imports
47
+ puts "#{e} has no imports"
48
+ next
49
+ end
50
+ pe.imports.map { |id| id.imports.map { |i| i.name } }
51
+ }.flatten.compact.uniq.sort
52
+
53
+ ARGV.each { |n|
54
+ if n[0] == ?! or n[0] == ?- or n[0] == ?^
55
+ funcnames.delete n[1..-1]
56
+ else
57
+ funcnames |= [n]
58
+ end
59
+ }
60
+ exit if funcnames.empty?
61
+
62
+ src = <<EOS + opts[:hdrs].to_a.map { |h| "#include <#{h}>\n" }.join
63
+ #ifdef DDK
64
+ #define NO_INTERLOCKED_INTRINSICS
65
+ typedef struct _CONTEXT CONTEXT; // needed by ntddk.h, but this will pollute the factorized output..
66
+ typedef CONTEXT *PCONTEXT;
67
+ #define dllimport stdcall // wtff
68
+ #define SORTPP_PASS // C_ASSERT proprocessor assert..
69
+ #define _MSC_EXTENSIONS // __volatile stuff
70
+ #include <ntddk.h>
71
+ #include <stdio.h>
72
+ #else
73
+ #define WIN32_LEAN_AND_MEAN
74
+ #include <windows.h>
75
+ #include <winternl.h>
76
+ #endif
77
+ EOS
78
+
79
+ parser = Ia32.new.new_cparser
80
+ parser.prepare_visualstudio
81
+ pp = parser.lexer
82
+ pp.warn_redefinition = false
83
+ pp.define('_WIN32_WINNT', '0x0600')
84
+ pp.define('DDK') if opts[:ddk]
85
+ pp.define_strong('IN', '__attribute__((in))')
86
+ pp.define_strong('__in', '__attribute__((in))')
87
+ pp.define_strong('OUT', '__attribute__((out))')
88
+ pp.define_strong('__out', '__attribute__((out))')
89
+ pp.include_search_path = opts[:path]
90
+ opts[:defs].each { |k, v| pp.define k, v }
91
+ parser.factorize_init
92
+ parser.parse src
93
+
94
+
95
+ outfd = (opts[:outfile] ? File.open(opts[:outfile], 'w') : $stdout)
96
+
97
+ # delete imports not present in the header files
98
+ funcnames.delete_if { |f|
99
+ if not parser.toplevel.symbol[f]
100
+ puts "// #{f.inspect} is not defined in the headers"
101
+ outfd.puts "// #{f.inspect} is not defined in the headers" if opts[:outfile]
102
+ true
103
+ end
104
+ }
105
+
106
+ parser.parse "void *fnptr[] = { #{funcnames.map { |f| '&'+f }.join(', ')} };"
107
+
108
+ outfd.puts parser.factorize_final
109
+ outfd.close
@@ -0,0 +1,43 @@
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
+ # this exemple illustrates the use of the cparser/preprocessor #factorize functionnality:
8
+ # we write some code using standard headers, and the factorize call on CParser
9
+ # gives us back the macro/C definitions that we use in our code, so that we can
10
+ # get rid of the header
11
+ # Argument: C file to factorize, [path to visual studio installation]
12
+ # with a single argument, uses GCC standard headers
13
+ #
14
+
15
+ require 'metasm'
16
+ include Metasm
17
+
18
+ abort 'target needed' if not file = ARGV.shift
19
+
20
+ visualstudiopath = ARGV.shift
21
+ if visualstudiopath
22
+ stub = <<EOS
23
+ // add the path to the visual studio std headers
24
+ #ifdef __METASM__
25
+ #pragma include_dir #{(visualstudiopath+'/platformsdk/include').inspect}
26
+ #pragma include_dir #{(visualstudiopath+'/include').inspect}
27
+ #pragma prepare_visualstudio
28
+ #pragma no_warn_redefinition
29
+ #endif
30
+ EOS
31
+ else
32
+ stub = <<EOS
33
+ #ifdef __METASM__
34
+ #pragma prepare_gcc
35
+ #endif
36
+ EOS
37
+ end
38
+ stub << "#line 0\n"
39
+
40
+ # to trace only pp macros (using eg an asm source), use Preprocessor#factorize instead
41
+
42
+ puts Ia32.new.new_cparser.factorize(stub + File.read(file), file)
43
+