[转] gancho Android Brazo Inline

fuente original: http://ele7enxxh.com/Android-Arm-Inline-Hook.html

AndroidNativeEmu Manual comestible: https://bbs.pediy.com/thread-254799.htm 

 

 

Esto combinará el proyecto de código fuente, y la aplicación de los principios enunciados en detalle Android Brazo línea de gancho.

¿Qué es la línea gancho

Inline Enganche es decir Hook salto interna, mediante la sustitución de la instrucción en el comienzo de la instrucción de salto de función para saltar a su función original como una función, también suelen retener la interfaz de llamada función original. En comparación con la tabla de gancho GOT, línea gancho tiene una aplicabilidad más amplia, gancho casi cualquier función, pero su aplicación es más compleja, más consideración y no puede ser demasiado corto para alguna función Hook.
El principio básico se refiere a otra información en línea.

Problemas a resolver

  1. modo de brazo con el modo de diferencia Pulgar
  2. estructura de comando de salto
  3. instrucciones relacionadas con el PC Enmienda
  4. enhebrado
  5. otros detalles

Ahora voy a combinar el código fuente para resolver estas cuestiones.

modo de brazo con el modo de diferencia Pulgar

Objetos descritas en este documento se basan en la arquitectura Inline Hook 32 bits, brazo en la versión 7 y por encima del sistema, el conjunto de instrucciones se divide en el conjunto de instrucciones del brazo y el conjunto de instrucciones del pulgar. instrucción del brazo es 4 alineación de bytes, la longitud de cada instrucción son de 32 bits; la instrucción del pulgar es la alineación de 2 bytes, dividido en Thumb16, Thumb32, en el que la longitud de instrucciones Thumb16 es de 16 bits, la longitud de la instrucción Thumb32 es de 32 bits.
Cuando una función para Inline Hook, primero tiene que determinar la instrucción actual es una instrucción de función o instrucciones del pulgar del brazo, utilizando la instrucción objeto de valor de dirección de bit [0] para determinar el tipo de la dirección de destino de la instrucción. 'Bit [0] un valor de 1, el programa de destino como instrucciones de pulgar;' bit [0] es 0, el programa de destino como instrucciones ARM. Su código de aplicación asociado a la macro siguiente:

1
2
3
4
5
6
// set bit [0] un valor de 1
# Definir SET_BIT0 (dir) (dir | 1)
// bit set'[0] es 0
# Definir CLEAR_BIT0 (dir) (dir & 0xFFFFFFFE)
// test 'bits [0] valor, si se trata de un retorno a la verdad, 0 si es falso si
# Definir TEST_BIT0 (dir) (dir & 1)

 

estructura de comando de salto

instrucciones de salto se dividen en los dos siguientes:

  • serie B de instrucciones: D, BL, BX, BLX
  • Escribir directamente al registro de PC

Brazo gama B-Series es solamente una instrucción de salto 4M, la instrucción del pulgar en rama gama de la serie B está a sólo 256 bytes, pero en la mayoría de los casos va a saltar rango es mayor que 4 M, por lo que utilizar LDR PC, [PC, ?]instrucción de salto de configuración. Más instrucciones Thumb16 y ninguna instrucción de salto adecuado, si se usa construida por separado instrucción de salto de instrucciones Thumb16, la realización de más instrucciones necesidad de utilizar y también modificaciones posteriores relacionados más engorrosos para la instrucción de PC, se considera siguiente, decidimos dar apoyo para la ARMv5.
Además, el procesador Arm tubería de 3 etapas para aumentar la velocidad de la corriente de instrucciones del procesador, es decir contador de programa R15 (PC) señala siempre "son fetch" comando, en lugar de "ser ejecutado", es decir, el PC total de es un puntero a la dirección de la instrucción que se está ejecutando más 2 dirección de instrucciones. Tales como la dirección de la instrucción de corriente es 0 × 8000, entonces el valor actual de la pc, el pulgar está por debajo de 8000 + 2 × 0  2, el brazo es inferior a + 8,000 ×. 4 0  2.
Brazo para el conjunto de instrucciones, la instrucción de salto:

LDR PC, [PC, # -4]
addr

 

LDR PC, [PC, #-4]Correspondiente código de máquina es: 0xE51FF004, addrpara la dirección para saltar. La instrucción de salto está en el rango de 32 bits, 32 bits para todo el sistema, es la dirección del salto.
Para el conjunto de instrucciones Thumb32, la instrucción de salto:

LDR.W PC, [PC, # 0]
addr

 

LDR.W PC, [PC, #0]Correspondiente código de máquina es: 0x00F0DFF8, addrpara la dirección para saltar. También es compatible con cualquier dirección de salto.
Su código de implementación asociada:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dieciséis
17
18
// mano brazo
si (TEST_BIT0 (Item-> target_addr)) {
int i;
 
i = 0;
si (CLEAR_BIT0 (Item-> target_addr)% 4! = 0) {
(( Uint16_t *) CLEAR_BIT0 (Item-> target_addr)) [i ++] = 0xBF00; // NOP
}
(( Uint16_t *) CLEAR_BIT0 (Item-> target_addr)) [i ++] = 0xF8DF;
(( Uint16_t *) CLEAR_BIT0 (Item-> target_addr)) [i ++] = 0xF000; // LDR.W PC, [PC]
(( Uint16_t *) CLEAR_BIT0 (Item-> target_addr)) [i ++] = Item-> new_addr y 0xFFFF;
(( Uint16_t *) CLEAR_BIT0 (Item-> target_addr)) [i ++] = Item-> new_addr >> 16;
}
// Modo Pulgar
else {
(( Uint32_t *) (Item-> target_addr)) [ 0] = 0xe51ff004; // LDR PC, [PC, # -4]
(( Uint32_t *) (Item-> target_addr)) [ 1] = Item-> new_addr;
}

 

En primer lugar, la función objetivo se determina por el conjunto de instrucciones de macros tipo TEST_BIT0, en el que si se trata de conjunto de instrucciones del pulgar, una pluralidad de los siguientes procesos adicionales:

1
2
3
si (CLEAR_BIT0 (Item-> target_addr)% 4! = 0) {
(( Uint16_t *) CLEAR_BIT0 (Item-> target_addr)) [i ++] = 0xBF00; // NOP
}

 

Se despeja el valor de bit [0], si el valor no es 4 byte alineado, añadir una de dos bytes NOPde instrucciones, de modo que la posterior alineación de bytes de instrucciones 4. Esto se debe a que la instrucción Thumb32, si se modifica el valor de instrucción del registro PC, esta instrucción debe ser de 4 bytes alineados, de lo contrario una instrucción ilegal.

instrucciones relacionadas con el PC Enmienda

conjunto de instrucciones del conjunto de instrucciones del pulgar o del brazo, hay una gran cantidad de comandos y valores de PC-relacionados, tales como: la serie B de instrucciones, series literales de directivas. Mandaron a los primeros saltos reemplazar la función original se trasladó a trampoline_instructions, el valor PC en este momento se ha cambiado, por lo que la necesidad de una instrucción relacionada PC a correcto (es decir, la llamada corrección para calcular la dirección real, y utilizar otra instrucciones realizan la misma función). código de corrección relacionado es relocate.c archivo. El cual INSTRUCTION_TYPEse describen las necesidades de instrucción para ser corregidos, espacio limitado, aquí sólo el código de instrucción juego brazo proceso de corrección de luz para la correspondiente relocateInstructionInArmfunción.
prototipo de la función es la siguiente:

1
2
3
4
5
6
7
8
9
10
/ *
target_addr: Gancho dirección de la función objetivo de ser, es decir, el valor PC actual, un comando de corrección
orig_instructions: almacenar la primera dirección de las instrucciones originales, instrucciones sobre cómo resolver la recuperación y el seguimiento de las instrucciones originales
longitud: longitud, instrucción original Arm almacena en la instrucción de 8 bytes; la instrucción del pulgar es de 12 bytes
trampoline_instructions: almacenar corrección después de la primera dirección de la instrucción para llamar la función original
orig_boundaries: almacenar el límite instrucción instrucción original (el límite es el llamado desplazamiento de la dirección de inicio de la instrucción), el hilo utilizado en el proceso posterior, la migración de la PC
trampoline_boundaries: almacenar límite de instrucción después de la orden de corrección, con el mismo propósito
contar: el número de elementos de la misma instrucción, con el uso del proceso
* /
estática vacío relocateInstructionInArm ( target_addr uint32_t, uint32_t * orig_instructions, int longitud, uint32_t * trampoline_instructions, int * orig_boundaries, int * trampoline_boundaries, int * recuento);

 

En la aplicación específica, la función por primera getTypeInArmdeterminar el tipo de la instrucción en curso, por este tipo de función, que consta de cuatro ramales de procesamiento:

  1. BLX_ARM, BL_ARM, B_ARM, BX_ARM
  2. ADD_ARM
  3. ADR1_ARM, ADR2_ARM, LDR_ARM, MOV_ARM
  4. otras instrucciones

Corrección BLX_ARM, BL_ARM, B_ARM, instrucciones BX_ARM

即为B系列指令(BLX <label>BL <label>B <label>BX PC)的修正,其中BLX_ARMBL_ARM需要修正LR寄存器的值,相关代码为:

1
2
3
if (type == BLX_ARM || type == BL_ARM) {
trampoline_instructions[trampoline_pos++] = 0xE28FE004; // ADD LR, PC, #4
}

 

接下来构造相应的跳转指令,即为:

1
trampoline_instructions[trampoline_pos++] = 0xE51FF004; // LDR PC, [PC, #-4]

 

最后解析指令,计算实际跳转地址value,并将其写入trampoline_instructions,相关代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (type == BLX_ARM) {
x = ((instruction & 0xFFFFFF) << 2) | ((instruction & 0x1000000) >> 23);
}
else if (type == BL_ARM || type == B_ARM) {
x = (instruction & 0xFFFFFF) << 2;
}
else {
x = 0;
}
 
top_bit = x >> 25;
imm32 = top_bit ? (x | ( 0xFFFFFFFF << 26)) : x;
if (type == BLX_ARM) {
value = pc + imm32 + 1;
}
else {
value = pc + imm32;
}
trampoline_instructions[trampoline_pos++] = value;

 

如此便完成了B系列指令的修正,关于指令的字节结构请参考Arm指令手册。

ADD_ARM指令的修正

ADD_ARM指的是ADR Rd, <label>格式的指令,其中<label>与PC相关。
首先通过循环遍历,得到Rd寄存器,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int rd;
int rm;
int r;
 
// 解析指令得到rd、rm寄存器
rd = (instruction & 0xF000) >> 12;
rm = instruction & 0xF;
 
// 为避免冲突,排除rd、rm寄存器,选择一个临时寄存器Rr
for (r = 12; ; --r) {
if (r != rd && r != rm) {
break;
}
}

 

接下来是构造修正指令:

1
2
3
4
5
6
7
8
9
10
11
// PUSH {Rr},保护Rr寄存器值
trampoline_instructions[trampoline_pos++] = 0xE52D0004 | (r << 12);
// LDR Rr, [PC, #8],将PC值存入Rr寄存器中
trampoline_instructions[trampoline_pos++] = 0xE59F0008 | (r << 12);
// 变换原指令`ADR Rd, <label>`为`ADR Rd, Rr, ?`
trampoline_instructions[trampoline_pos++] = (instruction & 0xFFF0FFFF) | (r << 16);
//POP {Rr},恢复Rr寄存器值
trampoline_instructions[trampoline_pos++] = 0xE49D0004 | (r << 12);
// ADD PC, PC,跳过下一条指令
trampoline_instructions[trampoline_pos++] = 0xE28FF000;
trampoline_instructions[trampoline_pos++] = pc;

 

ADR1_ARM、ADR2_ARM、LDR_ARM、MOV_ARM

分别为ADR Rd, <label>ADR Rd, <label>LDR Rt, <label>MOV Rd, PC
同样首先解析指令,得到value,相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int r;
uint32_t value;
 
r = (instruction & 0xF000) >> 12;
 
if (type == ADR1_ARM || type == ADR2_ARM || type == LDR_ARM) {
uint32_t imm32;
 
imm32 = instruction & 0xFFF;
if (type == ADR1_ARM) {
value = pc + imm32;
}
else if (type == ADR2_ARM) {
value = pc - imm32;
}
else if (type == LDR_ARM) {
int is_add;
 
is_add = (instruction & 0x800000) >> 23;
if (is_add) {
value = (( uint32_t *) (pc + imm32))[0];
}
else {
value = (( uint32_t *) (pc - imm32))[0];
}
}
}
else {
value = pc;
}

 

最后构造修正指令,代码如下:

1
2
3
4
5
// LDR Rr, [PC]
trampoline_instructions[trampoline_pos++] = 0xE51F0000 | (r << 12);
// 跳过下一条指令
trampoline_instructions[trampoline_pos++] = 0xE28FF000; // ADD PC, PC
trampoline_instructions[trampoline_pos++] = value;

 

其他指令

事实上,还有些指令格式需要修正,例如:PUSH {PC}PUSH {SP}等,虽然这些指令被Arm指令手册标记为deprecated,但是仍然为合法指令,不过在实际汇编中并未发现此类指令,故未做处理,相关代码如下:

1
2
// 直接将指令存放到trampoline_instructions中
trampoline_instructions[trampoline_pos++] = instruction;

 

处理完所有待处理指令后,最后加入返回指令:

// LDR PC, [PC, #-4]
trampoline_instructions[trampoline_pos++] = 0xe51ff004;
trampoline_instructions[trampoline_pos++] = lr;

 

Thumb指令的修正,大家可以参考这里的思路,自行阅读源码。

线程处理

一个完善的Inline Hook方案必须要考虑多线程环境,即要考虑线程恰好执行到被修改指令的位置。在Window下,使用GetThreadContextSetThreadContext枚举所有线程,迁移context到搬迁后的指令中。然而在Linux+Arm环境下,并没有直接提供相同功能的API,不过可以使用ptrace完成,主要流程如下:

  1. 解析/proc/self/task目录,获取所有线程id
  2. 创建子进程,父进程等待。子进程枚举所有线程,PTRACE_ATTACH线程,迁移线程PC寄存器,枚举完毕后,子进程给自己发SIGSTOP信号,等待父进程唤醒
  3. 父进程检测到子进程已经SIGSTOP,完成Inline Hook工作,向子进程发送SIGCONT信号,同时等待子进程退出
  4. 子进程枚举所有线程,PTRACE_DETACH线程,枚举完毕后,子进程退出
  5. 父进程继续其他工作

这里使用子进程完成线程处理工作,实际上是迫不得已的。因为,如果直接使用本进程PTRACE_ATTACH线程,会出现operation not permitted,即使赋予root权限也是同样的错误,具体原因不得而知。
具体代码请参考freezeunFreeze两个函数。

其他一些细节

    1. 页保护
      页面大小为4096字节,使用mprotect函数修改页面属性,修改为PROT_READ | PROT_WRITE | PROT_EXEC
    2. 刷新缓存
      对于ARM处理器来说,缓存机制作用明显,内存中的指令已经改变,但是cache中的指令可能仍为原有指令,所以需要手动刷新cache中的内容。采用cacheflush即可实现。
    3. 一个已知的BUG
      虽然本库已经把大部分工作放在了registerInlineHook函数中,但是在inlineHookinlineUnHook函数中还是不可避免的使用了部分libc库的API函数,例如:mprotectmemcpymunmapfreecacheflush等。如果使用本库对上述API函数进行Hook,可能会失败甚至崩溃,这是因为此时原函数的指令已经被破坏,或者其逻辑已经改变。解决这个Bug有两个方案,第一是采用其他Hook技术;第二将本库中的这些API函数全部采用内部实现,即不依赖于libc库,可采用静态链接libc库,或者使用汇编直接调相应的系统调用号。

Supongo que te gusta

Origin www.cnblogs.com/kuliuheng/p/12670828.html
Recomendado
Clasificación