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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- 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
|
-
|
|
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) {
|
|
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 ?
|
|
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.
|
|
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
|