X64: Generalize Codegen initializer search (part of #21)

This commit is contained in:
Katy Coe
2020-02-15 05:19:41 +01:00
parent 6ec5c06fbc
commit 0a99f549ff

View File

@@ -67,21 +67,32 @@ namespace Il2CppInspector
// mov rbp, rsp is a 3-byte instruction encoded as 0x48 0x89 0xE5
private bool isPrologue(byte[] buff, int offset) => buff[offset] == 0x55 && buff[offset + 1] == 0x48 && buff[offset + 2] == 0x89 && buff[offset + 3] == 0xE5;
// 0b0100_0X0Y to set 64-bit mode, 0x33 for XOR, 0b11_XXX_YYY for register numbers
// Volume 2C, page 5-278
// 0b0100_0X0Y to set 64-bit mode, 0x31 or 0x33 for XOR, 0b11_XXX_YYY for register numbers
// Volume 2C, page 5-612
private (int reg_op1, int reg_op2)? getXorR64R64(byte[] buff, int offset) {
if ((buff[offset] & 0b1111_1010) != 0b_0100_0000 || buff[offset + 1] != 0x33 || (buff[offset + 2] & 0b1100_0000) != 0b1100_0000)
if ((buff[offset] & 0b1111_1010) != 0b_0100_0000 ||
(buff[offset + 1] != 0x31 && buff[offset + 1] != 0x33) ||
(buff[offset + 2] & 0b1100_0000) != 0b1100_0000)
return null;
return (((buff[offset] & 0b0000_0100) << 1) + ((buff[offset + 2] & 0b0011_1000) >> 3),
((buff[offset] & 0b0000_0001) << 3) + (buff[offset + 2] & 0b0000_0111));
}
// 0x31 or 0x33 for XOR, 0b11_XXX_YYY for register numbers
// Volume 2C, page 5-612
private (int reg_op1, int reg_op2)? getXorR32R32(byte[] buff, int offset) {
if ((buff[offset] != 0x31 && buff[offset] != 0x33) || (buff[offset + 1] & 0b1100_0000) != 0b1100_0000)
return null;
return ((buff[offset + 1] & 0b0011_1000) >> 3, buff[offset + 1] & 0b0000_0111);
}
protected override (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc) {
// Setup
var buffSize = 0x66; // minimum number of bytes to process the longest expected function
var leaSize = 7; // the length of an LEA instruction with a 64-bit register operand and a 32-bit memory operand
var xorSize = 3; // the length of a XOR instruction of two 64-bit registers
var xor64Size = 3; // the length of a XOR instruction of two 64-bit registers
var xor32Size = 2; // the length of a XOR instruction of two 32-bit registers
var pushSize = 2; // the length of a PUSH instruction with a 64-bit register
int RAX = 0, RBX = 3, RCX = 1, RDX = 2, R8 = 8;
@@ -96,22 +107,51 @@ namespace Il2CppInspector
// 2. Inlined version with il2cpp::utils::RegisterRuntimeInitializeAndCleanup(CallbackFunction, CallbackFunction, order)
// Version 1 passes "this" in rcx and the arguments in rdx (our wanted pointer), r8d (always zero) and r9d (always zero)
// or "this" in rdi, and the arguments in rsi (our wanted pointer), edx (always zero) and ecx (always zero)
// Version 2 has a standard prologue and loads the wanted pointer into rax (lea rax)
(int reg, uint operand)? lea;
// Check for regular version
var xor = getXorR64R64(buff, 0);
if (xor != null && xor.Value.reg_op1 == xor.Value.reg_op2) {
lea = getLea(buff, xorSize);
// Generalize it as follows:
// - each instruction must be lea r64, imm32 or xor r32, r32
// - xors must always have the same register for both operands
// - lea that can't be mapped into the file is the pointer to 'this', otherwise it's the pointer to the init function
// - the last instruction should always be jmp (not currently enforced)
// - function length should not be longer than 5 instructions (two leas, two xors and one jmp)
if (lea != null) {
xor = getXorR64R64(buff, xorSize + leaSize);
if (xor != null && xor.Value.reg_op1 == xor.Value.reg_op2) {
// We found Il2CppCodegenRegistration(void)
pCgr = image.GlobalOffset + loc + (ulong) (xorSize + leaSize) + lea.Value.operand;
for (int offset = 0, instructions = 0; instructions < 4; instructions++) {
// All allowed instruction types
var xor32 = getXorR32R32(buff, offset);
var xor64 = getXorR64R64(buff, offset);
lea = getLea(buff, offset);
if (xor32 != null && xor32.Value.reg_op1 == xor32.Value.reg_op2) {
offset += xor32Size;
}
else if (xor64 != null && xor64.Value.reg_op1 == xor64.Value.reg_op2) {
offset += xor64Size;
}
else if (lea != null) {
offset += leaSize;
if (pCgr == 0) {
try {
// We may have found Il2CppCodegenRegistration(void)
pCgr = image.GlobalOffset + loc + (ulong) offset + lea.Value.operand;
var newLoc = image.MapVATR(pCgr);
}
catch (InvalidOperationException) {
// this pointer
pCgr = 0;
}
}
}
else {
// not lea or xor
pCgr = 0;
break;
}
}
// Check for inlined version