branch 跳转是arm中常用的汇编跳转
例如
c
void a(){
xxx
}
void call_a(){
a();//调用a函数
}
此时编译器就会产生
asm
lable a
nop;
lable call_a
bl a:
这里的bl 就是 跳转[b] 并 拿回返回值[l] 尽管a是一个 void 函数 但是执行流程还是要回来的
如果是
c
void aexit(){
exit(1);
}
aexit()
此时就会产生 b aexit的汇编了 因为一去不回了
现在假设 我们需要在已经编译完成的函数上改变既有的执行流程 那么 在不考虑栈平衡和寄存器的变量的问题的基础上
我们来对测试的代码进行测试 如
c
#include <stdio.h>
void asmc(){
char c;
int i;
int j;
i = 0;
while (1) {
c = getchar();
if (c == '\n') {
break;
}
if (c == ' ') {
continue;
}
if (c == '(') {
i++;
continue;
}
if (c == ')') {
i--;
continue;
}
if (i == 0) {
putchar(c);
}
}
return;
}
void asme(){
printf("this is asme\n");
return;
}
int main() {
asmc();
asme();
return 0;
}
在编译完成的asm是
main_asm
; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
main
var_8= -8
var_4= -4
var_s0= 0
; __unwind {
SUB SP, SP, #0x20
STP X29, X30, [SP,#0x10+var_s0]
ADD X29, SP, #0x10
MOV W8, WZR
STR W8, [SP,#0x10+var_8]
STUR WZR, [X29,#var_4]
BL asmc
BL asme ; asme()
LDR W0, [SP,#0x10+var_8]
LDP X29, X30, [SP,#0x10+var_s0]
ADD SP, SP, #0x20 ; ' '
RET
; } // starts at 7E0
; End of function main
; .text ends
asmc_asm
; Attributes: bp-based frame
asmc
var_8= -8
var_1= -1
var_s0= 0
; __unwind {
SUB SP, SP, #0x20
STP X29, X30, [SP,#0x10+var_s0]
ADD X29, SP, #0x10
STR WZR, [SP,#0x10+var_8]
loc_744
BL .getchar
STURB W0, [X29,#var_1]
LDURB W8, [X29,#var_1]
SUBS W8, W8, #0xA
B.NE loc_75C
B loc_7B8
loc_75C
LDURB W8, [X29,#var_1]
SUBS W8, W8, #0x20 ; ' '
B.NE loc_76C
B loc_744
loc_76C
LDURB W8, [X29,#var_1]
SUBS W8, W8, #0x28 ; '('
B.NE loc_788
LDR W8, [SP,#0x10+var_8]
ADD W8, W8, #1
STR W8, [SP,#0x10+var_8]
B loc_744
loc_788
LDURB W8, [X29,#var_1]
SUBS W8, W8, #0x29 ; ')'
B.NE loc_7A4
LDR W8, [SP,#0x10+var_8]
SUBS W8, W8, #1
STR W8, [SP,#0x10+var_8]
B loc_744
loc_7A4
LDR W8, [SP,#0x10+var_8]
CBNZ W8, loc_7B4
LDURB W0, [X29,#var_1] ; c
BL .putchar
loc_7B4
B loc_744
loc_7B8
LDP X29, X30, [SP,#0x10+var_s0]
ADD SP, SP, #0x20 ; ' '
RET
; } // starts at 734
; End of function asmc
asme_asm
; asme()
; Attributes: bp-based frame
; __int64 asme()
asme
var_s0= 0
; __unwind {
STP X29, X30, [SP,#-0x10+var_s0]!
MOV X29, SP
; 2: return printf("this is asme\n");
ADRL X0, aThisIsAsme ; "this is asme\n"
BL .printf
LDP X29, X30, [SP+var_s0],#0x10
RET
; } // starts at 7C4
; End of function asme
其中 各个函数的偏移量是
offsetsinfo
asmc .text 0000000000000734 00000090 00000020 R . . . . B . .
asme .text 00000000000007C4 0000001C 00000010 R . . . . B T .
main .text 00000000000007E0 00000030 00000020 R . . . . B T .
那么跳转到 asme函数的就是 bl asme = bl 7C4
不过这是常规的情况下 也就是 ida的自动patch插件会自动的将7C4转换成 当前所在地址的位置和 7C4 的距离 然后生成目标的汇编
不然直接 bl 7C4 会出现刻舟求剑的情况
不过在ida中 确实可以直接填写 bl 7C4
但是原始逻辑也并没有那么的简单
通常情况下寄存器执行到b的跳转汇编的时候一般都是计算偏移量 因为b跳转的上限距离是±的xxxx
因此 在cpu的实际运行中 是类似于
asm
b 0x8(向下两个地址)
b -0x16(向上四个地址)
而如果是期望直接真的跳转到一个地址上 那么汇编会变成
asm
LDR X8 = XXXX(这里通常会是一个栈地址或者是一个已经准备好的寄存器)
BR X8 直接跳转寄存器实现了 C语言的指针跳转
但是这种条寄存器一般只有是inlineHook时才会出现的汇编 在正常的使用中几乎没有这种汇编的出现