metasm 1.0.1 → 1.0.2

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