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