resedit 1.7.3 → 1.8
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 +4 -4
- data/lib/resedit.rb +10 -4
- data/lib/resedit/app/mz_command.rb +15 -6
- data/lib/resedit/classes/changeable.rb +27 -13
- data/lib/resedit/classes/config.rb +35 -0
- data/lib/resedit/{mz/mzenv.rb → classes/env.rb} +30 -15
- data/lib/resedit/classes/exefile.rb +339 -0
- data/lib/resedit/classes/hexwriter.rb +7 -15
- data/lib/resedit/mz/bw.rb +67 -0
- data/lib/resedit/mz/le.rb +234 -0
- data/lib/resedit/mz/multiexe.rb +85 -0
- data/lib/resedit/mz/mz.rb +231 -124
- metadata +8 -5
- data/lib/resedit/mz/mz_body.rb +0 -145
- data/lib/resedit/mz/mz_header.rb +0 -200
@@ -4,9 +4,9 @@ module Resedit
|
|
4
4
|
|
5
5
|
class HexWriter
|
6
6
|
|
7
|
-
attr_accessor :written
|
7
|
+
attr_accessor :written, :addressFormatter, :addr
|
8
8
|
|
9
|
-
def initialize(addr)
|
9
|
+
def initialize(addr, addressFormatter=nil)
|
10
10
|
@written = 0
|
11
11
|
@charsInLine = 0x10
|
12
12
|
@addr = addr
|
@@ -17,23 +17,15 @@ module Resedit
|
|
17
17
|
@chr = ''
|
18
18
|
@cchr = ''
|
19
19
|
@pcol = nil
|
20
|
-
@
|
21
|
-
end
|
22
|
-
|
23
|
-
def setSegments(segments, sfix)
|
24
|
-
@segments = segments.sort.reverse
|
25
|
-
@sfix = sfix
|
20
|
+
@addressFormatter = addressFormatter
|
26
21
|
end
|
27
22
|
|
28
23
|
def addrFormat()
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
min = 0 if !min
|
34
|
-
add = sprintf(" %04X:%04X", min, @addr - @sfix - (min << 4))
|
24
|
+
if @addressFormatter
|
25
|
+
res = @addressFormatter.formatAddress(@addr)+" | "
|
26
|
+
else
|
27
|
+
res = sprintf("%08X | ", @addr)
|
35
28
|
end
|
36
|
-
res = sprintf("%08X%s | ", @addr, add)
|
37
29
|
@addr += @charsInLine
|
38
30
|
return res
|
39
31
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'resedit/classes/exefile'
|
2
|
+
|
3
|
+
module Resedit
|
4
|
+
|
5
|
+
class BWHeader < ExeHeader
|
6
|
+
MAGIC = 0x5742
|
7
|
+
HSIZE = 0xB0
|
8
|
+
HDRDESCR = [:Magic, :LastPageBytes, :BlocksInFile, :Reserved1, :Reserved2, :MinAlloc, :MaxAlloc, :SS, :SP, :FirstRelocSel, :IP, :CS,
|
9
|
+
:RuntimeGdtSize, :MAKEPMVer, :NextHeaderPos, :CVInfoOffset, :LastSelUsed, :PMemAlloc, :AllocIncr, :Reserved4, :Options,
|
10
|
+
:TransStackSel, :ExpFlags, :ProgramSize, :GdtImageSize, :FirstSelector, :DefaultMemStrategy, :Reserved5, :TransferBufferSize,
|
11
|
+
:Reserved6, :ExpPath]
|
12
|
+
HDRUNPACK = "v14V2v3s6v6HHvs48a64"
|
13
|
+
|
14
|
+
# unsigned_16 signature; /* BW signature to mark valid file */
|
15
|
+
# unsigned_16 last_page_bytes; /* length of image mod 512 */
|
16
|
+
# unsigned_16 pages_in_file; /* number of 512 byte pages */
|
17
|
+
# unsigned_16 reserved1;
|
18
|
+
# unsigned_16 reserved2;
|
19
|
+
# unsigned_16 min_alloc; /* required memory, in KB */
|
20
|
+
# unsigned_16 max_alloc; /* max KB (private allocation) */
|
21
|
+
# unsigned_16 stack_seg; /* segment of stack */
|
22
|
+
# unsigned_16 stack_ptr; /* initial SP value */
|
23
|
+
# unsigned_16 first_reloc_sel; /* huge reloc list selector */
|
24
|
+
# unsigned_16 init_ip; /* initial IP value */
|
25
|
+
# unsigned_16 code_seg; /* segment of code */
|
26
|
+
# unsigned_16 runtime_gdt_size; /* runtime GDT size in bytes */
|
27
|
+
# unsigned_16 MAKEPM_version; /* ver * 100, GLU = (ver+10)*100 */
|
28
|
+
# /* end of DOS style EXE header */
|
29
|
+
# unsigned_32 next_header_pos; /* file pos of next spliced .EXP */
|
30
|
+
# unsigned_32 cv_info_offset; /* offset to start of debug info */
|
31
|
+
# unsigned_16 last_sel_used; /* last selector value used */
|
32
|
+
# unsigned_16 pmem_alloc; /* private xm amount KB if nonzero */
|
33
|
+
# unsigned_16 alloc_incr; /* auto ExtReserve amount, in KB */
|
34
|
+
# unsigned_8 reserved4[6];
|
35
|
+
# /* the following used to be referenced as gdtimage[0..1] */
|
36
|
+
# unsigned_16 options; /* runtime options */
|
37
|
+
# unsigned_16 trans_stack_sel; /* sel of transparent stack */
|
38
|
+
# unsigned_16 exp_flags; /* see ef_ constants below */
|
39
|
+
# unsigned_16 program_size; /* size of program in paras */
|
40
|
+
# unsigned_16 gdtimage_size; /* size of gdt in file (bytes) */
|
41
|
+
# unsigned_16 first_selector; /* gdt[first_sel] = gdtimage[0], 0 => 0x80 */
|
42
|
+
# unsigned_8 default_mem_strategy;
|
43
|
+
# unsigned_8 reserved5;
|
44
|
+
# unsigned_16 transfer_buffer_size; /* default in bytes, 0 => 8KB */
|
45
|
+
# /* the following used to be referenced as gdtimage[2..15] */
|
46
|
+
# unsigned_8 reserved6[48];
|
47
|
+
# char EXP_path[64]; /* original .EXP file name */
|
48
|
+
|
49
|
+
def headerSize(); HSIZE end
|
50
|
+
|
51
|
+
def fileSize(); @info[:BlocksInFile] * BLK + @info[:LastPageBytes] end
|
52
|
+
def entry; sprintf("%04X:%04X", @info[:CS], @info[:IP]) end
|
53
|
+
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
class BWBody < ExeBody
|
59
|
+
end
|
60
|
+
|
61
|
+
class BW < ExeFile
|
62
|
+
HDRCLASS = BWHeader
|
63
|
+
BODYCLASS = BWBody
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'resedit/classes/exefile'
|
2
|
+
|
3
|
+
module Resedit
|
4
|
+
|
5
|
+
class LEHeader < ExeHeader
|
6
|
+
MAGIC = [0x454C, 0x584C, 0x434C]
|
7
|
+
HSIZE = 0xAC
|
8
|
+
HDRDESCR = [:Magic, :BOrd, :WOrd, :FormatLevel, :CpuType, :OsType, :ModuleVersion, :ModuleFlags, :ModulePages, :EIPObj, :EIP, :ESPObj, :ESP,
|
9
|
+
:PageSize, :PageShift, :FixupSize, :FixupCsum, :LoaderSize, :LoaderCsum, :ObjectTableOfs, :ObjectsInModule,
|
10
|
+
:ObjectPageOfs, :ObjectIterOfs, :ResourceTableOfs, :ResourceTableEntries, :ResidentTableOfs, :EntryTableOfs,
|
11
|
+
:ModuleDirectivesOfs, :ModuleDirectives, :FixupPageOfs, :FixupRecordOfs, :ImportTblOfs, :ImportEntries, :ImportProcOfs,
|
12
|
+
:PerPageCsum, :DataPagesOfs, :PreloadPages, :NonResTableOfs, :NonResTableLen, :NonResTableCsum, :AutoDSObject,
|
13
|
+
:DebugInfoOfs, :DebugInfoLen, :InstancePreload, :InstanceDemand, :Heapsize]
|
14
|
+
HDRUNPACK = "vHHVvvV40"
|
15
|
+
|
16
|
+
# 00h | "L" "X" |B-ORD|W-ORD| FORMAT LEVEL |
|
17
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
18
|
+
# 08h | CPU TYPE | OS TYPE | MODULE VERSION |
|
19
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
20
|
+
# 10h | MODULE FLAGS | MODULE # OF PAGES |
|
21
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
22
|
+
# 18h | EIP OBJECT # | EIP |
|
23
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
24
|
+
# 20h | ESP OBJECT # | ESP |
|
25
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
26
|
+
# 28h | PAGE SIZE | PAGE OFFSET SHIFT / LE: last page bytes !!! |
|
27
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
28
|
+
# 30h | FIXUP SECTION SIZE | FIXUP SECTION CHECKSUM|
|
29
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
30
|
+
# 38h | LOADER SECTION SIZE |LOADER SECTION CHECKSUM|
|
31
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
32
|
+
# 40h | OBJECT TABLE OFF | # OBJECTS IN MODULE |
|
33
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
34
|
+
# 48h | OBJECT PAGE TABLE OFF | OBJECT ITER PAGES OFF |
|
35
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
36
|
+
# 50h | RESOURCE TABLE OFFSET |#RESOURCE TABLE ENTRIES|
|
37
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
38
|
+
# 58h | RESIDENT NAME TBL OFF | ENTRY TABLE OFFSET |
|
39
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
40
|
+
# 60h | MODULE DIRECTIVES OFF | # MODULE DIRECTIVES |
|
41
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
42
|
+
# 68h | FIXUP PAGE TABLE OFF |FIXUP RECORD TABLE OFF |
|
43
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
44
|
+
# 70h | IMPORT MODULE TBL OFF | # IMPORT MOD ENTRIES |
|
45
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
46
|
+
# 78h | IMPORT PROC TBL OFF | PER-PAGE CHECKSUM OFF |
|
47
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
48
|
+
# 80h | DATA PAGES OFFSET FROM MZ !!! | #PRELOAD PAGES |
|
49
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
50
|
+
# 88h | NON-RES NAME TBL OFF | NON-RES NAME TBL LEN |
|
51
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
52
|
+
# 90h | NON-RES NAME TBL CKSM | AUTO DS OBJECT # |
|
53
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
54
|
+
# 98h | DEBUG INFO OFF | DEBUG INFO LEN |
|
55
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
56
|
+
# A0h | #INSTANCE PRELOAD | #INSTANCE DEMAND |
|
57
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
58
|
+
# A8h | HEAPSIZE |
|
59
|
+
#
|
60
|
+
|
61
|
+
# Object Table
|
62
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
63
|
+
# 00h | VIRTUAL SIZE | RELOC BASE ADDR |
|
64
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
65
|
+
# 08h | OBJECT FLAGS | PAGE TABLE INDEX |
|
66
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
67
|
+
# 10h | # PAGE TABLE ENTRIES | RESERVED |
|
68
|
+
# +-----+-----+-----+-----+-----+-----+-----+-----+
|
69
|
+
|
70
|
+
attr_reader :tables
|
71
|
+
|
72
|
+
def initialize(exe, file, fsize)
|
73
|
+
@_tablesOrig = nil
|
74
|
+
@sofs = file.tell()
|
75
|
+
super(exe, file, fsize)
|
76
|
+
end
|
77
|
+
|
78
|
+
def loadTables(file)
|
79
|
+
if @_tablesOrig==nil
|
80
|
+
objtblend = @sofs+@info[:ObjectTableOfs]+@info[:ObjectsInModule]*0x18
|
81
|
+
#addData(file, objtblend-file.tell())
|
82
|
+
objtblend = @sofs+@info[:ObjectPageOfs]+@info[:ModulePages]*8
|
83
|
+
addData(file, objtblend-file.tell())
|
84
|
+
end
|
85
|
+
tbl = {:Objects => [], :Pages => []}
|
86
|
+
ofs = 0
|
87
|
+
for i in 0..@info[:ObjectsInModule]-1
|
88
|
+
descr = getData(@info[:ObjectTableOfs]+i*0x18, 0x18).unpack("V*")
|
89
|
+
tbl[:Objects] += [descr]
|
90
|
+
@exe.env.set("seg#{i}".to_sym, ofs.to_s)
|
91
|
+
ofs += descr[4]*@info[:PageSize]
|
92
|
+
end
|
93
|
+
for i in 0..@info[:ModulePages]-1
|
94
|
+
tbl[:Pages] += getData(@info[:ObjectPageOfs]+i*4, 4).unpack("V")
|
95
|
+
end
|
96
|
+
if @_tablesOrig==nil
|
97
|
+
@tables = @_tablesOrig = tbl
|
98
|
+
end
|
99
|
+
return tbl
|
100
|
+
end
|
101
|
+
|
102
|
+
def loadTail(file);
|
103
|
+
addData(file, headerSize() + @sofs - file.tell())
|
104
|
+
end
|
105
|
+
|
106
|
+
def readRelocs()
|
107
|
+
def read(pos,cnt, unp); [getData(@info[:FixupRecordOfs]+pos, cnt).unpack(unp),pos+cnt] end
|
108
|
+
ret = {}
|
109
|
+
pgs = getData(@info[:FixupPageOfs], 4*(@info[:ModulePages]+1)).unpack("V*")
|
110
|
+
for i in 0..@info[:ModulePages]-1
|
111
|
+
pos = pgs[i]
|
112
|
+
op = @tables[:Pages][i]
|
113
|
+
pgofs = ((op>>16) + ((op>>8) & 0xFF) -1) * @info[:PageSize]
|
114
|
+
ret[pgofs] = {}
|
115
|
+
while pos<pgs[i+1]
|
116
|
+
v,pos = read(pos, 5, "CCvC")
|
117
|
+
raise "Unknown fixup type #{v[0]} #{v[1]}" if (v[0]!=7 && v[0]!=2) || (v[1] & ~0x10 !=0 )
|
118
|
+
trg, pos=read(pos, v[1]==0x10 ? 4 : 2,v[1]==0x10 ? "V" : "v")
|
119
|
+
next if v[2]>0x7FFF
|
120
|
+
ofs = pgofs+v[2]
|
121
|
+
ret[pgofs][pgofs+v[2]] = @tables[:Objects][v[3]-1][1]+trg[0]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
return ret
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def mode(how)
|
129
|
+
super(how)
|
130
|
+
if @mode == HOW_ORIGINAL
|
131
|
+
@tables = @_tablesOrig
|
132
|
+
else
|
133
|
+
@_tables = loadTables(nil) if !@_tables
|
134
|
+
@tables = @_tables
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def headerSize(); @info[:DataPagesOfs]-@exe.mzSize end
|
139
|
+
|
140
|
+
def fileSize(); headerSize()+(@info[:ModulePages]-1)*@info[:PageSize]+@info[:PageShift] end
|
141
|
+
def entry; sprintf("0x%08X", @info[:EIP]) end
|
142
|
+
|
143
|
+
def print(what, how=nil)
|
144
|
+
ret = super(what, how)
|
145
|
+
if what=="tables"
|
146
|
+
puts "Objects: #{@tables[:Objects].map{|o| o.map{|v| v.to_s(16)}}}"
|
147
|
+
puts "Pages: #{@tables[:Pages].map{|x| x.to_s(16)}}"
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
return ret
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class LEBody < ExeBody
|
155
|
+
|
156
|
+
def initialize(exe, file, fsize)
|
157
|
+
super(exe, file, fsize)
|
158
|
+
end
|
159
|
+
|
160
|
+
def sections()
|
161
|
+
if !@sex
|
162
|
+
ofs = 0
|
163
|
+
@sex=[]
|
164
|
+
@exe.header.tables[:Objects].each{|descr|
|
165
|
+
sz = descr[4] * @exe.header.info[:PageSize]
|
166
|
+
if sz!=0
|
167
|
+
@sex += [[descr[1], ofs, sz]]
|
168
|
+
ofs += sz
|
169
|
+
end
|
170
|
+
}
|
171
|
+
end
|
172
|
+
@sex
|
173
|
+
end
|
174
|
+
|
175
|
+
def relocations()
|
176
|
+
if !@relocs
|
177
|
+
@relocs = @exe.header.readRelocs().sort_by{|k,v| k}.reverse.to_h
|
178
|
+
@relocs.each{|k,v|
|
179
|
+
@relocs[k] = v.sort_by{|k2,v2| k2}.reverse.to_h
|
180
|
+
}
|
181
|
+
end
|
182
|
+
@relocs
|
183
|
+
end
|
184
|
+
|
185
|
+
def formatAddress(raw)
|
186
|
+
return sprintf("%08X %08X", raw, raw2addr(raw))
|
187
|
+
end
|
188
|
+
|
189
|
+
def raw2addr(ofs)
|
190
|
+
s = sections.find{|s| s[1]<=ofs && s[1]+s[2]>ofs}
|
191
|
+
raise "Not raw offset #{ofs.to_s(16)}" if !s
|
192
|
+
return s[0]+ofs-s[1]
|
193
|
+
end
|
194
|
+
|
195
|
+
def addr2raw(addr)
|
196
|
+
s = sections.find{|s| s[0]<=addr && s[0]+s[2]>addr}
|
197
|
+
raise "Not virtual address #{addr.to_s(16)}" if !s
|
198
|
+
return s[1]+addr-s[0]
|
199
|
+
end
|
200
|
+
|
201
|
+
def readRelocated(ofs, size);
|
202
|
+
rel = relocations()
|
203
|
+
d = getData(ofs, size)
|
204
|
+
@relocs.each{|o,v|
|
205
|
+
next if o>ofs+size
|
206
|
+
v.each{|a, r|
|
207
|
+
next if a>ofs+size
|
208
|
+
break if a<ofs
|
209
|
+
pos = a-ofs
|
210
|
+
d = d[0,pos] + [r].pack("V") + (d[pos+4..-1] or '')
|
211
|
+
}
|
212
|
+
break if o<ofs
|
213
|
+
}
|
214
|
+
return d[0,size]
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
class LE < ExeFile
|
221
|
+
HDRCLASS = LEHeader
|
222
|
+
BODYCLASS = LEBody
|
223
|
+
MODE = 32
|
224
|
+
|
225
|
+
attr_reader :mzSize
|
226
|
+
|
227
|
+
def load(file, sz, prev)
|
228
|
+
@mzSize = prev.header.fileSize()
|
229
|
+
super(file, sz, prev)
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'resedit/classes/exefile'
|
2
|
+
require 'resedit/mz/mz'
|
3
|
+
require 'resedit/mz/bw'
|
4
|
+
require 'resedit/mz/le'
|
5
|
+
|
6
|
+
module Resedit
|
7
|
+
|
8
|
+
class Multiexe < ExeFile
|
9
|
+
|
10
|
+
KNOWN_TYPES = {'MZ' => MZ, 'BW' => BW, 'LE' => LE, 'LX' => LE, 'LC'=>LE}
|
11
|
+
|
12
|
+
attr_reader :cur
|
13
|
+
|
14
|
+
def initialize(path, quiet = false)
|
15
|
+
@cur = nil
|
16
|
+
@parts = []
|
17
|
+
super(path, quiet)
|
18
|
+
end
|
19
|
+
|
20
|
+
def load(f, fsize)
|
21
|
+
cid = 0
|
22
|
+
while !f.eof?
|
23
|
+
s = f.read(2).unpack('A2')[0]
|
24
|
+
f.seek(-2, :CUR)
|
25
|
+
#log("Loading part #{s} @ 0x#{f.tell.to_s(16)}")
|
26
|
+
raise "Unknown format #{s}" if !KNOWN_TYPES[s]
|
27
|
+
obj = KNOWN_TYPES[s].new(nil, @quiet)
|
28
|
+
sz = fsize - f.tell()
|
29
|
+
obj.load(f, sz, @parts.length>0 ? @parts[-1] : 0 )
|
30
|
+
cid = @parts.length() if obj.is_a?(LE)
|
31
|
+
@parts += [obj]
|
32
|
+
end
|
33
|
+
setPart(cid)
|
34
|
+
end
|
35
|
+
|
36
|
+
def loadConfig(cfg)
|
37
|
+
@parts.each.with_index{|pr, i|
|
38
|
+
pr.loadConfig(cfg[i.to_s])
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def header; @cur.header end
|
43
|
+
def body; @cur.body end
|
44
|
+
def env; @cur.env end
|
45
|
+
|
46
|
+
def setPart(id); @cur = @parts[id] end
|
47
|
+
def close(); @parts.each{|pr| pr.close()} end
|
48
|
+
|
49
|
+
def print(what, how=nil)
|
50
|
+
if what=="parts"
|
51
|
+
puts "#{@parts.length} parts:"
|
52
|
+
@parts.each.with_index{|pr, i|
|
53
|
+
puts "#{i}: #{pr.class} #{pr}"
|
54
|
+
}
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
@cur.print(what, how)
|
58
|
+
end
|
59
|
+
def hex(ofs, size=nil, how=nil, disp=nil); @cur.hex(ofs, size, how, disp) end
|
60
|
+
def hexify(str); @cur.hexify(str) end
|
61
|
+
def getValue(value, type); @cur.getValue(value, type) end
|
62
|
+
def append(value, type=nil, where=nil); @cur.append(value, type, where) end
|
63
|
+
def replace(value, type=nil, where=nil); @cur.replace(value, type, where) end
|
64
|
+
def change(ofs, value, disp=nil, type=nil); @cur.change(ofs, value, disp, type) end
|
65
|
+
def reloc(ofs); @cur.reloc(ofs) end
|
66
|
+
def dasm(ofs, size=nil, how=nil) @cur.dasm(ofs, size, how) end
|
67
|
+
def valueof(str, type); @cur.valueof(str, type) end
|
68
|
+
def revert(what); @cur.revert(what) end
|
69
|
+
def readRelocated(ofs, size); @cur.readRelocated(ofs, size) end
|
70
|
+
|
71
|
+
def saveConfig()
|
72
|
+
cfg = {}
|
73
|
+
@parts.each.with_index{|pr, i|
|
74
|
+
cfg[i] = pr.saveConfig()
|
75
|
+
}
|
76
|
+
return cfg
|
77
|
+
end
|
78
|
+
|
79
|
+
def saveFile(f)
|
80
|
+
@parts.each{|pr| pr.saveFile(f)}
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/lib/resedit/mz/mz.rb
CHANGED
@@ -1,175 +1,282 @@
|
|
1
|
-
require 'resedit/
|
2
|
-
require 'resedit/mz/mz_body'
|
3
|
-
require 'resedit/classes/hexwriter'
|
4
|
-
require 'resedit/mz/mzenv'
|
1
|
+
require 'resedit/classes/exefile'
|
5
2
|
|
6
3
|
module Resedit
|
7
4
|
|
8
|
-
class
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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("0:0"))
|
36
|
-
end
|
5
|
+
class MZHeader < ExeHeader
|
6
|
+
MAGIC = 0x5A4D
|
7
|
+
HSIZE = 0x1C
|
8
|
+
HDRDESCR = [:Magic, :BytesInLastBlock, :BlocksInFile, :NumberOfRelocations, :HeaderParagraphs, :MinExtraParagraphs, :MaxExtraParagraphs,
|
9
|
+
:SS, :SP, :Checksum, :IP, :CS, :RelocTableOffset, :OverlayNumber]
|
37
10
|
|
11
|
+
attr_reader :relocFix
|
38
12
|
|
39
|
-
def
|
13
|
+
def initialize(exe, file, size)
|
14
|
+
super(exe, file, size)
|
15
|
+
@relocFix = 0
|
16
|
+
@newOfs = false
|
17
|
+
if @info[:RelocTableOffset]>=0x40
|
18
|
+
@newOfs = getData(0x3C, 2).unpack("v")[0]
|
19
|
+
end
|
40
20
|
end
|
41
21
|
|
22
|
+
def entry; sprintf("%04X:%04X", @info[:CS], @info[:IP]) end
|
42
23
|
|
43
|
-
def
|
44
|
-
|
24
|
+
def setFileSize(size)
|
25
|
+
mode(HOW_CHANGED)
|
26
|
+
mod = size % BLK
|
27
|
+
setInfo(:BytesInLastBlock, [mod, size / BLK + (mod ? 1 : 0)])
|
45
28
|
end
|
46
29
|
|
30
|
+
def rebuildHeader(codesize)
|
31
|
+
mode(HOW_ORIGINAL)
|
32
|
+
ss = @info[:SS]
|
33
|
+
cs = @info[:CS]
|
34
|
+
sz = fileSize()-headerSize()
|
35
|
+
codesize += PARA - codesize % PARA if codesize % PARA!=0
|
36
|
+
changeSize(sz + codesize + headerSize())
|
37
|
+
paras = codesize / PARA
|
38
|
+
setInfo(:SS, ss+paras)
|
39
|
+
setInfo(:CS, cs+paras)
|
40
|
+
for i in 0..@info[:NumberOfRelocations]-1
|
41
|
+
rel = getRelocation(i)
|
42
|
+
rel[1] += paras-@relocFix
|
43
|
+
fix(@info[:RelocTableOffset] + i * 4, rel.pack('vv'))
|
44
|
+
end
|
45
|
+
mode(HOW_CHANGED)
|
46
|
+
@relocFix = paras
|
47
|
+
MZEnv.instance().set(:relocFix, paras.to_s)
|
48
|
+
return codesize
|
49
|
+
end
|
47
50
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
+
def addHeaderSize(size)
|
52
|
+
mode(HOW_CHANGED)
|
53
|
+
paras = size/16 + (size%16 == 0 ? 0 : 1)
|
54
|
+
insert(headerSize(), "\x00" * (paras * PARA))
|
55
|
+
setFileSize(fileSize() + paras * PARA)
|
56
|
+
setInfo(:HeaderParagraphs, @info[:HeaderParagraphs] + paras)
|
57
|
+
mode(HOW_CHANGED)
|
58
|
+
end
|
51
59
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
60
|
+
def loadChanges(cfg)
|
61
|
+
super(cfg)
|
62
|
+
mode(HOW_ORIGINAL)
|
63
|
+
ocs = @info[:CS]
|
64
|
+
mode(HOW_CHANGED)
|
65
|
+
ncs = @info[:CS]
|
66
|
+
@relocFix = ncs - ocs
|
55
67
|
end
|
56
68
|
|
69
|
+
def headerSize(); @info[:HeaderParagraphs] * PARA end
|
57
70
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
71
|
+
def fileSize()
|
72
|
+
return @newOfs if @newOfs
|
73
|
+
sz = @info[:BlocksInFile] * BLK
|
74
|
+
if @info[:BytesInLastBlock] != 0
|
75
|
+
sz -= BLK - @info[:BytesInLastBlock]
|
76
|
+
end
|
77
|
+
return sz
|
64
78
|
end
|
65
79
|
|
66
80
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
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()
|
81
|
+
def getRelocation(idx)
|
82
|
+
raise "Wrong relocation index " if idx<0 || idx >= @info[:NumberOfRelocations]
|
83
|
+
return getData(@info[:RelocTableOffset] + idx * 4, 4).unpack('vv')
|
84
84
|
end
|
85
85
|
|
86
|
+
def setRelocation(idx, data)
|
87
|
+
raise "Wrong relocation index " if idx<0 || idx >= @info[:NumberOfRelocations]
|
88
|
+
change(@info[:RelocTableOffset] + idx * 4, data.pack('vv'))
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def freeSpace(middle = false)
|
93
|
+
return @info[:RelocTableOffset] - HSIZE if middle
|
94
|
+
return headerSize() - HSIZE - @info[:NumberOfRelocations] * 4
|
95
|
+
end
|
86
96
|
|
87
|
-
def
|
88
|
-
|
89
|
-
return
|
97
|
+
def setSpaceForRelocs(count)
|
98
|
+
add = count - @info[:NumberOfRelocations]
|
99
|
+
return if add<=0
|
100
|
+
add -= freeSpace()/4
|
101
|
+
return if add<=0
|
102
|
+
addHeaderSize(add*4)
|
103
|
+
setInfo(:NumberOfRelocations, count)
|
104
|
+
mode(HOW_CHANGED)
|
90
105
|
end
|
91
106
|
|
107
|
+
def addReloc(ofs)
|
108
|
+
mode(HOW_CHANGED)
|
109
|
+
#check relocation exists
|
110
|
+
for i in 0..@info[:NumberOfRelocations]-1
|
111
|
+
rel = getRelocation(i)
|
112
|
+
if @exe.body.seg2Linear(rel[0], rel[1]) == ofs
|
113
|
+
return false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
#add relocation
|
117
|
+
setSpaceForRelocs(@info[:NumberOfRelocations]+1)
|
118
|
+
val = @exe.body.linear2seg(ofs)
|
119
|
+
setRelocation(@info[:NumberOfRelocations], val)
|
120
|
+
return true
|
121
|
+
end
|
92
122
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
123
|
+
def print(what, how)
|
124
|
+
mode(parseHow(how))
|
125
|
+
if what == "header"
|
126
|
+
ofs=0
|
127
|
+
@info.each{|k,v|
|
128
|
+
printf("%20s:\t%s\n", k.to_s, colVal(ofs, 2))
|
129
|
+
ofs+=2
|
130
|
+
}
|
131
|
+
puts
|
132
|
+
fsz = fileSize()
|
133
|
+
hsz = headerSize()
|
134
|
+
s = colStr(sprintf("%d (%X)", fsz,fsz), changed?(2, 4))
|
135
|
+
printf("mz file size: %s\treal file size: %d (0x%X)\n", s, @fsize, @fsize)
|
136
|
+
printf("header size: %s\n", colStr(hsz, changed?(8, 2)))
|
137
|
+
printf("code size: %s\n", colStr(fsz - hsz, @mz.body.changed?(0)))
|
138
|
+
printf("reloc table size: %s\n", colStr(@info[:NumberOfRelocations] * 4, changed?(6, 2)))
|
139
|
+
printf("free space in header: before relocs 0x%X, after relocs 0x%X\n", freeSpace(true), freeSpace())
|
140
|
+
printf("reloc fix: 0x%X\n", @relocFix)
|
141
|
+
return true
|
142
|
+
end
|
143
|
+
@exe.body.mode(@mode)
|
144
|
+
if what == "reloc"
|
145
|
+
ofs = @info[:RelocTableOffset]
|
146
|
+
for i in 0..@info[:NumberOfRelocations]-1
|
147
|
+
s1 = colVal(ofs,2)
|
148
|
+
s2 = colVal(ofs+2,2)
|
149
|
+
s3 = @exe.body.segData(getRelocation(i), 2, true)
|
150
|
+
printf("%08X\t%s:%s\t= %s\n", ofs, s2, s1, s3)
|
151
|
+
ofs += 4
|
102
152
|
end
|
103
|
-
|
104
|
-
|
105
|
-
return
|
153
|
+
return true
|
154
|
+
end
|
155
|
+
return super(what, how)
|
106
156
|
end
|
107
157
|
|
158
|
+
end
|
159
|
+
|
108
160
|
|
109
|
-
|
110
|
-
|
111
|
-
|
161
|
+
class MZBody < ExeBody
|
162
|
+
|
163
|
+
attr_reader :segments
|
164
|
+
|
165
|
+
def initialize(exe, file, size)
|
166
|
+
super(exe, file, size)
|
167
|
+
@segments = nil
|
112
168
|
end
|
113
169
|
|
170
|
+
def reloadSegments()
|
171
|
+
@segments = Set.new()
|
172
|
+
for i in 0..@exe.header.info[:NumberOfRelocations]-1
|
173
|
+
r = @exe.header.getRelocation(i)
|
174
|
+
@segments.add(r[1])
|
175
|
+
sd = segData(r, 2)
|
176
|
+
next if !sd
|
177
|
+
val = sd.unpack('v')[0]
|
178
|
+
@segments.add(val)
|
179
|
+
end
|
180
|
+
@msegs = @segments.sort.reverse
|
181
|
+
end
|
114
182
|
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
183
|
+
def patchRelocs(add)
|
184
|
+
@segments = Set.new()
|
185
|
+
for i in 0..@exe.header.info[:NumberOfRelocations]-1
|
186
|
+
r = @exe.header.getRelocation(i)
|
187
|
+
@segments.add(r[1])
|
188
|
+
ofs = seg2Linear(r[0], r[1])
|
189
|
+
val = getData(ofs, 2).unpack('v')[0] + add
|
190
|
+
fix(ofs, [val].pack('v'))
|
191
|
+
@segments.add(val)
|
123
192
|
end
|
124
|
-
|
193
|
+
@msegs = @segments.sort.reverse
|
125
194
|
end
|
126
195
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
|
196
|
+
def seg2Linear(a,s) (s << 4) + a end
|
197
|
+
|
198
|
+
def seg4Linear(linear)
|
199
|
+
linear >>= 4
|
200
|
+
reloadSegments() if !@segments
|
201
|
+
min = @segments.sort.reverse.find{|e| e <= linear}
|
202
|
+
return min ? min : 0
|
131
203
|
end
|
132
204
|
|
133
205
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
206
|
+
def linear2seg(linear, inSegments=nil)
|
207
|
+
inSegments = [seg4Linear(linear)] if !inSegments
|
208
|
+
res = []
|
209
|
+
inSegments.each{|s|
|
210
|
+
raise sprintf("Linear %X less than segment %04X", inSegments[0], s) if linear < (s<<4)
|
211
|
+
a = linear - (s << 4)
|
212
|
+
res += [a,s]
|
213
|
+
}
|
214
|
+
return res
|
138
215
|
end
|
139
216
|
|
140
217
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
218
|
+
def segData(reloc, size, isStr=false)
|
219
|
+
ofs = seg2Linear(reloc[0], reloc[1])
|
220
|
+
return nil if ofs > @root.size()
|
221
|
+
return getData(ofs, size) if !isStr
|
222
|
+
return colVal(ofs, size)
|
144
223
|
end
|
145
224
|
|
225
|
+
def formatAddress(addr)
|
226
|
+
@sfix=0
|
227
|
+
seg = (addr-@sfix) >> 4
|
228
|
+
min = @msegs.find{|e| e <= seg}
|
229
|
+
min = 0 if !min
|
230
|
+
return sprintf("%08X %04X:%04X", addr, min, addr - @sfix - (min << 4))
|
231
|
+
end
|
146
232
|
|
147
|
-
def
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
233
|
+
def raw2addr(ofs); linear2seg(ofs) end
|
234
|
+
def addr2raw(addr); seg2Linear(addr[0], addr[1]) end
|
235
|
+
|
236
|
+
|
237
|
+
def append(bytes, where=nil)
|
238
|
+
mode(HOW_CHANGED)
|
239
|
+
relfix = @exe.header.relocFix
|
240
|
+
res = @addsz
|
241
|
+
buf = @addsz>0 ? @root.nbuf[0, @addsz] : ''
|
242
|
+
buf += bytes
|
243
|
+
removeAppend()
|
244
|
+
@addsz = buf.length
|
245
|
+
sz = @exe.header.rebuildHeader(@addsz)
|
246
|
+
insert(0, bytes + "\x00"*(sz-@addsz))
|
247
|
+
seg = linear2seg(res)
|
248
|
+
res = [res, seg, sz/0x10]
|
249
|
+
patchRelocs(sz/0x10 - relfix)
|
250
|
+
return res
|
154
251
|
end
|
155
252
|
|
156
|
-
|
157
|
-
|
253
|
+
|
254
|
+
def print(what, how)
|
255
|
+
if what=="header"
|
256
|
+
reloadSegments() if !@segments
|
257
|
+
puts "Known segments: " + @segments.sort.map{ |i| sprintf('%04X',i) }.join(", ")
|
258
|
+
return true
|
259
|
+
end
|
260
|
+
return super(what, how)
|
158
261
|
end
|
159
262
|
|
160
263
|
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
264
|
+
def printDasm(inst, str)
|
265
|
+
seg = linear2seg(inst.address)
|
266
|
+
printf("%08X %04X:%04X%s\n",inst.address, seg[1], seg[0], str)
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
class MZ < ExeFile
|
273
|
+
HDRCLASS = MZHeader
|
274
|
+
BODYCLASS = MZBody
|
275
|
+
|
276
|
+
def reloc(ofs)
|
277
|
+
ofs = s2i(ofs)
|
278
|
+
res = @header.addReloc(ofs)
|
279
|
+
log((res ? "Relocation added %08X" : "Relocation %08X already exists"), ofs)
|
173
280
|
end
|
174
281
|
|
175
282
|
end
|