Files
filza27.2/XPF/external/ChOma/src/PatchFinder_arm64.c
T

341 lines
12 KiB
C

#include "PatchFinder_arm64.h"
#include "PatchFinder.h"
#include "arm64.h"
#include <sys/mman.h>
// Unified check for whether anything writes to a register between firstAddr and secondAddr
bool pfsec_arm64_scan_register_write(PFSection *section, arm64_register reg, uint64_t endAddr, uint64_t startAddr)
{
if (startAddr == endAddr) {
return false;
}
else if (startAddr > endAddr) {
printf("Fatal error pfsec_arm64_scan_register_write: startAddr > endAddr\n");
return false;
}
uint16_t regNum = ARM64_REG_GET_NUM(reg);
for (uint64_t curAddr = (endAddr - sizeof(uint32_t)); curAddr > startAddr; curAddr -= sizeof(uint32_t)) {
uint32_t inst = pfsec_read32(section, curAddr);
arm64_register destinationReg, sourceReg;
// Check for immediate add writing to reg
if (arm64_dec_add_imm(inst, &destinationReg, &sourceReg, NULL) == 0) goto found_inst_match;
// Check for immediate mov writing to reg
else if (arm64_dec_mov_imm(inst, &destinationReg, NULL, NULL, NULL) == 0) goto found_inst_match;
// Check for register mov writing to reg
else if (arm64_dec_mov_reg(inst, &destinationReg, &sourceReg) == 0) goto found_inst_match;
// Check for immediate ldr writing to reg
else if (arm64_dec_ldr_imm(inst, &destinationReg, NULL, NULL, NULL, NULL) == 0) goto found_inst_match;
// Check for literal ldr writing to reg
else if (arm64_dec_ldr_lit(inst, curAddr, NULL, &destinationReg) == 0) goto found_inst_match;
// Check for immediate ldrs writing to reg
else if (arm64_dec_ldrs_imm(inst, &destinationReg, NULL, NULL, NULL, NULL) == 0) goto found_inst_match;
// Nothing matched, continue
else continue;
found_inst_match:
if (ARM64_REG_GET_NUM(destinationReg) == regNum) {
goto found_write;
}
// Didn't write to our register, continue
continue;
}
return false;
found_write:
return true;
}
uint64_t pfsec_arm64_resolve_adrp_ldr_str_add_reference(PFSection *section, uint64_t adrpAddr, uint64_t ldrStrAddAddr)
{
uint32_t ldrStrAddInst = pfsec_read32(section, ldrStrAddAddr);
uint64_t imm = 0;
if (arm64_dec_ldr_imm(ldrStrAddInst, NULL, NULL, &imm, NULL, NULL) != 0) {
if (arm64_dec_ldrs_imm(ldrStrAddInst, NULL, NULL, &imm, NULL, NULL) != 0) {
if (arm64_dec_str_imm(ldrStrAddInst, NULL, NULL, &imm, NULL, NULL) != 0) {
uint16_t addImm = 0;
if (arm64_dec_add_imm(ldrStrAddInst, NULL, NULL, &addImm) == 0) {
imm = (uint64_t)addImm;
}
else {
return 0;
}
}
}
}
uint64_t adrpTarget = 0;
arm64_dec_adr_p(pfsec_read32(section, adrpAddr), adrpAddr, &adrpTarget, NULL, NULL);
return adrpTarget + imm;
}
uint64_t pfsec_arm64_resolve_adrp_ldr_str_add_reference_auto(PFSection *section, uint64_t ldrStrAddAddr)
{
uint32_t inst = pfsec_read32(section, ldrStrAddAddr);
bool isAdd = false;
arm64_register reg;
if (arm64_dec_ldr_imm(inst, NULL, &reg, NULL, NULL, NULL) != 0) {
if (arm64_dec_str_imm(inst, NULL, &reg, NULL, NULL, NULL) != 0) {
if (arm64_dec_add_imm(inst, NULL, &reg, NULL) != 0) {
return 0;
}
else {
isAdd = true;
}
}
}
uint32_t adrpInst = 0, adrpInstMask = 0;
arm64_gen_adr_p(OPT_BOOL_NONE, OPT_UINT64_NONE, OPT_UINT64_NONE, reg, &adrpInst, &adrpInstMask);
uint64_t adrpAddr = pfsec_find_prev_inst(section, ldrStrAddAddr, 100, adrpInst, adrpInstMask);
if (!adrpAddr) return 0;
if (!isAdd) {
// For str and ldr, there can be the following sequence:
// adr(p) xA, <page> (adrpAddr)
// (0..n) instructions
// add xA, xA, <page_off> (specialCaseAddAddr)
// (0..n) instructions
// ldr/str xB, [xA] (ldrStrAddAddr)
// We search for the middle add instruction and if we find it, we need to handle this case
uint32_t searchCount = ((ldrStrAddAddr - adrpAddr) / 4) - 1;
if (searchCount) {
uint32_t specialCaseAddInst = 0, specialCaseAddMask = 0;
arm64_gen_add_imm(reg, reg, OPT_UINT64_NONE, &specialCaseAddInst, &specialCaseAddMask);
uint64_t specialCaseAddAddr = pfsec_find_prev_inst(section, ldrStrAddAddr, searchCount, specialCaseAddInst, specialCaseAddMask);
if (specialCaseAddAddr) {
// In this case, we need to make sure there are no writes in the two "(0..n) instructions" sections
if (pfsec_arm64_scan_register_write(section, reg, ldrStrAddAddr, specialCaseAddAddr)) return 0;
if (pfsec_arm64_scan_register_write(section, reg, specialCaseAddAddr, adrpAddr)) return 0;
uint64_t baseAddr = pfsec_arm64_resolve_adrp_ldr_str_add_reference(section, adrpAddr, specialCaseAddAddr);
if (!baseAddr) return 0;
uint32_t ldrStrAddInst = pfsec_read32(section, ldrStrAddAddr);
uint64_t imm64 = 0;
uint16_t imm16 = 0;
if (arm64_dec_add_imm(ldrStrAddInst, NULL, NULL, &imm16) != 0) {
if (arm64_dec_ldr_imm(ldrStrAddInst, NULL, NULL, &imm64, NULL, NULL) != 0) {
if (arm64_dec_str_imm(ldrStrAddInst, NULL, NULL, &imm64, NULL, NULL) != 0) {
fprintf(stderr, "Warning: failed decoding ldrStrAddInst (%#x @ %#llx)\n", ldrStrAddInst, ldrStrAddAddr);
}
}
}
else {
imm64 = imm16;
}
return baseAddr + imm64;
}
}
}
if (pfsec_arm64_scan_register_write(section, reg, ldrStrAddAddr, adrpAddr)) return 0;
return pfsec_arm64_resolve_adrp_ldr_str_add_reference(section, adrpAddr, ldrStrAddAddr);
}
uint64_t pfsec_arm64_resolve_stub(PFSection *section, uint64_t stubAddr)
{
// A stub is usually:
// adrp x16, ?
// ldr x16, ?
// br x16
// First, check if what we have actually is a stub
uint32_t inst[3];
pfsec_read_at_address(section, stubAddr, inst, sizeof(inst));
uint32_t stubInst[3], stubMask[3];
arm64_gen_adr_p(OPT_BOOL(true), OPT_UINT64_NONE, OPT_UINT64_NONE, ARM64_REG_X(16), &stubInst[0], &stubMask[0]);
arm64_gen_ldr_imm(0, LDR_STR_TYPE_UNSIGNED, ARM64_REG_X(16), ARM64_REG_X(16), OPT_UINT64_NONE, &stubInst[1], &stubMask[1]);
stubInst[2] = 0xd61f0200;
stubMask[2] = 0xffffffff;
if ((inst[0] & stubMask[0]) == stubInst[0] ||
(inst[1] & stubMask[1]) == stubInst[1] ||
(inst[2] & stubMask[2]) == stubInst[2]) {
// This is a stub, resolve it
uint64_t ptrAddr = pfsec_arm64_resolve_adrp_ldr_str_add_reference(section, stubAddr, stubAddr + 4);
if (ptrAddr) {
uint64_t targetAddr = 0;
MachO *macho = pfsec_get_macho(section);
if (macho) {
macho_read_at_vmaddr(macho, ptrAddr, sizeof(targetAddr), &targetAddr);
}
else {
DyldSharedCache *sharedCache = pfsec_get_dsc(section);
if (sharedCache) {
dsc_read_from_vmaddr(sharedCache, ptrAddr, sizeof(targetAddr), &targetAddr);
}
}
return targetAddr;
}
}
// Not a stub, just return original address
return stubAddr;
}
void pfsec_arm64_enumerate_xrefs(PFSection *section, Arm64XrefTypeMask types, void (^xrefBlock)(Arm64XrefType type, uint64_t source, uint64_t target, bool *stop))
{
bool stop = false;
if (section->info.initprot & PROT_EXEC) {
for (uint64_t addr = section->info.vmaddr; addr < (section->info.vmaddr + section->info.size) && !stop; addr += sizeof(uint32_t)) {
uint32_t inst = pfsec_read32(section, addr);
if ((types & ARM64_XREF_TYPE_MASK_B) || (types & ARM64_XREF_TYPE_MASK_BL)) {
uint64_t target = 0;
bool isBl = 0;
if (arm64_dec_b_l(inst, addr, &target, &isBl) == 0) {
if (isBl && (types & ARM64_XREF_TYPE_MASK_BL)) {
xrefBlock(ARM64_XREF_TYPE_BL, addr, target, &stop);
}
else if (!isBl && (types & ARM64_XREF_TYPE_MASK_B)) {
xrefBlock(ARM64_XREF_TYPE_B, addr, target, &stop);
}
continue;
}
}
if (types & ARM64_XREF_TYPE_MASK_ADR) {
uint64_t target = 0;
bool isAdrp = false;
arm64_register reg;
if (arm64_dec_adr_p(inst, addr, &target, &reg, &isAdrp) == 0) {
if (!isAdrp) {
xrefBlock(ARM64_XREF_TYPE_ADR, addr, target, &stop);
}
continue;
}
}
if ((types & ARM64_XREF_TYPE_MASK_B_COND) || (types & ARM64_XREF_TYPE_MASK_BC_COND)) {
uint64_t target = 0;
bool isBc = false;
if (arm64_dec_b_c_cond(inst, addr, &target, NULL, &isBc) == 0) {
if (!isBc && (types & ARM64_XREF_TYPE_MASK_B_COND)) {
xrefBlock(ARM64_XREF_TYPE_B_COND, addr, target, &stop);
}
if (isBc && (types & ARM64_XREF_TYPE_MASK_BC_COND)) {
xrefBlock(ARM64_XREF_TYPE_BC_COND, addr, target, &stop);
}
}
}
if ((types & ARM64_XREF_TYPE_MASK_CBZ) || (types & ARM64_XREF_TYPE_MASK_CBNZ)) {
uint64_t target = 0;
bool isCbnz = false;
if (arm64_dec_cb_n_z(inst, addr, &isCbnz, NULL, &target) == 0) {
if (!isCbnz && (types & ARM64_XREF_TYPE_MASK_CBZ)) {
xrefBlock(ARM64_XREF_TYPE_CBZ, addr, target, &stop);
}
if (isCbnz && (types & ARM64_XREF_TYPE_MASK_CBNZ)) {
xrefBlock(ARM64_XREF_TYPE_CBNZ, addr, target, &stop);
}
}
}
if ((types & ARM64_XREF_TYPE_MASK_TBZ) || (types & ARM64_XREF_TYPE_MASK_TBNZ)) {
uint64_t target = 0;
bool isTbnz = false;
if (arm64_dec_tb_n_z(inst, addr, &isTbnz, NULL, &target, NULL) == 0) {
if (!isTbnz && (types & ARM64_XREF_TYPE_MASK_TBZ)) {
xrefBlock(ARM64_XREF_TYPE_CBZ, addr, target, &stop);
}
if (isTbnz && (types & ARM64_XREF_TYPE_MASK_TBNZ)) {
xrefBlock(ARM64_XREF_TYPE_CBNZ, addr, target, &stop);
}
}
}
#define ADRP_SEEK_BACK 8
if (types & ARM64_XREF_TYPE_MASK_ADRP_ADD) {
uint16_t addImm = 0;
arm64_register addDestinationReg;
arm64_register addSourceReg;
if (arm64_dec_add_imm(inst, &addDestinationReg, &addSourceReg, &addImm) == 0) {
uint32_t adrpInst = 0;
uint32_t adrpMask = 0;
if (arm64_gen_adr_p(OPT_BOOL(true), OPT_UINT64_NONE, OPT_UINT64_NONE, addSourceReg, &adrpInst, &adrpMask) == 0) {
uint64_t adrpAddr = pfsec_find_prev_inst(section, addr, ADRP_SEEK_BACK, adrpInst, adrpMask);
if (adrpAddr != 0) {
if (!pfsec_arm64_scan_register_write(section, addSourceReg, addr, adrpAddr)) {
uint64_t adrpTarget = 0;
arm64_dec_adr_p(pfsec_read32(section, adrpAddr), adrpAddr, &adrpTarget, NULL, NULL);
xrefBlock(ARM64_XREF_TYPE_ADRP_ADD, addr, adrpTarget + addImm, &stop);
}
}
}
}
}
if (types & ARM64_XREF_TYPE_MASK_ADRP_LDR) {
arm64_register ldrDestinationReg;
arm64_register ldrSourceReg;
uint64_t ldrImm = 0;
char ldrType = -1;
arm64_ldr_str_type instType = 0;
if (arm64_dec_ldr_imm(inst, &ldrDestinationReg, &ldrSourceReg, &ldrImm, &ldrType, &instType) == 0) {
uint32_t adrpInst = 0;
uint32_t adrpMask = 0;
if (arm64_gen_adr_p(OPT_BOOL(true), OPT_UINT64_NONE, OPT_UINT64_NONE, ldrSourceReg, &adrpInst, &adrpMask) == 0) {
uint64_t adrpAddr = pfsec_find_prev_inst(section, addr, ADRP_SEEK_BACK, adrpInst, adrpMask);
if (adrpAddr != 0) {
if (!pfsec_arm64_scan_register_write(section, ldrSourceReg, addr, adrpAddr)) {
uint64_t adrpTarget = 0;
arm64_dec_adr_p(pfsec_read32(section, adrpAddr), adrpAddr, &adrpTarget, NULL, NULL);
xrefBlock(ARM64_XREF_TYPE_ADRP_LDR, addr, adrpTarget + ldrImm, &stop);
}
}
}
}
}
if (types & ARM64_XREF_TYPE_MASK_ADRP_STR) {
arm64_register strDestinationReg;
arm64_register strSourceReg;
uint64_t strImm = 0;
char strType = -1;
arm64_ldr_str_type instType = 0;
if (arm64_dec_str_imm(inst, &strDestinationReg, &strSourceReg, &strImm, &strType, &instType) == 0) {
uint32_t adrpInst = 0;
uint32_t adrpMask = 0;
if (arm64_gen_adr_p(OPT_BOOL(true), OPT_UINT64_NONE, OPT_UINT64_NONE, strSourceReg, &adrpInst, &adrpMask) == 0) {
uint64_t adrpAddr = pfsec_find_prev_inst(section, addr, ADRP_SEEK_BACK, adrpInst, adrpMask);
if (adrpAddr != 0) {
if (!pfsec_arm64_scan_register_write(section, strSourceReg, addr, adrpAddr)) {
uint64_t adrpTarget = 0;
arm64_dec_adr_p(pfsec_read32(section, adrpAddr), adrpAddr, &adrpTarget, NULL, NULL);
xrefBlock(ARM64_XREF_TYPE_ADRP_STR, addr, adrpTarget + strImm, &stop);
}
}
}
}
}
}
}
if ((types & ARM64_XREF_TYPE_MASK_POINTER)) {
if (!strncmp(section->info.sectname, "__cfstring", sizeof(section->info.sectname))) {
for (uint64_t addr = section->info.vmaddr; addr < (section->info.vmaddr + section->info.size) && !stop; addr += 0x20) {
uint32_t fileoff = pfsec_read32(section, addr + 0x10);
uint64_t vmaddr = 0;
MachO *macho = pfsec_get_macho(section);
if (macho) {
macho_translate_fileoff_to_vmaddr(macho, fileoff, &vmaddr, NULL);
xrefBlock(ARM64_XREF_TYPE_POINTER, addr, vmaddr, &stop);
}
}
}
else {
for (uint64_t addr = section->info.vmaddr; addr < (section->info.vmaddr + section->info.size) && !stop; addr += sizeof(uint64_t)) {
uint64_t ptr = pfsec_read_pointer(section, addr);
xrefBlock(ARM64_XREF_TYPE_POINTER, addr, ptr, &stop);
}
}
}
}