metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -69,11 +69,11 @@ class Shellcode < ExeFormat
69
69
  parse(*a) if not a.empty?
70
70
  @encoded << assemble_sequence(@source, @cpu)
71
71
  @source.clear
72
- encode
72
+ self
73
73
  end
74
74
 
75
75
  def encode(binding={})
76
- @encoded.fixup! binding
76
+ @encoded.fixup! binding if binding.kind_of? Hash
77
77
  @encoded.fixup @encoded.binding(@base_addr)
78
78
  @encoded.fill @encoded.rawsize
79
79
  self
@@ -107,7 +107,11 @@ class Shellcode < ExeFormat
107
107
  # returns a virtual subclass of Shellcode whose cpu_from_headers will return cpu
108
108
  def self.withcpu(cpu)
109
109
  c = Class.new(self)
110
- c.send(:define_method, :cpu_from_headers) { cpu }
110
+ c.send(:define_method, :cpu_from_headers) {
111
+ cpu = Metasm.const_get(cpu) if cpu.kind_of?(::String)
112
+ cpu = cpu.new if cpu.kind_of?(::Class) and cpu.ancestors.include?(CPU)
113
+ cpu
114
+ }
111
115
  c
112
116
  end
113
117
  end
@@ -0,0 +1,114 @@
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
+ require 'metasm/exe_format/main'
8
+
9
+ module Metasm
10
+ # Similar to Shellcode, with distinct sections per memory permission (R / RW / RX)
11
+ # encoding-side only
12
+ class Shellcode_RWX < ExeFormat
13
+ # the array of source elements (Instr/Data etc)
14
+ attr_accessor :source_r, :source_w, :source_x
15
+ # base address per section
16
+ attr_accessor :base_r, :base_w, :base_x
17
+ # encodeddata
18
+ attr_accessor :encoded_r, :encoded_w, :encoded_x
19
+
20
+ def initialize(cpu=nil)
21
+ @base_r = @base_w = @base_x = nil
22
+ @encoded_r = EncodedData.new
23
+ @encoded_w = EncodedData.new
24
+ @encoded_x = EncodedData.new
25
+
26
+ super(cpu)
27
+ end
28
+
29
+ def parse_init
30
+ @source_r = []
31
+ @source_w = []
32
+ @source_x = []
33
+ @cursource = @source_x
34
+ super()
35
+ end
36
+
37
+ # allows definition of the base address
38
+ def parse_parser_instruction(instr)
39
+ case instr.raw.downcase
40
+ when '.base', '.baseaddr', '.base_addr'
41
+ # ".base_addr <expression>"
42
+ # expression should #reduce to integer
43
+ @lexer.skip_space
44
+ raise instr, 'syntax error' if not base = Expression.parse(@lexer).reduce
45
+ raise instr, 'syntax error' if tok = @lexer.nexttok and tok.type != :eol
46
+ if @cursource.equal?(@source_r)
47
+ @base_r = base
48
+ elsif @cursource.equal?(@source_w)
49
+ @base_w = base
50
+ elsif @cursource.equal?(@source_x)
51
+ @base_x = base
52
+ else raise instr, "Where am I ?"
53
+ end
54
+ when '.rdata', '.rodata'
55
+ @cursource = @source_r
56
+ when '.data', '.bss'
57
+ @cursource = @source_w
58
+ when '.text'
59
+ @cursource = @source_x
60
+ else super(instr)
61
+ end
62
+ end
63
+
64
+ # encodes the source found in self.source
65
+ # appends it to self.encoded
66
+ # clears self.source
67
+ # the optional parameter may contain a binding used to fixup! self.encoded
68
+ # uses self.base_addr if it exists
69
+ def assemble(*a)
70
+ parse(*a) if not a.empty?
71
+ @encoded_r << assemble_sequence(@source_r, @cpu); @source_r.clear
72
+ @encoded_w << assemble_sequence(@source_w, @cpu); @source_w.clear
73
+ @encoded_x << assemble_sequence(@source_x, @cpu); @source_x.clear
74
+ self
75
+ end
76
+
77
+ def encode(binding={})
78
+ bd = {}
79
+ bd.update @encoded_r.binding(@base_r)
80
+ bd.update @encoded_w.binding(@base_w)
81
+ bd.update @encoded_x.binding(@base_x)
82
+ bd.update binding if binding.kind_of?(Hash)
83
+ @encoded_r.fixup bd
84
+ @encoded_w.fixup bd
85
+ @encoded_x.fixup bd
86
+ self
87
+ end
88
+ alias fixup encode
89
+
90
+ # resolve inter-section xrefs, raise if unresolved relocations remain
91
+ # call this when you have assembled+allocated memory for every section
92
+ def fixup_check(base_r=nil, base_w=nil, base_x=nil, bd={})
93
+ if base_r.kind_of?(Hash)
94
+ bd = base_r
95
+ base_r = nil
96
+ end
97
+ @base_r = base_r if base_r
98
+ @base_w = base_w if base_w
99
+ @base_x = base_x if base_x
100
+ fixup bd
101
+ ed = EncodedData.new << @encoded_r << @encoded_w << @encoded_x
102
+ raise ["Unresolved relocations:", ed.reloc.map { |o, r| "#{r.target} " + (Backtrace.backtrace_str(r.backtrace) if r.backtrace).to_s }].join("\n") if not ed.reloc.empty?
103
+ self
104
+ end
105
+
106
+ def encode_string(*a)
107
+ encode(*a)
108
+ ed = EncodedData.new << @encoded_r << @encoded_w << @encoded_x
109
+ ed.fixup(ed.binding)
110
+ raise ["Unresolved relocations:", ed.reloc.map { |o, r| "#{r.target} " + (Backtrace.backtrace_str(r.backtrace) if r.backtrace).to_s }].join("\n") if not ed.reloc.empty?
111
+ ed.data
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,205 @@
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
+ require 'metasm/exe_format/main'
7
+ require 'metasm/encode'
8
+ require 'metasm/decode'
9
+ begin
10
+ require 'zlib'
11
+ rescue LoadError
12
+ end
13
+
14
+ module Metasm
15
+ class SWF < ExeFormat
16
+ attr_accessor :signature, :version, :header, :chunks
17
+
18
+ CHUNK_TYPE = {
19
+ 0 => 'End', 1 => 'ShowFrame', 2 => 'DefineShape', 3 => 'FreeCharacter',
20
+ 4 => 'PlaceObject', 5 => 'RemoveObject', 6 => 'DefineBits', 7 => 'DefineButton',
21
+ 8 => 'JPEGTables', 9 => 'SetBackgroundColor', 10 => 'DefineFont', 11 => 'DefineText',
22
+ 12 => 'DoAction', 13 => 'DefineFontInfo', 14 => 'DefineSound', 15 => 'StartSound',
23
+ 16 => 'StopSound', 17 => 'DefineButtonSound', 18 => 'SoundStreamHead', 19 => 'SoundStreamBlock',
24
+ 20 => 'DefineBitsLossless', 21 => 'DefineBitsJPEG2', 22 => 'DefineShape2', 23 => 'DefineButtonCxform',
25
+ 24 => 'Protect', 25 => 'PathsArePostScript', 26 => 'PlaceObject2',
26
+ 28 => 'RemoveObject2', 29 => 'SyncFrame', 31 => 'FreeAll',
27
+ 32 => 'DefineShape3', 33 => 'DefineText2', 34 => 'DefineButton2', 35 => 'DefineBitsJPEG3',
28
+ 36 => 'DefineBitsLossless2', 37 => 'DefineEditText', 38 => 'DefineVideo', 39 => 'DefineSprite',
29
+ 40 => 'NameCharacter', 41 => 'ProductInfo', 42 => 'DefineTextFormat', 43 => 'FrameLabel',
30
+ 44 => 'DefineBehavior', 45 => 'SoundStreamHead2', 46 => 'DefineMorphShape', 47 => 'FrameTag',
31
+ 48 => 'DefineFont2', 49 => 'GenCommand', 50 => 'DefineCommandObj', 51 => 'CharacterSet',
32
+ 52 => 'FontRef', 53 => 'DefineFunction', 54 => 'PlaceFunction', 55 => 'GenTagObject',
33
+ 56 => 'ExportAssets', 57 => 'ImportAssets', 58 => 'EnableDebugger', 59 => 'DoInitAction',
34
+ 60 => 'DefineVideoStream', 61 => 'VideoFrame', 62 => 'DefineFontInfo2', 63 => 'DebugID',
35
+ 64 => 'EnableDebugger2', 65 => 'ScriptLimits', 66 => 'SetTabIndex', 67 => 'DefineShape4',
36
+ 68 => 'DefineMorphShape2', 69 => 'FileAttributes', 70 => 'PlaceObject3', 71 => 'ImportAssets2',
37
+ 72 => 'DoABC', 76 => 'SymbolClass', 82 => 'DoABC2',
38
+ }
39
+
40
+ class SerialStruct < Metasm::SerialStruct
41
+ new_int_field :u8, :u16, :u32, :f16, :f32
42
+ end
43
+
44
+ class Rectangle < SerialStruct
45
+ virtual :nbits, :xmin, :xmax, :ymin, :ymax
46
+
47
+ def decode(swf)
48
+ byte = swf.decode_u8
49
+ bleft = 3
50
+ @nbits = byte >> bleft
51
+ @xmin, @xmax, @ymin, @ymax = (0..3).map {
52
+ nb = @nbits
53
+ v = 0
54
+ while nb > bleft
55
+ nb -= bleft
56
+ v |= (byte & ((1<<bleft)-1)) << nb
57
+
58
+ bleft = 8
59
+ byte = swf.decode_u8
60
+ end
61
+ v |= (byte >> (bleft-nb)) & ((1<<nb)-1)
62
+ bleft -= nb
63
+
64
+ Expression.make_signed(v, @nbits)
65
+ }
66
+ end
67
+
68
+ def set_default_values(swf)
69
+ @xmin ||= 0
70
+ @xmax ||= 31
71
+ @ymin ||= 0
72
+ @ymax ||= 31
73
+ @nbits = (0..30).find { |nb|
74
+ [@xmin, @xmax, @ymin, @ymax].all? { |v|
75
+ if nb == 0
76
+ v == 0
77
+ elsif v >= 0
78
+ # reserve sign bit
79
+ (v >> (nb-1)) == 0
80
+ else
81
+ (v >> nb) == -1
82
+ end
83
+ } } || 31
84
+ end
85
+
86
+ def encode(swf)
87
+ ed = super(swf)
88
+
89
+ byte = @nbits << 3
90
+ bleft = 3
91
+ [@xmin, @xmax, @ymin, @ymax].each { |v|
92
+ nb = @nbits
93
+ while nb > bleft
94
+ byte |= (v >> (nb-bleft)) & ((1<<bleft)-1)
95
+ nb -= bleft
96
+
97
+ ed << byte
98
+ byte = 0
99
+ bleft = 8
100
+ end
101
+ byte |= (v & ((1<<nb)-1)) << (bleft-nb)
102
+ bleft -= nb
103
+ }
104
+ ed << byte if bleft < 8
105
+
106
+ ed
107
+ end
108
+ end
109
+
110
+ class Header < SerialStruct
111
+ virtual :view
112
+ u16 :framerate # XXX bigendian...
113
+ u16 :framecount
114
+
115
+ def bswap_framerate(swf)
116
+ @framerate = ((@framerate >> 8) & 0xff) | ((@framerate & 0xff) << 8) if swf.endianness == :little
117
+ end
118
+
119
+ def decode(swf)
120
+ @view = Rectangle.decode(swf)
121
+ super(swf)
122
+ bswap_framerate(swf)
123
+ end
124
+
125
+ def encode(swf)
126
+ ed = @view.encode(swf)
127
+ bswap_framerate(swf)
128
+ ed << super(swf)
129
+ bswap_framerate(swf)
130
+ ed
131
+ end
132
+ end
133
+
134
+ class Chunk < SerialStruct
135
+ bitfield :u16, 0 => :length_, 6 => :tag
136
+ fld_enum :tag, CHUNK_TYPE
137
+ attr_accessor :data
138
+
139
+ def decode(swf)
140
+ super(swf)
141
+ @length = (@length_ == 0x3f ? swf.decode_u32 : @length_)
142
+ @data = swf.encoded.read(@length)
143
+ end
144
+
145
+ def set_default_values(swf)
146
+ @length = @data.length
147
+ @length_ = [@length, 0x3f].min
148
+ end
149
+
150
+ def encode(swf)
151
+ super(swf) <<
152
+ (swf.encode_u32(@length) if @length >= 0x3f) <<
153
+ @data
154
+ end
155
+ end
156
+
157
+ def decode_u8( edata=@encoded) edata.decode_imm(:u8, @endianness) end
158
+ def decode_u16(edata=@encoded) edata.decode_imm(:u16, @endianness) end
159
+ def decode_u32(edata=@encoded) edata.decode_imm(:u32, @endianness) end
160
+ def decode_f16(edata=@encoded) edata.decode_imm(:i16, @endianness)/256.0 end
161
+ def decode_f32(edata=@encoded) edata.decode_imm(:i32, @endianness)/65536.0 end
162
+ def encode_u8(w) Expression[w].encode(:u8, @endianness) end
163
+ def encode_u16(w) Expression[w].encode(:u16, @endianness) end
164
+ def encode_u32(w) Expression[w].encode(:u32, @endianness) end
165
+ def encode_f16(w) Expression[(w*256).to_i].encode(:u16, @endianness) end
166
+ def encode_f32(w) Expression[(w*65536).to_i].encode(:u32, @endianness) end
167
+ def sizeof_u8 ; 1 ; end
168
+ def sizeof_u16 ; 2 ; end
169
+ def sizeof_u32 ; 4 ; end
170
+ def sizeof_f16 ; 2 ; end
171
+ def sizeof_f32 ; 4 ; end
172
+
173
+ attr_accessor :endianness
174
+ def initialize(cpu = nil)
175
+ @endianness = :little
176
+ @header = Header.new
177
+ @chunks = []
178
+ super(cpu)
179
+ end
180
+
181
+ def decode_header
182
+ @signature = @encoded.read(3)
183
+ @version = decode_u8
184
+ @data_length = decode_u32
185
+ case @signature
186
+ when 'FWS'
187
+ when 'CWS'
188
+ # data_length = uncompressed data length
189
+ data = @encoded.read(@encoded.length-8)
190
+ data = Zlib::Inflate.inflate(data)
191
+ @encoded = EncodedData.new(data)
192
+ else raise InvalidExeFormat, "Bad signature #{@signature.inspect}"
193
+ end
194
+ @data_length = [@data_length, @encoded.length].min
195
+ @header = Header.decode(self)
196
+ end
197
+
198
+ def decode
199
+ decode_header
200
+ while @encoded.ptr < @data_length
201
+ @chunks << Chunk.decode(self)
202
+ end
203
+ end
204
+ end
205
+ end
@@ -51,7 +51,7 @@ class XCoff < ExeFormat
51
51
  @nsec ||= xcoff.sections.size
52
52
  @symptr ||= xcoff.symbols ? xcoff.new_label('symptr') : 0
53
53
  @nsym ||= xcoff.symbols ? xcoff.symbols.length : 0
54
- @opthdr ||= xcoff.optheader ? OptHeader.size(xcoff) : 0
54
+ @opthdr ||= xcoff.optheader ? xcoff.optheader.sizeof(xcoff) : 0
55
55
  super(xcoff)
56
56
  end
57
57
  end
@@ -61,13 +61,9 @@ class XCoff < ExeFormat
61
61
  xwords :tsize, :dsize, :bsize, :entry, :text_start, :data_start, :toc
62
62
  halfs :snentry, :sntext, :sndata, :sntoc, :snloader, :snbss, :aligntext, :aligndata, :modtype, :cpu
63
63
  xwords :maxstack, :maxdata
64
- new_field(:res, lambda { |exe, me| exe.encoded.read(exe.intsize == 32 ? 8 : 120) }, lambda { |exe, me, val| val }, '')
64
+ new_field(:res, lambda { |exe, me| exe.encoded.read(exe.intsize == 32 ? 8 : 120) }, lambda { |exe, me, val| val }, lambda { |exe, me| exe.intsize == 32 ? 8 : 120 }, '')
65
65
 
66
66
 
67
- def self.size(xcoff)
68
- xcoff.intsize == 32 ? 2*2+7*4+10*2+2*4+2+8 : 2*2+7*8+10*2+2*8+2+120
69
- end
70
-
71
67
  def set_default_values(xcoff)
72
68
  @vstamp ||= 1
73
69
  @snentry ||= 1
@@ -99,12 +95,16 @@ class XCoff < ExeFormat
99
95
  # basic immediates decoding functions
100
96
  def decode_half( edata = @encoded) edata.decode_imm(:u16, @endianness) end
101
97
  def decode_word( edata = @encoded) edata.decode_imm(:u32, @endianness) end
102
- def decode_xhalf(edata = @encoded) edata.edoced_imm((@intsize == 32 ? :u16 : :u32), @endianness) end
98
+ def decode_xhalf(edata = @encoded) edata.decode_imm((@intsize == 32 ? :u16 : :u32), @endianness) end
103
99
  def decode_xword(edata = @encoded) edata.decode_imm((@intsize == 32 ? :u32 : :u64), @endianness) end
104
100
  def encode_half(w) Expression[w].encode(:u16, @endianness) end
105
101
  def encode_word(w) Expression[w].encode(:u32, @endianness) end
106
102
  def encode_xhalf(w) Expression[w].encode((@intsize == 32 ? :u16 : :u32), @endianness) end
107
103
  def encode_xword(w) Expression[w].encode((@intsize == 32 ? :u32 : :u64), @endianness) end
104
+ def sizeof_half ; 2 ; end
105
+ def sizeof_word ; 4 ; end
106
+ def sizeof_xhalf ; @intsize == 32 ? 2 : 4 ; end
107
+ def sizeof_xword ; @intsize == 32 ? 4 : 8 ; end
108
108
 
109
109
 
110
110
  attr_accessor :header, :segments, :relocs, :intsize, :endianness
@@ -0,0 +1,335 @@
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
+ require 'metasm/exe_format/main'
7
+ require 'metasm/encode'
8
+ require 'metasm/decode'
9
+ begin
10
+ require 'zlib'
11
+ rescue LoadError
12
+ end
13
+
14
+ # generic ZIP file, may be an APK or JAR
15
+ # supports only a trivial subset of the whole ZIP specification
16
+ # single file archive
17
+ # deflate or no compression
18
+ # no encryption
19
+ # 32bit offsets/sizes
20
+
21
+ module Metasm
22
+ class ZIP < ExeFormat
23
+ MAGIC_LOCALHEADER = 0x04034b50
24
+ COMPRESSION_METHOD = { 0 => 'NONE', 1 => 'SHRUNK', 2 => 'REDUCE1', 3 => 'REDUCE2',
25
+ 4 => 'REDUCE3', 5 => 'REDUCE4', 6 => 'IMPLODE', 7 => 'TOKENIZED',
26
+ 8 => 'DEFLATE', 9 => 'DEFLATE64', 10 => 'OLDTERSE', 12 => 'BZIP2', 14 => 'LZMA',
27
+ 18 => 'TERSE', 19 => 'LZ77', 97 => 'WAVPACK', 98 => 'PPMD' }
28
+
29
+ # zip file format:
30
+ #
31
+ # [local header 1]
32
+ # compressed data 1
33
+ #
34
+ # [local header 2]
35
+ # compressed data 2
36
+ #
37
+ # [central header 1]
38
+ # [central header 2]
39
+ #
40
+ # [end of central directory]
41
+
42
+ class LocalHeader < SerialStruct
43
+ word :signature, MAGIC_LOCALHEADER
44
+ half :verneed, 10
45
+ half :flags # bit 3 => has data descriptor following the compressed data
46
+ half :compress_method, 0, COMPRESSION_METHOD
47
+ halfs :mtime, :mdate
48
+ word :crc32
49
+ words :compressed_sz, :uncompressed_sz
50
+ halfs :fname_len, :extra_len
51
+ attr_accessor :fname, :extra
52
+ attr_accessor :compressed_off
53
+
54
+ def decode(zip)
55
+ super(zip)
56
+ raise "Invalid ZIP signature #{@signature.to_s(16)}" if @signature != MAGIC_LOCALHEADER
57
+ @fname = zip.encoded.read(@fname_len) if @fname_len > 0
58
+ @extra = zip.encoded.read(@extra_len) if @extra_len > 0
59
+ @compressed_off = zip.encoded.ptr
60
+ end
61
+
62
+ def set_default_values(zip)
63
+ @fname_len = fname ? @fname.length : 0
64
+ @extra_len = extra ? @extra.length : 0
65
+ super(zip)
66
+ end
67
+
68
+ def encode(zip)
69
+ ed = super(zip)
70
+ ed << fname << extra
71
+ end
72
+
73
+ # return a new LocalHeader with all fields copied from a CentralHeader
74
+ def self.from_central(f)
75
+ l = new
76
+ l.verneed = f.verneed
77
+ l.flags = f.flags
78
+ l.compress_method = f.compress_method
79
+ l.mtime = f.mtime
80
+ l.mdate = f.mdate
81
+ l.crc32 = f.crc32
82
+ l.compressed_sz = f.compressed_sz
83
+ l.uncompressed_sz = f.uncompressed_sz
84
+ l.fname = f.fname
85
+ l.extra = f.extra
86
+ l
87
+ end
88
+ end
89
+
90
+ MAGIC_CENTRALHEADER = 0x02014b50
91
+ class CentralHeader < SerialStruct
92
+ word :signature, MAGIC_CENTRALHEADER
93
+ half :vermade, 10
94
+ half :verneed, 10
95
+ half :flags
96
+ half :compress_method, 0, COMPRESSION_METHOD
97
+ halfs :mtime, :mdate
98
+ word :crc32
99
+ words :compressed_sz, :uncompressed_sz
100
+ halfs :fname_len, :extra_len, :comment_len
101
+ half :disk_nr
102
+ half :file_attr_intern
103
+ word :file_attr_extern
104
+ word :localhdr_off
105
+ attr_accessor :fname, :extra, :comment
106
+ attr_accessor :data
107
+
108
+ def decode(zip)
109
+ super(zip)
110
+ raise "Invalid ZIP signature #{@signature.to_s(16)}" if @signature != MAGIC_CENTRALHEADER
111
+ @fname = zip.encoded.read(@fname_len) if @fname_len > 0
112
+ @extra = zip.encoded.read(@extra_len) if @extra_len > 0
113
+ @comment = zip.encoded.read(@comment_len) if @comment_len > 0
114
+ end
115
+
116
+ def set_default_values(zip)
117
+ @fname_len = fname ? @fname.length : 0
118
+ @extra_len = extra ? @extra.length : 0
119
+ @comment_len = comment ? @comment.length : 0
120
+ super(zip)
121
+ end
122
+
123
+ def encode(zip)
124
+ ed = super(zip)
125
+ ed << fname << extra << comment
126
+ end
127
+
128
+ # reads the raw file data from the archive
129
+ def file_data(zip)
130
+ return @data if data
131
+
132
+ zip.encoded.ptr = @localhdr_off
133
+ LocalHeader.decode(zip)
134
+ raw = zip.encoded.read(@compressed_sz)
135
+ @data = case @compress_method
136
+ when 'NONE'
137
+ raw
138
+ when 'DEFLATE'
139
+ z = Zlib::Inflate.new(-Zlib::MAX_WBITS)
140
+ z.inflate(raw)
141
+ else
142
+ raise "Unsupported zip compress method #@compress_method"
143
+ end
144
+ end
145
+
146
+ def zlib_deflate(data, level=Zlib::DEFAULT_COMPRESSION)
147
+ z = Zlib::Deflate.new(level, -Zlib::MAX_WBITS)
148
+ z.deflate(data) + z.finish
149
+ end
150
+
151
+ # encode the data, fixup related fields
152
+ def encode_data(zip)
153
+ data = file_data(zip)
154
+ @compress_method = 'NONE' if data == ''
155
+
156
+ @crc32 = Zlib.crc32(data)
157
+ @uncompressed_sz = data.length
158
+
159
+ case compress_method
160
+ when 'NONE'
161
+ when 'DEFLATE'
162
+ data = zlib_deflate(data)
163
+ when nil
164
+ # autodetect compression method
165
+ # compress if we win more than 10% space
166
+ cdata = zlib_deflate(data)
167
+ ratio = cdata.length * 100 / data.length
168
+ if ratio < 90
169
+ @compress_method = 'DEFLATE'
170
+ data = cdata
171
+ else
172
+ @compress_method = 'NONE'
173
+ end
174
+ end
175
+
176
+ @compressed_sz = data.length
177
+
178
+ data
179
+ end
180
+ end
181
+
182
+ MAGIC_ENDCENTRALDIRECTORY = 0x06054b50
183
+ class EndCentralDirectory < SerialStruct
184
+ word :signature, MAGIC_ENDCENTRALDIRECTORY
185
+ halfs :disk_nr, :disk_centraldir, :entries_nr_thisdisk, :entries_nr
186
+ word :directory_sz
187
+ word :directory_off
188
+ half :comment_len
189
+ attr_accessor :comment
190
+
191
+ def decode(zip)
192
+ super(zip)
193
+ raise "Invalid ZIP end signature #{@signature.to_s(16)}" if @signature != MAGIC_ENDCENTRALDIRECTORY
194
+ @comment = zip.encoded.read(@comment_len) if @comment_len > 0
195
+ end
196
+
197
+ def set_default_values(zip)
198
+ @entries_nr_thisdisk = zip.files.length
199
+ @entries_nr = zip.files.length
200
+ @comment_len = comment ? @comment.length : 0
201
+ super(zip)
202
+ end
203
+
204
+ def encode(zip)
205
+ ed = super(zip)
206
+ ed << comment
207
+ end
208
+ end
209
+
210
+ def decode_half(edata=@encoded) edata.decode_imm(:u16, @endianness) end
211
+ def decode_word(edata=@encoded) edata.decode_imm(:u32, @endianness) end
212
+ def encode_half(w) Expression[w].encode(:u16, @endianness) end
213
+ def encode_word(w) Expression[w].encode(:u32, @endianness) end
214
+ def sizeof_half ; 2 ; end
215
+ def sizeof_word ; 4 ; end
216
+
217
+ attr_accessor :files, :header
218
+
219
+ def initialize(cpu = nil)
220
+ @endianness = :little
221
+ @header = EndCentralDirectory.new
222
+ @files = []
223
+ super(cpu)
224
+ end
225
+
226
+ # scan and decode the 'end of central directory' header
227
+ def decode_header
228
+ if not @encoded.ptr = @encoded.data.rindex([MAGIC_ENDCENTRALDIRECTORY].pack('V'))
229
+ raise "ZIP: no end of central directory record"
230
+ end
231
+ @header = EndCentralDirectory.decode(self)
232
+ end
233
+
234
+ # read the whole central directory file descriptors
235
+ def decode
236
+ decode_header
237
+ @encoded.ptr = @header.directory_off
238
+ while @encoded.ptr < @header.directory_off + @header.directory_sz
239
+ @files << CentralHeader.decode(self)
240
+ end
241
+ end
242
+
243
+ # checks if a given file name exists in the archive
244
+ # returns the CentralHeader or nil
245
+ # case-insensitive if lcase is false
246
+ def has_file(fname, lcase=true)
247
+ decode if @files.empty?
248
+ if lcase
249
+ @files.find { |f| f.fname == fname }
250
+ else
251
+ fname = fname.downcase
252
+ @files.find { |f| f.fname.downcase == fname }
253
+ end
254
+ end
255
+
256
+ # returns the uncompressed raw file content from a given name
257
+ # nil if name not found
258
+ # case-insensitive if lcase is false
259
+ def file_data(fname, lcase=true)
260
+ if f = has_file(fname, lcase)
261
+ f.file_data(self)
262
+ end
263
+ end
264
+
265
+ # add a new file to the zip archive
266
+ def add_file(fname, data, compress=:auto)
267
+ f = CentralHeader.new
268
+
269
+ case compress
270
+ when 'NONE', false; f.compress_method = 'NONE'
271
+ when 'DEFLATE', true; f.compress_method = 'DEFLATE'
272
+ end
273
+
274
+ f.fname = fname
275
+ f.data = data
276
+
277
+ @files << f
278
+ f
279
+ end
280
+
281
+ # create a new zip file
282
+ def encode
283
+ edata = EncodedData.new
284
+ central_dir = EncodedData.new
285
+
286
+ @files.each { |f|
287
+ encode_entry(f, edata, central_dir)
288
+ }
289
+
290
+ @header.directory_off = edata.length
291
+ @header.directory_sz = central_dir.length
292
+
293
+ edata << central_dir << @header.encode(self)
294
+
295
+ @encoded = edata
296
+ end
297
+
298
+ # add one file to the zip stream
299
+ def encode_entry(f, edata, central_dir)
300
+ f.localhdr_off = edata.length
301
+
302
+ # may autodetect compression method
303
+ raw = f.encode_data(self)
304
+
305
+ zipalign(f, edata)
306
+
307
+ central_dir << f.encode(self) # calls f.set_default_values
308
+
309
+ l = LocalHeader.from_central(f)
310
+ edata << l.encode(self)
311
+
312
+ edata << raw
313
+ end
314
+
315
+ # zipalign: ensure uncompressed data starts on a 4-aligned offset
316
+ def zipalign(f, edata)
317
+ if f.compress_method == 'NONE' and not f.extra
318
+ o = (edata.length + f.fname.length + 2) & 3
319
+ f.extra = " "*(4-o) if o > 0
320
+ end
321
+
322
+ end
323
+
324
+ # when called as AutoExe, try to find a meaningful exefmt
325
+ def self.autoexe_load(bin)
326
+ z = decode(bin)
327
+ if dex = z.file_data('classes.dex')
328
+ puts "ZIP: APK file, loading 'classes.dex'" if $VERBOSE
329
+ AutoExe.load(dex)
330
+ else
331
+ z
332
+ end
333
+ end
334
+ end
335
+ end