天天看點

SandHook 之 Native Inline Hook簡介支援特點代碼結尾

簡介

在 SandHook ART Hook 穩定之後,抽空把 Native Inline Hook 實作了,雖然有重複造輪子的嫌疑,但确實是本人一行一行碼出來的,基本所有的東西都是自己實作的。

Github: https://github.com/ganyao114/SandHook

支援

目前支援

  • ARM32
  • ARM64

其實 X86 非常好實作,但是想了一下還是往後稍稍吧,等 ARM 穩定了再說

特點

  • 純手寫的反彙編器和彙編器

沒有用 vixl 或者其他庫,也不是那種一堆 bit 操作難以閱讀那種,可讀性很強。

當然我也僅僅實作了部分常見指令。

  • 指令修複考慮到了更多的 Case

比如當你重複多次 Hook 同一個函數時,SandHook 可以比較好的支援

  • 純手寫的 ELF 解析

你可以搜尋到比 dlysm 更多的符号,你可以直接這樣 hook

  • 除了 Hook 之外

你也可以用其中的彙編器和解析器做一些其他事情,當然也可以很友善的擴充支援更多的指令。

代碼

Hook 部分

#include "hook_arm64.h"
#include "code_buffer.h"
#include "lock.h"

using namespace SandHook::Hook;
using namespace SandHook::Decoder;
using namespace SandHook::Asm;
using namespace SandHook::Assembler;
using namespace SandHook::Utils;

#include "assembler_arm64.h"
#include "code_relocate_arm64.h"
using namespace SandHook::RegistersA64;
void *InlineHookArm64Android::inlineHook(void *origin, void *replace) {
    AutoLock lock(hookLock);

    void* backup = nullptr;
    AssemblerA64 assemblerBackup(backupBuffer);

    StaticCodeBuffer inlineBuffer = StaticCodeBuffer(reinterpret_cast<Addr>(origin));
    AssemblerA64 assemblerInline(&inlineBuffer);
    CodeContainer* codeContainerInline = &assemblerInline.codeContainer;

    //build inline trampoline
#define __ assemblerInline.
    Label* target_addr_label = new Label();
    __ Ldr(IP1, target_addr_label);
    __ Br(IP1);
    __ Emit(target_addr_label);
    __ Emit((Addr) replace);
#undef __

    //build backup method
    CodeRelocateA64 relocate = CodeRelocateA64(assemblerBackup);
    backup = relocate.relocate(origin, codeContainerInline->size(), nullptr);
#define __ assemblerBackup.
    Label* origin_addr_label = new Label();
    __ Ldr(IP1, origin_addr_label);
    __ Br(IP1);
    __ Emit(origin_addr_label);
    __ Emit((Addr) origin + codeContainerInline->size());
    __ finish();
#undef __

    //commit inline trampoline
    assemblerInline.finish();
    return backup;
}
           

指令解析與彙編

首先是描述一個指令的 bit 結構,基本可以對照 ARM 手冊

DEFINE_OPCODE_T32(LDR_LIT, 0b1111100)
DEFINE_STRUCT_T32(LDR_LIT) {
    InstT32 op:7;
    InstT32 U:1;
    InstT32 S:1;
    InstT32 opcode:7;
    InstT32 imm12:12;
    InstT32 rt:T32_REG_WIDE;
};
           

指令的解析與彙編

void T32_LDR_LIT::decode(T32_STRUCT_LDR_LIT *inst) {
    DECODE_OP;
    DECODE_RT(Reg);
    s = S(inst->S);
    offset = getImmPCOffset();
}

void T32_LDR_LIT::assembler() {
    SET_OPCODE(LDR_LIT);
    ENCODE_OP;
    ENCODE_RT;
    get()->S = s;
    if (offset >= 0) {
        get()->U = add;
        get()->imm12 = static_cast<InstT32>(offset);
    } else {
        get()->U = cmp;
        get()->imm12 = static_cast<InstT32>(-offset);
    }
}
           

彙編器:基本是對每個指令封裝的調用

void AssemblerA32::Mov(RegisterA32 &rd, U16 imm16) {
    Emit(reinterpret_cast<Unit<Base>*>(new INST_T32(MOV_MOVT_IMM)(INST_T32(MOV_MOVT_IMM)::MOV, rd, imm16)));
}

void AssemblerA32::Movt(RegisterA32 &rd, U16 imm16) {
    Emit(reinterpret_cast<Unit<Base>*>(new INST_T32(MOV_MOVT_IMM)(INST_T32(MOV_MOVT_IMM)::MOVT, rd, imm16)));
}

void AssemblerA32::Mov(RegisterA32 &rd, U32 imm32) {
    U16 immL = BITS16L(imm32);
    U16 immH = BITS16H(imm32);
    Mov(rd, immL);
    Movt(rd, immH);
}
           

解析器:

void Arm64Decoder::decode(void *codeStart, Addr codeLen, InstVisitor &visitor, bool onlyPcRelInst) {
    InstA64 *pc = reinterpret_cast<InstA64 *>(codeStart);
    Addr endAddr = (Addr) codeStart + codeLen;
    Unit<Base>* unit = nullptr;
    while((Addr) pc < endAddr) {
        // pc relate insts
        CASE(B_BL)
        CASE(B_COND)
        CASE(CBZ_CBNZ)
        CASE(TBZ_TBNZ)
        CASE(LDR_LIT)
        CASE(ADR_ADRP)
        if (onlyPcRelInst)
            goto label_matched;
        CASE(MOV_WIDE)
        CASE(MOV_REG)
        CASE(LDR_IMM)
        CASE(LDR_UIMM)
        CASE(LDRSW_IMM)
        CASE(LDRSW_UIMM)
        CASE(STR_UIMM)
        CASE(STR_IMM)
        CASE(BR_BLR_RET)
        CASE(SUB_EXT_REG)
        CASE(SVC)
        CASE(EXCEPTION_GEN)
        label_matched:
        if (unit == nullptr) {
            unit = reinterpret_cast<Unit<Base> *>(new INST_A64(UNKNOW)(*reinterpret_cast<STRUCT_A64(UNKNOW) *>(pc)));
        }
        if (!visitor.visit(unit, pc)) {
            break;
        }
        pc = reinterpret_cast<InstA64 *>((Addr)pc + unit->size());
        unit = nullptr;
    }
}
           

指令修複

void* CodeRelocateA64::relocate(Instruction<Base> *instruction, void *toPc) throw(ErrorCodeException) {
    void* curPc = __ getPC();

    //insert later bind labels
    __ Emit(getLaterBindLabel(curOffset));

    if (!instruction->pcRelate()) {
        __ Emit(instruction);
        instruction->ref();
        return curPc;
    }
    switch (instruction->instCode()) {
        CASE(B_BL)
        CASE(B_COND)
        CASE(TBZ_TBNZ)
        CASE(CBZ_CBNZ)
        CASE(LDR_LIT)
        CASE(ADR_ADRP)
        default:
            __ Emit(instruction);
            instruction->ref();
    }
    return curPc;
}


IMPL_RELOCATE(B_BL) {

    if (inRelocateRange(inst->offset, sizeof(InstA64))) {
        inst->ref();
        inst->bindLabel(*getLaterBindLabel(inst->offset + curOffset));
        __ Emit(reinterpret_cast<Instruction<Base>*>(inst));
        return;
    }

    Addr targetAddr = inst->getImmPCOffsetTarget();

    if (inst->op == inst->BL) {
        Addr lr = reinterpret_cast<Addr>(toPc);
        lr += 4 * 4; // MovWide * 4;
        lr += 4 * 4; // MovWide * 4;
        lr += 4; // Br
        __ Mov(LR, lr);
    }
    __ Mov(IP1, targetAddr);
    __ Br(IP1);
}
           

結尾

其實 Native Inline Hook 與 ART Hook 還是有很大不同的,其本身并沒有什麼深度,隻要你多看看手冊 ==,當然 native hook 目前測試的不多,歡迎測試一起完善。