mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
302 lines
8.1 KiB
Go
302 lines
8.1 KiB
Go
package cpp
|
|
|
|
func matchSeedLoadX1(start uint64, data []byte, offset int, refs uint64Set) (int, uint64, bool) {
|
|
if offset+8 > len(data) {
|
|
return 0, 0, false
|
|
}
|
|
pc := start + uint64(offset)
|
|
raw0 := readUint32At(data, offset)
|
|
raw1 := readUint32At(data, offset+4)
|
|
|
|
if (raw0&0x9f00001f) == 0x10000001 && isArm64Nop(raw1) {
|
|
immhi := int64((raw0 >> 5) & 0x7ffff)
|
|
immlo := int64((raw0 >> 29) & 0x3)
|
|
offsetImm := (immhi << 2) | immlo
|
|
if offsetImm&(1<<20) != 0 {
|
|
offsetImm |= ^int64((1 << 21) - 1)
|
|
}
|
|
addr := uint64(int64(pc) + offsetImm)
|
|
if hasUint64Set(refs, addr) {
|
|
return offset + 8, addr, true
|
|
}
|
|
}
|
|
|
|
if (raw0 & 0x9f000000) == 0x90000000 {
|
|
adrpRd := int(raw0 & 0x1f)
|
|
isAdd := (raw1 & 0x1f800000) == 0x11000000
|
|
addRn := int((raw1 >> 5) & 0x1f)
|
|
addRd := int(raw1 & 0x1f)
|
|
if isAdd && adrpRd == addRn && addRd == 1 {
|
|
page, ok := decodeADRPImmediate(pc, raw0)
|
|
if ok {
|
|
addImm := uint64((raw1 >> 10) & 0xfff)
|
|
if (raw1>>22)&1 == 1 {
|
|
addImm <<= 12
|
|
}
|
|
addr := page + addImm
|
|
if hasUint64Set(refs, addr) {
|
|
return offset + 8, addr, true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0, 0, false
|
|
}
|
|
|
|
func isBranchRegisterRaw(raw uint32) bool {
|
|
return (raw&0xfffffc1f) == 0xd61f0000 ||
|
|
(raw&0xfffff800) == 0xd71f0800 ||
|
|
(raw&0xfffff800) == 0xd63f0800
|
|
}
|
|
|
|
func isConditionalBranchRaw(raw uint32) bool {
|
|
switch {
|
|
case (raw & 0xff000010) == 0x54000000: // b.cond
|
|
return true
|
|
case (raw & 0x7e000000) == 0x34000000: // cbz/cbnz
|
|
return true
|
|
case (raw & 0x7e000000) == 0x36000000: // tbz/tbnz
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func invalidatesX1Raw(raw uint32) bool {
|
|
switch {
|
|
case (raw & 0x9f000000) == 0x90000000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0x9f000000) == 0x10000000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0xff000000) == 0x91000000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0xff000000) == 0xd1000000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0xff200000) == 0xaa000000:
|
|
rd := int(raw & 0x1f)
|
|
rn := int((raw >> 5) & 0x1f)
|
|
rm := int((raw >> 16) & 0x1f)
|
|
imm6 := (raw >> 10) & 0x3f
|
|
if rd != 1 {
|
|
return false
|
|
}
|
|
return !((rn == 31 && imm6 == 0 && rm == 1) || (rm == 31 && imm6 == 0 && rn == 1))
|
|
case (raw & 0x7f800000) == 0x52800000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0x7f800000) == 0x72800000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0xffc00000) == 0xf9400000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0xffc00000) == 0xb9400000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0xffe00c00) == 0xf8400000:
|
|
return int(raw&0x1f) == 1
|
|
case (raw & 0x7fc00000) == 0xa9400000:
|
|
return int(raw&0x1f) == 1 || int((raw>>10)&0x1f) == 1
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isPassThroughSafeRaw(raw uint32) bool {
|
|
switch {
|
|
case isArm64Nop(raw), isPacibsp(raw), isReturnInstruction(raw):
|
|
return true
|
|
case (raw & 0xfc4003e0) == 0xa80003e0: // stp Xt1, Xt2, [sp, ...]
|
|
return true
|
|
case (raw & 0x7fc003ff) == 0xa9407bfd: // ldp x29, x30, [sp, ...]
|
|
return true
|
|
case (raw & 0xff0003ff) == 0x910003fd, // add x29, sp, #imm
|
|
(raw & 0xff0003ff) == 0x910003ff, // add sp, sp, #imm
|
|
(raw & 0xff0003ff) == 0xd10003ff: // sub sp, sp, #imm
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isRawTrackable(raw uint32) bool {
|
|
switch {
|
|
case (raw & 0x9f000000) == 0x90000000,
|
|
(raw & 0x9f000000) == 0x10000000,
|
|
(raw & 0xff000000) == 0x91000000,
|
|
(raw & 0xff000000) == 0xd1000000,
|
|
(raw & 0xff200000) == 0xaa000000,
|
|
(raw & 0x7f800000) == 0x52800000,
|
|
(raw & 0x7f800000) == 0x72800000,
|
|
(raw & 0xffc00000) == 0xf9400000,
|
|
(raw & 0xffc00000) == 0xb9400000,
|
|
(raw & 0xffe00c00) == 0xf8400000,
|
|
(raw & 0x7fc00000) == 0xa9400000:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func seedLoadStillInX1(data []byte, nextOff int, callOff int) bool {
|
|
for off := nextOff; off < callOff && off+4 <= len(data); off += 4 {
|
|
raw := readUint32At(data, off)
|
|
if isPassThroughSafeRaw(raw) {
|
|
continue
|
|
}
|
|
if invalidatesX1Raw(raw) {
|
|
return false
|
|
}
|
|
if !isRawTrackable(raw) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func collectConstructorTargetsForStringRefs(start uint64, data []byte, refs uint64Set, prev uint64Set) uint64Set {
|
|
out := make(uint64Set)
|
|
for off := 0; off+8 <= len(data); off += 4 {
|
|
nextOff, _, ok := matchSeedLoadX1(start, data, off, refs)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for scan := nextOff; scan+4 <= len(data) && scan <= nextOff+64*4; scan += 4 {
|
|
pc := start + uint64(scan)
|
|
raw := readUint32At(data, scan)
|
|
if target, ok := decodeBLTarget(pc, raw); ok {
|
|
if (prev == nil || hasUint64Set(prev, target)) && seedLoadStillInX1(data, nextOff, scan) {
|
|
out[target] = struct{}{}
|
|
}
|
|
break
|
|
}
|
|
if invalidatesX1Raw(raw) || isReturnInstruction(raw) || isConditionalBranchRaw(raw) || isBranchRegisterRaw(raw) || isCallRegisterRaw(raw) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func importStubReferenceTarget(start uint64, data []byte, refSlots uint64Set) (uint64, bool) {
|
|
if len(data) < 12 {
|
|
return 0, false
|
|
}
|
|
raw0 := readUint32At(data, 0)
|
|
if (raw0 & 0x9f000000) != 0x90000000 {
|
|
return 0, false
|
|
}
|
|
page, ok := decodeADRPImmediate(start, raw0)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
|
|
raw1 := readUint32At(data, 4)
|
|
raw2 := readUint32At(data, 8)
|
|
rd := int(raw0 & 0x1f)
|
|
|
|
if (raw1 & 0xffc00000) == 0xf9400000 {
|
|
rt := int(raw1 & 0x1f)
|
|
rn := int((raw1 >> 5) & 0x1f)
|
|
imm12 := uint64((raw1>>10)&0xfff) * 8
|
|
if rn == rd && branchRegisterUses(raw2, rt) {
|
|
target := page + imm12
|
|
return target, hasUint64Set(refSlots, target)
|
|
}
|
|
}
|
|
|
|
if len(data) >= 16 {
|
|
raw3 := readUint32At(data, 12)
|
|
if (raw1&0x1f800000) == 0x11000000 && (raw2&0xffc00000) == 0xf9400000 {
|
|
addRn := int((raw1 >> 5) & 0x1f)
|
|
addRd := int(raw1 & 0x1f)
|
|
ldrRn := int((raw2 >> 5) & 0x1f)
|
|
ldrRt := int(raw2 & 0x1f)
|
|
ldrImm12 := uint64((raw2 >> 10) & 0xfff)
|
|
if addRn == rd && ldrRn == addRd && ldrImm12 == 0 && branchRegisterUses(raw3, ldrRt) {
|
|
addImm := uint64((raw1 >> 10) & 0xfff)
|
|
if (raw1>>22)&1 == 1 {
|
|
addImm <<= 12
|
|
}
|
|
target := page + addImm
|
|
return target, hasUint64Set(refSlots, target)
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0, false
|
|
}
|
|
|
|
func branchRegisterUses(raw uint32, reg int) bool {
|
|
if reg < 0 || reg > 31 {
|
|
return false
|
|
}
|
|
rawReg := int((raw >> 5) & 0x1f)
|
|
return rawReg == reg && ((raw&0xfffffc1f) == 0xd61f0000 ||
|
|
(raw&0xfffff800) == 0xd71f0800)
|
|
}
|
|
|
|
func findPassThroughConstructorTarget(start uint64, data []byte, targets uint64Set) (uint64, bool) {
|
|
preserved := [7]bool{true, true, true, true, true, true, true}
|
|
branches := 0
|
|
for off := 0; off+4 <= len(data) && off <= 64*4; off += 4 {
|
|
pc := start + uint64(off)
|
|
raw := readUint32At(data, off)
|
|
if target, ok := decodeBLTarget(pc, raw); ok {
|
|
branches++
|
|
if hasUint64Set(targets, target) && branches == 1 &&
|
|
preserved[0] && preserved[1] && preserved[2] && preserved[3] &&
|
|
preserved[4] && preserved[5] && preserved[6] {
|
|
return target, true
|
|
}
|
|
return 0, false
|
|
}
|
|
if isReturnInstruction(raw) || isConditionalBranchRaw(raw) || isBranchRegisterRaw(raw) || isCallRegisterRaw(raw) {
|
|
return 0, false
|
|
}
|
|
if isPassThroughSafeRaw(raw) {
|
|
continue
|
|
}
|
|
if !isRawTrackable(raw) {
|
|
return 0, false
|
|
}
|
|
invalidatePassThroughRegs(raw, &preserved)
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func invalidatePassThroughRegs(raw uint32, x *[7]bool) {
|
|
mark := func(reg int, preserved bool) {
|
|
if reg >= 0 && reg < len(x) && !preserved {
|
|
x[reg] = false
|
|
}
|
|
}
|
|
switch {
|
|
case (raw & 0x9f000000) == 0x90000000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0x9f000000) == 0x10000000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0xff000000) == 0x91000000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0xff000000) == 0xd1000000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0xff200000) == 0xaa000000:
|
|
rd := int(raw & 0x1f)
|
|
rn := int((raw >> 5) & 0x1f)
|
|
rm := int((raw >> 16) & 0x1f)
|
|
imm6 := (raw >> 10) & 0x3f
|
|
preserved := (rn == 31 && imm6 == 0 && rm == rd) || (rm == 31 && imm6 == 0 && rn == rd)
|
|
mark(rd, preserved)
|
|
case (raw & 0x7f800000) == 0x52800000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0x7f800000) == 0x72800000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0xffc00000) == 0xf9400000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0xffc00000) == 0xb9400000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0xffe00c00) == 0xf8400000:
|
|
mark(int(raw&0x1f), false)
|
|
case (raw & 0x7fc00000) == 0xa9400000:
|
|
mark(int(raw&0x1f), false)
|
|
mark(int((raw>>10)&0x1f), false)
|
|
}
|
|
}
|