resedit 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,166 @@
1
+ require 'resedit/mz/mz_header'
2
+ require 'resedit/mz/mz_body'
3
+ require 'resedit/mz/hexwriter'
4
+ require 'resedit/mz/mzenv'
5
+
6
+ module Resedit
7
+
8
+ class MZ
9
+ ZM = 0x4D5A
10
+
11
+ attr_reader :fname, :path, :name, :fsize
12
+ attr_reader :header, :body
13
+
14
+ def initialize(path, quiet = false)
15
+ raise "File not specified" if !path
16
+ @quiet = quiet
17
+ @path = path.downcase()
18
+ @fsize = File.size(path)
19
+ open(@path,"rb:ascii-8bit"){|f|
20
+ @header = MZHeader.new(self, f, fsize)
21
+ hsz = @header.headerSize()
22
+ @body = MZBody.new(self, f, @header.fileSize() - hsz)
23
+ save = f.read(2)
24
+ zm = save ? save.unpack('v')[0] : nil
25
+ if zm == ZM
26
+ @header.loadChanges(f)
27
+ @body.loadChanges(f)
28
+ log("Change info loaded.")
29
+ end
30
+ }
31
+ @fname = File.basename(@path)
32
+ @name = File.basename(@path, ".*")
33
+ hi = @header.info()
34
+ env().set(:entry, hi[:CS].to_s+":"+hi[:IP].to_s)
35
+ env().set(:append, sprintf("%04X:0",@body.appSeg))
36
+ end
37
+
38
+
39
+ def close()
40
+ end
41
+
42
+
43
+ def log(fmt, *args)
44
+ App::get().log(fmt, *args) if !@quiet
45
+ end
46
+
47
+
48
+ def env() return MZEnv.instance() end
49
+ def s2i(str) return MZEnv.instance().s2i(str) end
50
+
51
+
52
+ def is?(id)
53
+ id = id.downcase
54
+ return id == @path || id == @fname || id == @name
55
+ end
56
+
57
+
58
+ def print(what, how=nil)
59
+ puts "Header changes:" if what=="changes"
60
+ res = @header.print(what, how)
61
+ puts "Code changes:" if what=="changes"
62
+ res |= @body.print(what, how)
63
+ raise "Don't know how to print: " + what if !res
64
+ end
65
+
66
+
67
+ def hex(ofs, size=nil, how=nil, disp=nil)
68
+ ofs = ofs ? s2i(ofs) : 0
69
+ size = size ? s2i(size) : 0x100
70
+ isfile = disp && (disp[0]=='f' || disp[0]=='F') ? true : false
71
+ wr = HexWriter.new(ofs)
72
+ how = @header.parseHow(how)
73
+ hsz = 0
74
+ if isfile
75
+ @header.mode(how)
76
+ hsz = @header.headerSize()
77
+ size = @header.hex(wr, ofs, size, how) if ofs < hsz
78
+ ofs -= hsz
79
+ ofs = 0 if ofs < 0
80
+ end
81
+ wr.setSegments(@body.segments, hsz)
82
+ @body.hex(wr, ofs, size, how) if size > 0
83
+ wr.finish()
84
+ end
85
+
86
+
87
+ def getValue(value, type)
88
+ s = env().value2bytes(value, type)
89
+ return s.force_encoding(Encoding::ASCII_8BIT)
90
+ end
91
+
92
+
93
+ def append(value, type=nil)
94
+ res = @body.append(getValue(value,type))
95
+ s = ""
96
+ res.each{|a|
97
+ if a.is_a?(Array)
98
+ s += sprintf(" %04X:%04X", a[1], a[0])
99
+ else
100
+ s += sprintf(" %08X", a)
101
+ end
102
+ }
103
+ log("Appended at %s",s)
104
+ end
105
+
106
+
107
+ def replace(value, type=nil)
108
+ @body.removeAppend()
109
+ return append(value,type)
110
+ end
111
+
112
+
113
+ def change(ofs, value, disp=nil, type=nil)
114
+ ofs = s2i(ofs)
115
+ isfile = disp && (disp[0]=='f' || disp[0]=='F') ? true : false
116
+ value = getValue(value, type)
117
+ if isfile
118
+ res = @header.change(ofs,value)
119
+ else
120
+ res = @body.change(ofs,value) + @header.headerSize()
121
+ end
122
+ log("Change added at %08X", res)
123
+ end
124
+
125
+
126
+ def dasm(ofs, size=nil, how=nil)
127
+ ofs = s2i(ofs ? ofs : "entry")
128
+ size = size ? s2i(size) : [0x20, @body.bytes.length-ofs].min
129
+ @body.dasm(ofs, size, how)
130
+ end
131
+
132
+
133
+ def valueof(str, type)
134
+ puts "value of " + str + " is:"
135
+ p getValue(str, type).unpack("H*")
136
+ end
137
+
138
+
139
+ def revert(what)
140
+ wid = env().s2i_nt(what)
141
+ what = wid[1] ? wid[0] : what
142
+ res = @header.revert(what)
143
+ res |= @body.revert(what)
144
+ raise "Don't know how to revert: "+what if !res
145
+ log("Reverted")
146
+ end
147
+
148
+
149
+ def save(filename, final=nil)
150
+ raise "Unknown final: " + final if final && final != "final"
151
+ raise "Filename expected." if !filename
152
+ open(filename, "wb:ascii-8bit"){|f|
153
+ @header.saveData(f)
154
+ @body.saveData(f)
155
+ if !final || final!='final'
156
+ f.write([ZM].pack('v'))
157
+ @header.saveChanges(f)
158
+ @body.saveChanges(f)
159
+ end
160
+ }
161
+ end
162
+
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,141 @@
1
+ require 'resedit/mz/changeable'
2
+ begin
3
+ require 'crabstone'
4
+ include Crabstone
5
+ $nocrabstone = false
6
+ rescue LoadError
7
+ $nocrabstone = true
8
+ end
9
+
10
+
11
+ module Resedit
12
+
13
+ class MZBody < Changeable
14
+
15
+ attr_reader :segments, :appSeg
16
+
17
+ def initialize(mz, file, size)
18
+ super(mz, file, size)
19
+ @segments = Set.new()
20
+ for i in 0..@mz.header.info[:NumberOfRelocations]-1
21
+ r = @mz.header.getRelocation(i)
22
+ @segments.add(r[1])
23
+ val = segData(r, 2).unpack('v')[0]
24
+ @segments.add(val)
25
+ end
26
+ @appSeg = (@realSize >> 4) + 1
27
+ end
28
+
29
+
30
+ def loadChanges(f)
31
+ super(f)
32
+ @appSeg = (@realSize >> 4) + 1
33
+ @segments.add(@appSeg)
34
+ end
35
+
36
+
37
+ def seg2Linear(a,s) (s << 4) + a end
38
+
39
+
40
+ def seg4Linear(linear)
41
+ linear >>= 4
42
+ min = @segments.sort.reverse.find{|e| e <= linear}
43
+ return min ? min : 0
44
+ end
45
+
46
+
47
+ def linear2seg(linear, inSegments=nil)
48
+ inSegments = [seg4Linear(linear)] if !inSegments
49
+ res = []
50
+ inSegments.each{|s|
51
+ raise sprintf("Linear %X less than segment %04X", inSegments[0], s) if linear < (s<<4)
52
+ a = linear - (s << 4)
53
+ res += [a,s]
54
+ }
55
+ return res
56
+ end
57
+
58
+
59
+ def segData(reloc, size, isStr=false)
60
+ ofs = seg2Linear(reloc[0], reloc[1])
61
+ return "None" if ofs>@bytes.length
62
+ return getData(ofs, size) if !isStr
63
+ return colVal(ofs, size)
64
+ end
65
+
66
+
67
+ def removeAppend()
68
+ @segments.each{|s|
69
+ @segments.delete(s) if (s << 4) > @realSize
70
+ }
71
+ super()
72
+ end
73
+
74
+
75
+ def revert(what)
76
+ @realOfs = @mz.header.headerSize()
77
+ super(what)
78
+ end
79
+
80
+
81
+ def append(bytes)
82
+ mode(HOW_ORIGINAL)
83
+ res = 0
84
+ addseg = false
85
+ if !@add
86
+ addseg = true
87
+ res = 0x10 - (@realSize % 0x10)
88
+ res = 0 if res == 0x10
89
+ bytes = ("\x90" * res).force_encoding(Encoding::ASCII_8BIT) + bytes if res > 0
90
+ end
91
+ res += super(bytes)
92
+ @mz.header.setCodeSize(@bytes.length + @add.length)
93
+ seg = linear2seg(res)
94
+ res = [res, seg]
95
+ if addseg
96
+ raise "Segs not match" if (@appSeg << 4) != res[0]
97
+ @segments.add(@appSeg)
98
+ res += [ [ 0, @appSeg] ]
99
+ end
100
+ return res
101
+ end
102
+
103
+
104
+ def print(what, how)
105
+ if what=="header"
106
+ puts "Known segments: " + @segments.sort.map{ |i| sprintf('%04X',i) }.join(", ")
107
+ return true
108
+ end
109
+ @realOfs = @mz.header.headerSize()
110
+ return super(what, how)
111
+ end
112
+
113
+
114
+ def dasm(ofs, size, how)
115
+ raise "Crabstone gem required to disasm." if $nocrabstone
116
+ mode(parseHow(how))
117
+ cs = Disassembler.new(ARCH_X86, MODE_16)
118
+ begin
119
+ while true
120
+ begin
121
+ d = getData(ofs,size)
122
+ cs.disasm(d, ofs).each {|i|
123
+ seg = linear2seg(i.address)
124
+ bts = i.bytes.map { |b| sprintf("%02X",b) }.join
125
+ inst = colStr(sprintf("%14s\t%s\t%s", bts, i.mnemonic, i.op_str), changed?(i.address, i.bytes.length))
126
+ printf("%08X %04X:%04X%s\n",i.address, seg[1], seg[0], inst)
127
+ }
128
+ break
129
+ rescue
130
+ ofs-=1
131
+ end
132
+ end
133
+
134
+ ensure
135
+ cs.close()
136
+ end
137
+ end
138
+
139
+
140
+ end
141
+ end
@@ -0,0 +1,123 @@
1
+ require 'resedit/mz/changeable'
2
+
3
+ module Resedit
4
+
5
+ class MZHeader < Changeable
6
+ MAGIC = 0x5a4D
7
+ BLK = 0x200
8
+ PARA = 0x10
9
+ HSIZE = 0x1C
10
+
11
+ attr_reader :info
12
+
13
+ def initialize(mz, file, size)
14
+ raise "Not MZ file" if size < HSIZE
15
+ super(mz, file, HSIZE)
16
+ @fsize = size
17
+ @_infoOrig = loadInfo()
18
+ @_info = nil
19
+ @info = @_infoOrig
20
+ raise "Not MZ file" if MAGIC != @info[:Magic]
21
+ readMore(file, headerSize() - HSIZE)
22
+ end
23
+
24
+
25
+ def mode(how)
26
+ super(how)
27
+ if @mode == HOW_ORIGINAL
28
+ @info = @_infoOrig
29
+ else
30
+ @_info = loadInfo() if !@_info
31
+ @info = @_info
32
+ end
33
+ end
34
+
35
+
36
+ def change(ofs, bytes)
37
+ super(ofs, bytes)
38
+ @_info = nil if (ofs < HSIZE)
39
+ end
40
+
41
+
42
+ def loadInfo()
43
+ v = getData(0, HSIZE).unpack('v*')
44
+ return {:Magic => v[0], :BytesInLastBlock => v[1], :BlocksInFile => v[2], :NumberOfRelocations => v[3],
45
+ :HeaderParagraphs => v[4], :MinExtraParagraphs => v[5], :MaxExtraParagraphs => v[6],
46
+ :SS => v[7], :SP => v[8], :Checksum => v[9], :IP => v[10], :CS => v[11],
47
+ :RelocTableOffset => v[12], :OverlayNumber => v[13]
48
+ }
49
+ end
50
+
51
+
52
+ def setCodeSize(size)
53
+ mode(HOW_CHANGED)
54
+ size += headerSize()
55
+ mod = size % BLK
56
+ ch = [mod, size / BLK + (mod ? 1 : 0)]
57
+ change(2, ch.pack('vv'))
58
+ end
59
+
60
+
61
+ def headerSize()
62
+ return @info[:HeaderParagraphs] * PARA
63
+ end
64
+
65
+
66
+ def fileSize()
67
+ sz = @info[:BlocksInFile] * BLK
68
+ if @info[:BytesInLastBlock] != 0
69
+ sz -= BLK - @info[:BytesInLastBlock]
70
+ end
71
+ return sz
72
+ end
73
+
74
+
75
+ def getRelocation(idx)
76
+ raise "Wrong relocation index " if idx<0 || idx>@info[:NumberOfRelocations]
77
+ return getData(@info[:RelocTableOffset] + idx * 4, 4).unpack('vv')
78
+ end
79
+
80
+
81
+ def freeSpace(middle = false)
82
+ return @info[:RelocTableOffset] - HSIZE if middle
83
+ return headerSize() - HSIZE - @info[:NumberOfRelocations] * 4
84
+ end
85
+
86
+
87
+ def print(what, how)
88
+ mode(parseHow(how))
89
+ if what == "header"
90
+ ofs=0
91
+ @info.each{|k,v|
92
+ printf("%20s:\t%s\n", k.to_s, colVal(ofs, 2))
93
+ ofs+=2
94
+ }
95
+ puts
96
+ fsz = fileSize()
97
+ hsz = headerSize()
98
+ s = colStr(sprintf("%d (%X)", fsz,fsz), changed?(2,4))
99
+ printf("mz file size: %s\treal file size: %d (0x%X)\n", s, @fsize, @fsize)
100
+ printf("header size: %s\n", colStr(hsz, changed?(8)))
101
+ printf("code size: %s\n", colStr(fsz - hsz, @mz.body.add != nil))
102
+ printf("reloc table size: %s\n", colStr(@info[:NumberOfRelocations] * 4, changed?(6)))
103
+ printf("free space in header: before relocs 0x%X, after relocs 0x%X\n", freeSpace(true), freeSpace())
104
+ return true
105
+ end
106
+ if what == "reloc"
107
+ ofs = @info[:RelocTableOffset]
108
+ for i in 0..@info[:NumberOfRelocations]-1
109
+ s1 = colVal(ofs,2)
110
+ s2 = colVal(ofs+2,2)
111
+ s3 = @mz.body.segData(getRelocation(i), 2, true)
112
+ printf("%08X\t%s:%s\t= %s\n", ofs, s2, s1, s3)
113
+ ofs += 4
114
+ end
115
+ return true
116
+ end
117
+ return super(what, how)
118
+ end
119
+
120
+
121
+ end
122
+
123
+ end
@@ -0,0 +1,82 @@
1
+ require 'singleton'
2
+
3
+ module Resedit
4
+
5
+ class MZEnv
6
+
7
+ include Singleton
8
+
9
+ def set(name,value)
10
+ MZEnv.class_eval{
11
+ define_method(name){ s2i(value) }
12
+ }
13
+ end
14
+
15
+ def s2i_nt(str)
16
+ return [s2i(str), true]
17
+ rescue Exception
18
+ return [0, false]
19
+ end
20
+
21
+ def s2i(str)
22
+ ss=str.split(':')
23
+ if ss.length == 2
24
+ ss[0] = '0x'+ss[0] if ss[0][0,2]!='0x'
25
+ ss[1] = '0x'+ss[1] if ss[1][0,2]!='0x'
26
+ return (s2i(ss[0]) << 4) + s2i(ss[1])
27
+ end
28
+ return eval(str, binding())
29
+ end
30
+
31
+
32
+ def valueHex(s, type)
33
+ s = s[0..-2] if s[-1] == 'h'
34
+ s = s[2..-1] if s[0,2] == '0x'
35
+ return nil if s.length == 0
36
+ sz = type[1]
37
+ sz = s.length / 2 + s.length % 2 if !sz || sz==0
38
+ hx = eval('0x'+s, binding())
39
+ s=""
40
+ for i in 0..sz-1
41
+ s += sprintf("%02X", hx & 0xFF)
42
+ hx >>= 8
43
+ end
44
+ return valueBytes(s)
45
+ rescue SyntaxError
46
+ return nil
47
+ end
48
+
49
+ def valueBytes(str)
50
+ return nil if str[0,2] == '0x' || str[0,2]=='0X'
51
+ return nil if str.length % 2 == 1
52
+ return nil if str[/\H/]
53
+ return [str].pack('H*')
54
+ rescue
55
+ return nil
56
+ end
57
+
58
+ def value2bytes(str, type)
59
+ tp = [nil, nil]
60
+ if type && type.length > 0
61
+ tp[0] = type[0]
62
+ t = type[1..-1]
63
+ t = t[1..-1] while t.length > 0 && (t[0]<'0' || t[0]>'9')
64
+ tp[1] = t.to_i
65
+ end
66
+ if tp[0]=='f' || (File.exists?(str) && !tp[0])
67
+ return File.read(str)
68
+ end
69
+ res = valueBytes(str) if !tp[0] || tp[0] == "b"
70
+ res = valueHex(str, tp) if !res && (!tp[0] || tp[0] == "h")
71
+ res = eval(str, binding()) if !res
72
+ return res if res.is_a?(String)
73
+ res = valueHex(res.to_s(16), tp)
74
+ raise str if !res
75
+ return res
76
+ rescue Exception => e
77
+ raise "Bad value: "+e.to_s
78
+ end
79
+
80
+ end
81
+
82
+ end