Skip to content

Instantly share code, notes, and snippets.

@ox1111
Forked from 0x36/depac.py
Created March 25, 2024 23:40
Show Gist options
  • Save ox1111/13c95b9d61e83415dd377c8f289b7f14 to your computer and use it in GitHub Desktop.
Save ox1111/13c95b9d61e83415dd377c8f289b7f14 to your computer and use it in GitHub Desktop.
# Fixing LC_DYLD_CHAINED_FIXUPS for macOS M1 kext drivers
# -*- coding: utf-8 -*-
#@category macOS.kext
from generic.continues import RethrowContinuesFactory
from ghidra.app.script import GhidraScript
from ghidra.app.util.bin import ByteProvider, RandomAccessByteProvider, BinaryReader
from ghidra.app.util.bin.format.macho import MachHeader,Section, commands
from ghidra.program.model.address import Address
from java.io import File
from java.io import ByteArrayInputStream;
from ghidra.program.model.data import PointerDataType
import struct
MH_EXECUTE = 0x00000002
MH_KEXT_BUNDLE = 0x0000000b
LC_DYLD_CHAINED_FIXUPS = 0x80000034
def depac():
print "[+] Fixing PAC'd pointers ... ",
f = File( currentProgram.getExecutablePath())
assert f.exists() == True
mem = currentProgram.getMemory()
image_base = currentProgram.getImageBase()
provider = RandomAccessByteProvider(f)
header = MachHeader.createMachHeader( RethrowContinuesFactory.INSTANCE, provider )
header.parse()
if header.getFileType() != MH_KEXT_BUNDLE:
print "Kernel image not supported, only KEXT"
return
depac_kext(provider,header)
def depac_kext(provider,header):
read64 = lambda off: struct.unpack("<Q",br.readByteArray(off,8))[0]
read32 = lambda off: struct.unpack("<I",br.readByteArray(off,4))[0]
image_base = currentProgram.getImageBase()
cmds = header.getLoadCommands()
mem = currentProgram.getMemory()
listing = currentProgram.getListing()
tcmd = None
for cmd in cmds:
if cmd.getCommandType() & 0xffffffff != LC_DYLD_CHAINED_FIXUPS:
continue
tcmd = cmd
break
assert tcmd != None
index = tcmd.getStartIndex()
br = BinaryReader(provider,True)
ll = br.readIntArray(index,cmd.getCommandSize()/4)
cmd = ll[0] & 0xffffffff
cmdsize = ll[1]
dataoff = ll[2]
datasize = ll[3]
off = dataoff
fixup =br.readIntArray(dataoff,7)
symbols_offset = fixup[3]
imports_offset = fixup[2]
starts_offset = fixup[1]
import_off = dataoff + imports_offset
syms_off = dataoff + symbols_offset
segs_off = off + starts_offset
segs_count = read32(segs_off)
segs = br.readIntArray(segs_off + 4,segs_count)
for seg_i in range(segs_count):
if segs[seg_i] == 0:
continue
starts_off = segs_off + segs[seg_i]
starts = br.readByteArray(starts_off,24)
tmp = struct.unpack("<IHHQIHH",starts)
page_count = tmp[5]
page_starts = br.readByteArray(starts_off+22,page_count*2)
page_starts = struct.unpack("<"+"H" * page_count,page_starts)
page_size, segment_offset = tmp[1],tmp[3]
i = 0
for idx in page_starts:
if idx == 0xffff:
continue
addr = image_base.add(segment_offset + i * page_size + idx )
off = segment_offset + i * page_size + idx
i+=1
while True and monitor.isCancelled() == False:
content = read64(off)
offset = content & 0xffffffff
nxt = (content >> 51) & 2047
bind = (content >> 62) & 1
tag = (content >> 32) & 0xffff
if bind == 1:
name_off = read32(import_off + offset * 4)
name_off = (name_off >> 9)
symbol = br.readAsciiString(syms_off + name_off )
symbolTable = currentProgram.getSymbolTable()
ns = symbolTable.getNamespace("Global",None)
sm = symbolTable.getSymbol(symbol,ns)
if sm != None:
sym_addr = sm.getAddress().toString()
b = struct.pack("<Q",int(sym_addr,16))
mem.setBytes(addr,b,0,8)
else:
target = image_base.add(offset)
b = struct.pack("<Q",int(target.toString(),16))
mem.setBytes(addr,b,0,8)
listing.clearCodeUnits(addr,addr.add(8),False)
listing.createData(addr,PointerDataType())
skip = nxt * 4
addr = addr.add(skip)
off+=skip
if skip == 0:
break
print "OK"
if __name__ == "__main__":
depac()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment