sub_18AB0(v21, v20);
__int64 __usercall sub_18AB0@<X0>(__int64 a1@<X8>)
{
__int64 v2; // x20
__int64 v3; // x0
char v5[24]; // [xsp+8h] [xbp-88h] BYREF
char s[16]; // [xsp+20h] [xbp-70h] BYREF
__int128 v7; // [xsp+30h] [xbp-60h] BYREF
__int64 v8; // [xsp+48h] [xbp-48h] BYREF
__int64 v9; // [xsp+50h] [xbp-40h]
__int64 v10; // [xsp+58h] [xbp-38h]
v10 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
sub_18D30();
.........
.......
可以看到sub_18AB0签名ida反汇编的不正确,传入了两个参数,但只显示了一个。这里可以改一下,改成sub_18AB0@
按tab键看下汇编代码
.text:0000000000013638 BL sub_1391C
.text:000000000001363C ADD X8, SP, #0xC0+var_90
.text:0000000000013640 ADD X0, SP, #0xC0+var_A8
.text:0000000000013644 MOV X1, SP
.text:0000000000013648 BL sub_18AB0
可以看到sub_18AB0传入参数是两个x0和x1,(arm64 abi调用约定函数的第 1~8 个参数,固定依次使用 X0、X1、X2、X3、X4、X5、X6、X7 这 8 个寄存器传递,多于8个时候才会使用栈)
sub_18AB0我们hook发现传参是传入了两个字符串,返回参数没有看出来什么价值,因为x8在arm64比较特殊,返回的内容大于16字节就用x8来存储返回内容的地址
,我们又hook了x8寄存器,发现x8寄存器里记录了返回的地址,通过hexdump(ptr(this.arg8).add(Process.pointerSize *2).readPointer()); 发现里面存储了加密的字符串。
我们进到方法里面看下
.text:0000000000018AB0 ; __unwind { // __gxx_personality_v0
.text:0000000000018AB0 SUB SP, SP, #0xA0
.text:0000000000018AB4 STR X23, [SP,#0x90+var_30]
.text:0000000000018AB8 STP X22, X21, [SP,#0x90+var_20]
.text:0000000000018ABC STP X20, X19, [SP,#0x90+var_10]
.text:0000000000018AC0 STP X29, X30, [SP,#0x90+var_s0]
.text:0000000000018AC4 ADD X29, SP, #0x90
.text:0000000000018AC8 MRS X22, #3, c13, c0, #2
.text:0000000000018ACC MOV X19, X8 ;因为结果在x8里面,所以我们看x8
.text:0000000000018AD0 LDR X8, [X22,#0x28]
.text:0000000000018AD4 STUR X8, [X29,#var_38]
.text:0000000000018AD8 ADD X8, SP, #0x90+var_88
.text:0000000000018ADC BL sub_18D30
.text:0000000000018AE0 ADRP X21, #y_ptr@PAGE
.text:0000000000018AE4 LDR X21, [X21,#y_ptr@PAGEOFF]
.text:0000000000018AE8 STP XZR, XZR, [SP,#0x90+var_48]
.text:0000000000018AEC ADRP X23, #x_ptr@PAGE
.text:0000000000018AF0 LDR W8, [X21]
.text:0000000000018AF4 LDR X23, [X23,#x_ptr@PAGEOFF]
.text:0000000000018AF8 CMP W8, #0xA
.text:0000000000018AFC B.LT loc_18B10
.text:0000000000018B00 LDR W8, [X23]
......
......
.text:0000000000018CAC MOV X1, X20 ; format
.text:0000000000018CB0 BL .sprintf
.text:0000000000018CB4 LDRB W2, [SP,#0x90+var_3A]
.text:0000000000018CB8 ADD X0, X23, #0x1C ; s
.text:0000000000018CBC MOV X1, X20 ; format
.text:0000000000018CC0 BL .sprintf
.text:0000000000018CC4 LDRB W2, [SP,#0x90+var_39]
.text:0000000000018CC8 ADD X0, X23, #0x1E ; s
.text:0000000000018CCC MOV X1, X20 ; format
.text:0000000000018CD0 BL .sprintf
.text:0000000000018CD4 ADD X1, SP, #0x90+s
.text:0000000000018CD8 MOV W2, #0x20 ; ' '
.text:0000000000018CDC MOV X0, X19
.text:0000000000018CE0 BL sub_19248
.text:0000000000018CE4 ADD X0, SP, #0x90+var_88
.text:0000000000018CE8 BL sub_12CF4
.text:0000000000018CEC LDR X8, [X22,#0x28]
.text:0000000000018CF0 LDUR X9, [X29,#var_38]
.text:0000000000018CF4 CMP X8, X9
.text:0000000000018CF8 B.NE loc_18D14
.text:0000000000018CFC LDP X29, X30, [SP,#0x90+var_s0]
.text:0000000000018D00 LDP X20, X19, [SP,#0x90+var_10]
.text:0000000000018D04 LDP X22, X21, [SP,#0x90+var_20]
.text:0000000000018D08 LDR X23, [SP,#0x90+var_30]
.text:0000000000018D0C ADD SP, SP, #0xA0
.text:0000000000018D10 RET
.text:0000000000018D14 ; ---------------------------------------------------------------------------
.text:0000000000018D14
.text:0000000000018D14 loc_18D14 ; CODE XREF: sub_18AB0+248↑j
.text:0000000000018D14 BL .__stack_chk_fail
.text:0000000000018D18 ; ---------------------------------------------------------------------------
.text:0000000000018D18 B loc_18D1C
.text:0000000000018D1C ; ---------------------------------------------------------------------------
.text:0000000000018D1C
.text:0000000000018D1C loc_18D1C ; CODE XREF: sub_18AB0+268↑j
.text:0000000000018D1C MOV X19, X0
.text:0000000000018D20 ADD X0, SP, #0x90+var_88
.text:0000000000018D24 BL sub_12CF4
.text:0000000000018D28 MOV X0, X19
.text:0000000000018D2C BL ._Unwind_Resume
.text:0000000000018D2C ; } // starts at 18AB0
.text:0000000000018D2C ; End of function sub_18AB0
.text:0000000000018D2C
.text:0000000000018D30
.text:0000000000018D30 ; =============== S U B R O U T I N E =======================================
因为结果在x8里面,所以我们看x8,发现x8在一开始的时候赋值给了x19,而x19在下面调用处,x0被赋值为x19,x0作为了sub_19248参数,很可疑,我们再去hook这个函数。
补充:
只要值被放进 X19–X28,它就从‘临时值’升级成‘逻辑对象’了
X8 不是“返回值”,而是“返回值将要写入的地方”
所以它在函数一开始就必须是有效的
Scratch Register (X0-X15) 就是工地上的临时工,干完这票就走,随时可能被换掉;
Callee-Saved Register (X19-X29) 就是公司的正式员工,稳定性强,负责核心业务。
x8三大用处:
1.间接返回:
ABI 规定:
- 调用者先分配内存
- 地址放在 X8
- 被调用函数把结果写到 [X8]
X8 放的是“返回值地址”,不是返回值本身
2.临时 scratch 寄存器
- 编译器爱用
- 调用后随时可被覆盖
3.JNI / C++ 内部惯用法
BL some_func
MOV X19, X8
通常意味着:
– 返回的是 对象
- 通过 X8 间接返回
- 然后被“升级”为长期变量(X19)
在 ARM64 ABI 里:
| 寄存器 | 含义 |
|---|---|
| X0–X7 | 参数 / 返回值(调用后不保证保存) |
| X8 | 临时寄存器 / 间接返回值 / scratch |
| X19–X28 | 被调用者保存(callee-saved) |
x8随时可能被下一个函数调用改掉
X19:跨函数调用仍然稳定
var sub_18AB0 = base.add(0x18AB0);
Inteceptor.attach(sub_18AB0, {
onEnter:function(args){
this.arg8 = this.context.x8;
console.log("sub_18AB0 onEnter: arg0:", ptr(args[0]).readCString(), " arg1:"ptr(args[1]).readCString(), " arg8:\n", hexdump(this.arg8))
}, onLeave: function(retVal){
console.log("sub_18AB0 onLeave:\n", hexdump(retVal), " arg8:\n", hexdump(this.arg8))
}
})
0 条评论