resedit 1.7.3 → 1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 77669d3b3c572c15f14392c6bb44ded2e682e993
4
- data.tar.gz: 600dd6a10b2c2c6c2a051b1e8f1ee3b04afaa651
3
+ metadata.gz: d5bb82eb4e37c29d800e45d61d1fd8359afb224d
4
+ data.tar.gz: 206c81a15f6e4bc8f50893ce9c8900fb89ec4ad4
5
5
  SHA512:
6
- metadata.gz: e98241f942125af594613c729bee32dd5aaeb10eff2a6532dec135c0c052663e3267d9f28d3585cfcc60a84f80fe0c83ad67e0343aee31f89c6e130de5ef4a88
7
- data.tar.gz: b47a0ed36ab4c03a5ce709ec90aa970f313a5c4c95dc728ee693efb6365c9e70d81233fd600fea07536f5c8cf1c42e0a3600c566f968f7a2092daa3513960482
6
+ metadata.gz: c28f9d26f1e08a3980b254ada58e772a2a65efec98ca6e4b9008625ca1f7790a61777d9dd882a618c001abcfa3a71b9d07ecacaf321c176fcfcbcb3f90d713cd
7
+ data.tar.gz: 05c2a94a3fba256b35e624d2477678d3210a3009f994fa347f500afae29dbbb7eabe79789ade857336e9ed1ecb9ab872da30d58b6ebe1d3bcf3c93de71716ce0
@@ -8,16 +8,22 @@ require 'resedit/app/app'
8
8
  require 'resedit/text/huffman'
9
9
  require 'resedit/app/font_convert'
10
10
  require 'resedit/app/text_convert'
11
+ require 'resedit/classes/env'
12
+ require 'resedit/classes/colorizer'
13
+ require 'resedit/classes/hexwriter'
14
+ require 'resedit/classes/changeable'
15
+ require 'resedit/classes/exefile'
16
+ require 'resedit/classes/config'
11
17
  require 'resedit/convert/bitconv'
12
18
  require 'resedit/convert/bitstream'
13
19
  require 'resedit/convert/codepatch'
14
20
  require 'resedit/convert/colors'
15
21
  require 'resedit/mz/mz'
16
- require 'resedit/classes/colorizer'
17
- require 'resedit/classes/hexwriter'
18
- require 'resedit/classes/changeable'
22
+ require 'resedit/mz/bw'
23
+ require 'resedit/mz/le'
24
+ require 'resedit/mz/multiexe'
19
25
 
20
26
 
21
27
  module Resedit
22
- VERSION = "1.7.3"
28
+ VERSION = "1.8"
23
29
  end
@@ -1,7 +1,6 @@
1
1
  require 'resedit/app/app_command'
2
- require 'resedit/mz/mz'
3
- require 'resedit/mz/mzenv'
4
-
2
+ require 'resedit/mz/multiexe'
3
+ require 'resedit/classes/env'
5
4
 
6
5
  module Resedit
7
6
 
@@ -28,7 +27,9 @@ module Resedit
28
27
  "revert"=>[method(:revert), "revert changes", {"ofs"=>"change offset/all"}],
29
28
  "hex"=>[method(:hex), "print hex file", {"ofs" => "data offset", "size" => "data size", "how"=>"original/modified", "disp" => "code/file"}],
30
29
  "dasm"=>[method(:dasm), "print disasm", {"ofs" => "data offset", "size" => "data size", "how"=>"original/modified"}],
30
+ "eval"=>[method(:expr), "print expression", {"expr" => "expression"}],
31
31
  }
32
+ @shorters = {"p"=>"print", "e"=>"eval"}
32
33
  @files = []
33
34
  @cur = nil
34
35
  end
@@ -58,7 +59,9 @@ module Resedit
58
59
 
59
60
  def getfile(id)
60
61
  return @cur if id == nil
61
- i,res=MZEnv.instance.s2i_nt(id)
62
+ #env = !@cur ? Env.new(self) : @cur.env
63
+ env = Env.new()
64
+ i,res = env.s2i_nt(id)
62
65
  if res
63
66
  raise "Bad file id: " + i.to_s if @files.length < i || i < 0
64
67
  return @files[i]
@@ -95,7 +98,7 @@ module Resedit
95
98
  def use(params)
96
99
  mz = getfile(params['file'])
97
100
  if mz==nil
98
- mz = MZ.new(params['file'])
101
+ mz = Multiexe.new(params['file'])
99
102
  @files+=[mz]
100
103
  end
101
104
  @cur = mz
@@ -105,7 +108,7 @@ module Resedit
105
108
 
106
109
  def close(params)
107
110
  mz = getfile(params['file'])
108
- raise "File not found: "+fn if nil == fl
111
+ raise "File not found: "+fn if nil == mz
109
112
  @files -= [mz]
110
113
  @cur = nil if @cur == mz
111
114
  mz.close()
@@ -158,9 +161,15 @@ module Resedit
158
161
  cur().dasm(params['ofs'], params['size'], params['how'])
159
162
  end
160
163
 
164
+ def expr(params)
165
+ env = @cur && @cur.env ? @cur.env : Env.new()
166
+ puts env.s2i(params['expr'])
167
+ end
168
+
161
169
 
162
170
  def job(params)
163
171
  cmd = params['cmd']
172
+ cmd = @shorters[cmd] if @shorters[cmd]
164
173
  if cmd.length==0 || File.exist?(cmd)
165
174
  App::get().setShell('mz')
166
175
  return if cmd.length == 0
@@ -224,6 +224,8 @@ module Resedit
224
224
 
225
225
  def debug(); LOG.level = Logger::DEBUG end
226
226
 
227
+ def size; @root.size end
228
+
227
229
  def dbgdump
228
230
  LOG.debug("---#{@root.size()}---\n")
229
231
  @root.dump()
@@ -294,27 +296,39 @@ module Resedit
294
296
  file.write(bytes())
295
297
  end
296
298
 
297
- def saveChanges(file)
299
+ def hexify(bts)
300
+ bts.each_byte.map { |b| sprintf("%02X",b) }.join
301
+ end
302
+
303
+ def unhexify(str)
304
+ str.scan(/../).collect { |c| c.to_i(16).chr }.join
305
+ end
306
+
307
+ def saveChanges()
298
308
  mode(HOW_CHANGED)
309
+ cfg = {}
299
310
  chs = getChanges()
300
- file.write([chs.length].pack('V'))
301
311
  chs.each{|o,bts|
302
- flg = bts[0].length==0 ? 0x80000000 : 0
303
- file.write([o, bts[1].length | flg ].pack('VV'))
304
- file.write(bts[0]) if flg==0
312
+ obj = {}
313
+ obj["insert"] = bts[1].length if bts[0].length==0
314
+ obj["change"] = hexify(bts[0]) if bts[0].length>0
315
+ cfg[o] = obj
305
316
  }
317
+ return cfg
306
318
  end
307
319
 
308
- def loadChanges(file)
320
+ def loadChanges(hs)
309
321
  mode(HOW_CHANGED)
310
322
  @root.revert
311
- clen = file.read(4).unpack('V')[0]
312
- for _ in 0..clen-1
313
- ofs, len = file.read(8).unpack('VV')
314
- isNu = len & 0x80000000 !=0
315
- len &= 0x7FFFFFFF
316
- @root.cload(ofs, isNu ? "" : file.read(len), len)
317
- end
323
+ hs.each{|ofs, obj|
324
+ if obj['insert']
325
+ len = obj['insert']
326
+ @root.cload(ofs.to_i, "", len)
327
+ elsif obj['change']
328
+ bts = unhexify(obj['change'])
329
+ @root.cload(ofs.to_i, bts, bts.length)
330
+ end
331
+ }
318
332
  mode(HOW_CHANGED)
319
333
  end
320
334
 
@@ -0,0 +1,35 @@
1
+ require 'json'
2
+
3
+ module Resedit
4
+
5
+ class Config
6
+
7
+ attr_accessor :cfg
8
+ def initialize(fname, section=nil)
9
+ @fname, @section = fname, section
10
+ @cfg = load()
11
+ @cfg = (@cfg[@section] or {}) if @section
12
+ end
13
+
14
+ def [](nm); @cfg[nm] end
15
+ def []=(nm, value); @cfg[nm]=value end
16
+
17
+ def load();
18
+ File.exists?(@fname) ? JSON.parse(File.read(@fname)) : {}
19
+ end
20
+
21
+ def save()
22
+ if @section
23
+ c = load()
24
+ c[@section] = @cfg
25
+ else
26
+ c = cfg
27
+ end
28
+ open(@fname, "w"){|f|
29
+ f.write(JSON.pretty_generate(c))
30
+ }
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -1,15 +1,17 @@
1
- require 'singleton'
1
+ require 'ostruct'
2
2
 
3
3
  module Resedit
4
4
 
5
- class MZEnv
5
+ class Env < OpenStruct
6
6
 
7
- include Singleton
7
+ def initialize(owner=nil)
8
+ super()
9
+ @owner = owner
10
+ @mode = @owner ? @owner.class::MODE : 16
11
+ end
8
12
 
9
- def set(name,value)
10
- MZEnv.class_eval{
11
- define_method(name){ s2i(value) }
12
- }
13
+ def set(name, value)
14
+ self[name] = s2i(value)
13
15
  end
14
16
 
15
17
  def s2i_nt(str)
@@ -18,17 +20,30 @@ module Resedit
18
20
  return [0, false]
19
21
  end
20
22
 
23
+ def addr2raw16(ss)
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
+ fix = 0
27
+ if ss.length == 3
28
+ raise "Dont known how to #{ss[2]} address" if ss[2]!="fix"
29
+ fix = relocFix
30
+ end
31
+ return ((s2i(ss[0])+fix) << 4) + s2i(ss[1])
32
+ end
33
+
34
+ def addr2raw32(ss)
35
+ ss[1] = '0x'+ss[1] if ss[1][0,2]!='0x'
36
+ if ss[0]==''
37
+ return @owner.body.addr2raw(s2i(ss[1]))
38
+ end
39
+ ss[0] = 'seg'+ss[0] if ss[0][0,3]!='seg'
40
+ return eval(ss[0]+"+"+ss[1])
41
+ end
42
+
21
43
  def s2i(str)
22
44
  ss=str.split(':')
23
45
  if ss.length == 2 || ss.length == 3
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
- fix = 0
27
- if ss.length == 3
28
- raise "Dont known how to #{ss[2]} address" if ss[2]!="fix"
29
- fix = relocFix
30
- end
31
- return ((s2i(ss[0])+fix) << 4) + s2i(ss[1])
46
+ return @mode==32 ? addr2raw32(ss) : addr2raw16(ss)
32
47
  end
33
48
  return eval(str, binding())
34
49
  end
@@ -0,0 +1,339 @@
1
+ require 'resedit/classes/changeable'
2
+ require 'resedit/classes/hexwriter'
3
+ require 'resedit/classes/env'
4
+ require 'json'
5
+
6
+ begin
7
+ require 'crabstone'
8
+ include Crabstone
9
+ $nocrabstone = false
10
+ rescue LoadError
11
+ $nocrabstone = true
12
+ end
13
+
14
+ module Resedit
15
+
16
+ class ExeHeader < Changeable
17
+ BLK = 0x200
18
+ PARA = 0x10
19
+ HDRDESCR = [:Magic]
20
+ HDRUNPACK = "v*"
21
+
22
+ attr_reader :info, :exe
23
+
24
+ def initialize(exe, file, fsize)
25
+ raise "Not EXE file" if fsize < self.class::HSIZE
26
+ @exe = exe
27
+ super(file, self.class::HSIZE)
28
+ @_infoOrig = loadInfo()
29
+ @_info = nil
30
+ @info = @_infoOrig
31
+ @relocFix = 0
32
+ raise "Not EXE file" if self.class::MAGIC != @info[:Magic] && (!self.class::MAGIC.is_a?(Array) || !self.class::MAGIC.include?(@info[:Magic]))
33
+ loadTables(file)
34
+ loadTail(file)
35
+ end
36
+
37
+ def mode(how)
38
+ super(how)
39
+ if @mode == HOW_ORIGINAL
40
+ @info = @_infoOrig
41
+ else
42
+ @_info = loadInfo() if !@_info
43
+ @info = @_info
44
+ end
45
+ end
46
+
47
+
48
+ def change(ofs, bytes)
49
+ super(ofs, bytes)
50
+ @_info = nil if (ofs < HSIZE)
51
+ end
52
+
53
+
54
+ def loadInfo()
55
+ v = getData(0, self.class::HSIZE).unpack(self.class::HDRUNPACK)
56
+ ret = self.class::HDRDESCR.map.with_index { |x, i| [x, v[i]] }.to_h
57
+ return ret
58
+ end
59
+ def loadTables(file); end
60
+ def loadTail(file);
61
+ addData(file, headerSize() - self.class::HSIZE)
62
+ end
63
+
64
+
65
+ def setInfo(field, values, pack="v*")
66
+ raise "Unknown header field #{field}" if !self.class::HDRDESCR.include?(field)
67
+ values = [values] if !values.is_a?(Array)
68
+ change(self.class::HDRDESCR.index(field)*2, values.pack(pack))
69
+ end
70
+
71
+ def setBodySize(size); setFileSize(size + headerSize()) end
72
+
73
+ def headerSize(); self.class::HSIZE end
74
+
75
+ def print(what, how)
76
+ return false if what!="header"
77
+ mode(parseHow(how))
78
+ ofs=0
79
+ wsz = @exe.wsize
80
+ @info.each{|k,v|
81
+ printf("%20s:\t%s\n", k.to_s, colStr(v, changed?(ofs,wsz)))
82
+ ofs+=2
83
+ }
84
+ puts
85
+ return true
86
+ end
87
+
88
+ def setHeaderSize(size); raise "NotImplemented" end
89
+ def fileSize(); raise "NotImplemented" end
90
+ def setFileSize(size); raise "NotImplemented" end
91
+ def entry(size); raise "NotImplemented" end
92
+ end
93
+
94
+
95
+ class ExeBody < Changeable
96
+
97
+ attr_reader :exe
98
+
99
+ def initialize(exe, file, size)
100
+ @exe = exe
101
+ super(file, size)
102
+ @addsz = 0
103
+ end
104
+
105
+ def removeAppend()
106
+ mode(HOW_CHANGED)
107
+ undo(0) if @root.obuf.length==0
108
+ @addsz = 0
109
+ end
110
+
111
+ def revert(what)
112
+ super(what)
113
+ @addsz = 0
114
+ end
115
+
116
+ def printDasm(inst, str)
117
+ printf("%08X %s\n",inst.address, str)
118
+ end
119
+
120
+ def hex(wr, ofs, size, how)
121
+ wr.addressFormatter = self
122
+ if how && (how[0]='r' || how[0]='R')
123
+ wr.addBytes(readRelocated(ofs, size))
124
+ return
125
+ end
126
+ super(wr, ofs, size, how)
127
+ end
128
+
129
+ def formatAddress(raw)
130
+ return sprintf("%08X", raw)
131
+ end
132
+
133
+ def dasm(ofs, size, how, mode)
134
+ raise "Crabstone gem required to disasm." if $nocrabstone
135
+ relocated = false
136
+ if how && how[0]='r' || how[0]='R'
137
+ relocated = true
138
+ how = nil
139
+ end
140
+ mode(parseHow(how))
141
+ cs = Disassembler.new(ARCH_X86, mode==16 ? MODE_16 : MODE_32)
142
+ begin
143
+ while true
144
+ begin
145
+ d = relocated ? readRelocated(ofs, size) : getData(ofs, size)
146
+ cs.disasm(d, ofs).each {|i|
147
+ bts = i.bytes.map { |b| sprintf("%02X",b) }.join
148
+ inst = colStr(sprintf("%14s\t%s\t%s", bts, i.mnemonic, i.op_str), changed?(i.address, i.bytes.length))
149
+ printDasm(i, inst)
150
+ }
151
+ break
152
+ rescue
153
+ ofs-=1
154
+ end
155
+ end
156
+ ensure
157
+ cs.close()
158
+ end
159
+ end
160
+
161
+ def raw2addr(ofs); raise "Not implemented" end
162
+ def addr2raw(addr); raise "Not implemented" end
163
+ def append(bytes, where=nil); raise "NotImplemented" end
164
+ def addrFormatter(hofs); nil end
165
+ def readRelocated(ofs, size); raise "NotImplemented" end
166
+ end
167
+
168
+ class ExeFile
169
+ HDRCLASS = nil
170
+ BODYCLASS = nil
171
+ CFGEXT = ".mzcfg.json"
172
+ MODE = 16
173
+
174
+ attr_reader :header, :body, :fname, :path, :name, :env
175
+
176
+ def initialize(path=nil, quiet = false)
177
+ @quiet = quiet
178
+ @path=path
179
+ @env = Env.new(self)
180
+ if @path!=nil
181
+ @path = path.downcase()
182
+ fsize = File.size(path)
183
+ open(@path,"rb:ascii-8bit"){|f|
184
+ load(f, fsize)
185
+ }
186
+ if File.exist?(@path+CFGEXT)
187
+ cfg = JSON.parse(File.read(@path+CFGEXT))
188
+ loadConfig(cfg)
189
+ end
190
+ @fname = File.basename(@path)
191
+ @name = File.basename(@path, ".*")
192
+ end
193
+ end
194
+
195
+ def wsize; self.class::MODE/8 end
196
+
197
+ def load(f, fsize, prev=nil)
198
+ @header = self.class::HDRCLASS.new(self, f, fsize)
199
+ @body = self.class::BODYCLASS.new(self, f, @header.fileSize() - @header.headerSize())
200
+ @env.set(:entry, @header.entry())
201
+ @env.set(:append, sprintf("0"))
202
+ end
203
+
204
+ def loadConfig(cfg)
205
+ cfg = cfg[self.class.name]
206
+ raise "Wrong config: #{self.class.name} expected" if !cfg
207
+ @header.loadChanges(cfg['header'])
208
+ @body.loadChanges(cfg['body'])
209
+ end
210
+
211
+ def close(); end
212
+ def log(fmt, *args); App::get().log(fmt, *args) if !@quiet end
213
+ def s2i(str) return @env.s2i(str) end
214
+
215
+
216
+ def is?(id)
217
+ id = id.downcase
218
+ return id == @path || id == @fname || id == @name
219
+ end
220
+
221
+ def print(what, how=nil)
222
+ puts "Header changes:" if what=="changes"
223
+ res = @header.print(what, how)
224
+ puts "Code changes:" if what=="changes"
225
+ res |= @body.print(what, how)
226
+ raise "Don't know how to print: " + what if !res
227
+ end
228
+
229
+
230
+ def hex(ofs, size=nil, how=nil, disp=nil)
231
+ ofs = ofs ? s2i(ofs) : 0
232
+ size = size ? s2i(size) : 0x100
233
+ isfile = disp && (disp[0]=='f' || disp[0]=='F') ? true : false
234
+ wr = HexWriter.new(ofs)
235
+ hsz = 0
236
+ if isfile
237
+ @header.mode(@header.parseHow(how))
238
+ hsz = @header.headerSize()
239
+ size = @header.hex(wr, ofs, size, how) if ofs < hsz
240
+ ofs -= hsz
241
+ ofs = 0 if ofs < 0
242
+ wr.addr = 0
243
+ end
244
+ @body.hex(wr, ofs, size, how) if size > 0
245
+ wr.finish()
246
+ end
247
+
248
+ def hexify(str); @header.hexify(str) end
249
+
250
+ def getValue(value, type)
251
+ s = @env.value2bytes(value, type)
252
+ return s.force_encoding(Encoding::ASCII_8BIT)
253
+ end
254
+
255
+
256
+ def append(value, type=nil, where=nil)
257
+ where = s2i(where) if where
258
+ res = @body.append(getValue(value,type), where)
259
+ s = ""
260
+ res.each{|a|
261
+ if a.is_a?(Array)
262
+ s += sprintf(" %04X:%04X", a[1], a[0])
263
+ else
264
+ s += sprintf(" %08X", a)
265
+ end
266
+ }
267
+ log("Appended at %s",s)
268
+ return res
269
+ end
270
+
271
+
272
+ def replace(value, type=nil, where=nil)
273
+ @body.removeAppend()
274
+ return append(value, type, where)
275
+ end
276
+
277
+
278
+ def change(ofs, value, disp=nil, type=nil)
279
+ ofs = s2i(ofs)
280
+ isfile = disp && (disp[0]=='f' || disp[0]=='F') ? true : false
281
+ value = getValue(value, type)
282
+ if isfile
283
+ res = @header.change(ofs,value)
284
+ else
285
+ res = @body.change(ofs,value) + @header.headerSize()
286
+ end
287
+ log("Change added at %08X", res) if res
288
+ end
289
+
290
+ def reloc(ofs); raise "NotImplemented" end
291
+
292
+ def readRelocated(ofs, size); @body.readRelocated(ofs, size) end
293
+
294
+ def dasm(ofs, size=nil, how=nil)
295
+ ofs = s2i(ofs ? ofs : "entry")
296
+ size = size ? s2i(size) : [0x20, @body.bytes.length-ofs].min
297
+ @body.dasm(ofs, size, how, self.class::MODE)
298
+ end
299
+
300
+
301
+ def valueof(str, type)
302
+ puts "value of " + str + " is:"
303
+ p getValue(str, type).unpack("H*")
304
+ end
305
+
306
+
307
+ def revert(what)
308
+ wid = @env.s2i_nt(what)
309
+ what = wid[1] ? wid[0] : what
310
+ res = @header.revert(what)
311
+ res |= @body.revert(what)
312
+ raise "Don't know how to revert: "+what if !res
313
+ log("Reverted")
314
+ end
315
+
316
+ def saveConfig()
317
+ cfg = {}
318
+ cfg['header'] = @header.saveChanges()
319
+ cfg['body'] = @body.saveChanges()
320
+ return {self.class.name => cfg}
321
+ end
322
+
323
+ def saveFile(f)
324
+ @header.saveData(f)
325
+ @body.saveData(f)
326
+ end
327
+
328
+ def save(filename)
329
+ raise "Filename expected." if !filename
330
+ open(filename, "wb:ascii-8bit"){|f|
331
+ saveFile(f)
332
+ }
333
+ open(filename+CFGEXT, "w"){|f|
334
+ f.write(JSON.pretty_generate(saveConfig()))
335
+ }
336
+ end
337
+ end
338
+
339
+ end