hacktools 0.39.0__tar.gz → 0.40.0__tar.gz
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.
- {hacktools-0.39.0/hacktools.egg-info → hacktools-0.40.0}/PKG-INFO +1 -1
- hacktools-0.40.0/hacktools/__init__.py +1 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/psp.py +162 -0
- {hacktools-0.39.0 → hacktools-0.40.0/hacktools.egg-info}/PKG-INFO +1 -1
- hacktools-0.39.0/hacktools/__init__.py +0 -1
- {hacktools-0.39.0 → hacktools-0.40.0}/LICENSE +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/MANIFEST.in +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/README.md +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/arch.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/assets/icon.png +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/c_ext/cmp_cri.c +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/c_ext/cmp_lzss.c +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/c_ext/cmp_misc.c +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/c_ext/cmp_racjin.c +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/c_ext/inc.h +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/common.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/compression.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/cpk.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/gb.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/gui.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/nasm.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/nds.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/nitro.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/psx.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/wii.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools/ws.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools.egg-info/SOURCES.txt +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools.egg-info/dependency_links.txt +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools.egg-info/requires.txt +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/hacktools.egg-info/top_level.txt +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/setup.cfg +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/setup.py +0 -0
- {hacktools-0.39.0 → hacktools-0.40.0}/tests/test_cmp.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.40.0"
|
|
@@ -243,6 +243,168 @@ def readELF(infile):
|
|
|
243
243
|
return elf
|
|
244
244
|
|
|
245
245
|
|
|
246
|
+
def expandELF(elfpath, n, fixheap=True):
|
|
247
|
+
# Inserts a code cave of n bytes at the end of the RWX PT_LOAD segment of a
|
|
248
|
+
# 32-bit ELF, fixing up all the headers so the file stays valid. Returns the
|
|
249
|
+
# virtual address of the cave (the old segment _end). When fixheap is set,
|
|
250
|
+
# MIPS references to _end used as the heap base are bumped past the cave.
|
|
251
|
+
# Read headers
|
|
252
|
+
with common.Stream(elfpath, "rb") as f:
|
|
253
|
+
e_phoff = f.readUIntAt(0x1c)
|
|
254
|
+
e_shoff = f.readUIntAt(0x20)
|
|
255
|
+
f.seek(0x2a)
|
|
256
|
+
e_phentsize = f.readUShort()
|
|
257
|
+
e_phnum = f.readUShort()
|
|
258
|
+
e_shentsize = f.readUShort()
|
|
259
|
+
e_shnum = f.readUShort()
|
|
260
|
+
|
|
261
|
+
phs = [] # (ph_off, p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align)
|
|
262
|
+
for i in range(e_phnum):
|
|
263
|
+
ph_off = e_phoff + i * e_phentsize
|
|
264
|
+
f.seek(ph_off)
|
|
265
|
+
entry = (ph_off,
|
|
266
|
+
f.readUInt(), f.readUInt(), f.readUInt(), f.readUInt(),
|
|
267
|
+
f.readUInt(), f.readUInt(), f.readUInt(), f.readUInt())
|
|
268
|
+
phs.append(entry)
|
|
269
|
+
|
|
270
|
+
sections = [] # (sh_off, sh_addr, sh_offset, sh_size)
|
|
271
|
+
for i in range(e_shnum):
|
|
272
|
+
sh_off = e_shoff + i * e_shentsize
|
|
273
|
+
f.seek(sh_off + 0x0c)
|
|
274
|
+
sh_addr = f.readUInt()
|
|
275
|
+
sh_offset = f.readUInt()
|
|
276
|
+
sh_size = f.readUInt()
|
|
277
|
+
sections.append((sh_off, sh_addr, sh_offset, sh_size))
|
|
278
|
+
|
|
279
|
+
f.seek(0)
|
|
280
|
+
data = bytearray(f.read())
|
|
281
|
+
|
|
282
|
+
# Identify the RWX PT_LOAD segment that holds .text/.data
|
|
283
|
+
target = next((p for p in phs if p[1] == 1 and p[7] == 7), None)
|
|
284
|
+
if target is None:
|
|
285
|
+
common.logError("Could not find RWX PT_LOAD segment")
|
|
286
|
+
return 0
|
|
287
|
+
_, _, p_offset, p_vaddr, _, p_filesz, p_memsz, _, _ = target
|
|
288
|
+
|
|
289
|
+
# The cave will live at vaddr = p_vaddr + p_memsz, above all original BSS
|
|
290
|
+
# We need filesz to reach that vaddr and cover the cave, which means inserting
|
|
291
|
+
# (p_memsz - p_filesz) zero bytes for the BSS gap + n cave bytes
|
|
292
|
+
bss_gap = p_memsz - p_filesz
|
|
293
|
+
insert_total = bss_gap + n
|
|
294
|
+
insert_at = p_offset + p_filesz
|
|
295
|
+
|
|
296
|
+
# Splice the inserted bytes (all zero) into the file
|
|
297
|
+
data[insert_at:insert_at] = bytes(insert_total)
|
|
298
|
+
with common.Stream(elfpath, "wb") as f:
|
|
299
|
+
f.write(bytes(data))
|
|
300
|
+
|
|
301
|
+
# Patch headers (positions are post-splice)
|
|
302
|
+
new_shoff = e_shoff + insert_total if e_shoff >= insert_at else e_shoff
|
|
303
|
+
with common.Stream(elfpath, "r+b") as f:
|
|
304
|
+
# e_shoff
|
|
305
|
+
f.writeUIntAt(0x20, new_shoff)
|
|
306
|
+
|
|
307
|
+
# Program headers
|
|
308
|
+
for (ph_off, p_type, ph_p_offset, ph_p_vaddr, ph_p_paddr, ph_p_filesz, ph_p_memsz, ph_p_flags, ph_p_align) in phs:
|
|
309
|
+
# Program header table itself doesn't move (e_phoff < insert_at).
|
|
310
|
+
new_p_offset = ph_p_offset + insert_total if ph_p_offset >= insert_at else ph_p_offset
|
|
311
|
+
new_p_vaddr = ph_p_vaddr
|
|
312
|
+
new_p_paddr = ph_p_paddr
|
|
313
|
+
new_p_filesz = ph_p_filesz
|
|
314
|
+
new_p_memsz = ph_p_memsz
|
|
315
|
+
if (p_type, ph_p_flags) == (1, 7):
|
|
316
|
+
# The cave segment grows in both file and memory.
|
|
317
|
+
new_p_filesz = ph_p_filesz + insert_total
|
|
318
|
+
new_p_memsz = ph_p_memsz + n
|
|
319
|
+
elif ph_p_vaddr >= p_vaddr + p_memsz:
|
|
320
|
+
# Any segment that lived at/above the old memsz boundary (e.g. PH1)
|
|
321
|
+
# gets shifted up by the cave size so it doesn't overlap
|
|
322
|
+
new_p_vaddr = ph_p_vaddr + n
|
|
323
|
+
new_p_paddr = ph_p_paddr + n
|
|
324
|
+
f.writeUIntAt(ph_off + 0x04, new_p_offset)
|
|
325
|
+
f.writeUIntAt(ph_off + 0x08, new_p_vaddr)
|
|
326
|
+
f.writeUIntAt(ph_off + 0x0c, new_p_paddr)
|
|
327
|
+
f.writeUIntAt(ph_off + 0x10, new_p_filesz)
|
|
328
|
+
f.writeUIntAt(ph_off + 0x14, new_p_memsz)
|
|
329
|
+
|
|
330
|
+
# Section headers
|
|
331
|
+
for sh_off, sh_addr, sh_offset, sh_size in sections:
|
|
332
|
+
new_addr, new_offset, new_size = sh_addr, sh_offset, sh_size
|
|
333
|
+
if sh_offset >= insert_at:
|
|
334
|
+
new_offset = sh_offset + insert_total
|
|
335
|
+
elif sh_offset != 0 and sh_size != 0 and sh_offset + sh_size == insert_at:
|
|
336
|
+
new_size = sh_size + insert_total
|
|
337
|
+
if sh_addr >= p_vaddr + p_memsz:
|
|
338
|
+
new_addr = sh_addr + n
|
|
339
|
+
new_sh_off = sh_off + insert_total if sh_off >= insert_at else sh_off
|
|
340
|
+
f.writeUIntAt(new_sh_off + 0x0c, new_addr)
|
|
341
|
+
f.writeUIntAt(new_sh_off + 0x10, new_offset)
|
|
342
|
+
f.writeUIntAt(new_sh_off + 0x14, new_size)
|
|
343
|
+
|
|
344
|
+
# Bump references to the original _end (= old PT_LOAD memsz boundary) that
|
|
345
|
+
# are used as the heap base. If we don't shift them, the game's first
|
|
346
|
+
# malloc/sbrk lands on top of our cave
|
|
347
|
+
old_end = p_vaddr + p_memsz
|
|
348
|
+
new_end = old_end + n
|
|
349
|
+
if fixheap:
|
|
350
|
+
sec_lo = p_offset
|
|
351
|
+
sec_hi = p_offset + p_filesz + insert_total
|
|
352
|
+
with common.Stream(elfpath, "r+b") as f:
|
|
353
|
+
# Literals: any 4-byte word equal to old_end gets bumped.
|
|
354
|
+
for off in range(sec_lo, sec_hi - 3, 4):
|
|
355
|
+
if f.readUIntAt(off) == old_end:
|
|
356
|
+
f.writeUIntAt(off, new_end)
|
|
357
|
+
# lui+addiu/ori pairs producing old_end, followed by a syscall nearby
|
|
358
|
+
for off in range(sec_lo, sec_hi - 4, 4):
|
|
359
|
+
w = f.readUIntAt(off)
|
|
360
|
+
if (w >> 26) != 0x0f:
|
|
361
|
+
continue
|
|
362
|
+
rt = (w >> 16) & 0x1f
|
|
363
|
+
hi = (w & 0xffff) << 16
|
|
364
|
+
for j in range(1, 9):
|
|
365
|
+
off2 = off + j * 4
|
|
366
|
+
if off2 >= sec_hi:
|
|
367
|
+
break
|
|
368
|
+
w2 = f.readUIntAt(off2)
|
|
369
|
+
op2 = w2 >> 26
|
|
370
|
+
rs2 = (w2 >> 21) & 0x1f
|
|
371
|
+
if rs2 != rt:
|
|
372
|
+
continue
|
|
373
|
+
if op2 == 0x09:
|
|
374
|
+
lo = w2 & 0xffff
|
|
375
|
+
if lo >= 0x8000:
|
|
376
|
+
lo -= 0x10000
|
|
377
|
+
addr = (hi + lo) & 0xffffffff
|
|
378
|
+
elif op2 == 0x0d:
|
|
379
|
+
addr = hi | (w2 & 0xffff)
|
|
380
|
+
else:
|
|
381
|
+
continue
|
|
382
|
+
if addr != old_end:
|
|
383
|
+
break
|
|
384
|
+
# Look ahead up to 3 instructions for a "syscall" (opcode=0, funct=0x0c)
|
|
385
|
+
# Only the heap-setup site matches this
|
|
386
|
+
has_syscall = False
|
|
387
|
+
for k in range(1, 5):
|
|
388
|
+
off3 = off2 + k * 4
|
|
389
|
+
if off3 >= sec_hi:
|
|
390
|
+
break
|
|
391
|
+
w3 = f.readUIntAt(off3)
|
|
392
|
+
if (w3 >> 26) == 0 and (w3 & 0x3f) == 0x0c:
|
|
393
|
+
has_syscall = True
|
|
394
|
+
break
|
|
395
|
+
if not has_syscall:
|
|
396
|
+
break
|
|
397
|
+
new_hi = (new_end >> 16) & 0xffff
|
|
398
|
+
new_lo = new_end & 0xffff
|
|
399
|
+
if op2 == 0x09 and (new_lo & 0x8000):
|
|
400
|
+
new_hi = (new_hi + 1) & 0xffff
|
|
401
|
+
f.writeUIntAt(off, (w & 0xffff0000) | new_hi)
|
|
402
|
+
f.writeUIntAt(off2, (w2 & 0xffff0000) | new_lo)
|
|
403
|
+
break
|
|
404
|
+
common.logDebug("Expanded ELF, cave at", common.toHex(old_end), "size", common.toHex(n))
|
|
405
|
+
return old_end
|
|
406
|
+
|
|
407
|
+
|
|
246
408
|
def extractBinaryStrings(elf, foundstrings, infile, func, encoding="shift_jis", elfsections=[".rodata"]):
|
|
247
409
|
with common.Stream(infile, "rb") as f:
|
|
248
410
|
for sectionname in elfsections:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.39.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|