ARM汇编写相对地址的bl,已知PC和目标地址,想写个.s,利用.org之类的指示符指定PC,然后"-c"编译,"objdump -d"查看机器码。不考虑ldr+blx。更进一步,想将几个固定地址的片段写在同一个.s里,比如代码、数据在不同地址。不考虑位置无关代码的自定位技巧。原始意图大致如此,但我用gcc未能得逞。
.org的第一形参是有符号整型,对于32-bits来说,0x80000000及以上的数被认为是负数,达不到预期目的。即使让它成为无符号整型或者动用64-bits,gcc/gas的.org指示符会实际填充,导致.o文件庞大。总之,原生.org不满足需求。
一度考虑用rasm2:
$ rasm2 -a arm -b 32 -o 0xf0e0dee0 -D "3a 06 00 eb"
0xf0e0dee0 4 3a0600eb bl 4041275344
$ rasm2 -a arm -b 32 -o 0xf0e0dee0 "bl 0xf0e0f7d0"
Branch into out of range
Cannot assemble 'bl 0xf0e0df70' at line 3
invalid
rasm2的反汇编正确,汇编失败,这可能与32-bits有关。nasm不支持arm。挣扎很久之后,bluerust推荐:
Keystone is a lightweight multi-platform, multi-architecture assembler framework
https://github.com/keystone-engine/keystone
源码可以在Linux、Windows下顺利编译,自带一个kstool用于演示。
$ ./kstool arm "bl 0xf0e0f7d0" 0xf0e0dee0
bl 0xf0e0f7d0 = [ 3a 06 00 eb ]
上例指定了bl指令所在地址,生成的机器码是相对跳转。动用分号后可以单行输入多
条指令:
$ echo -n "push {ip, lr};mov r0, #0xff000000" | ./kstool arm
push {ip, lr};mov r0, #0xff000000 = [ 00 50 2d e9 ff 04 a0 e3 ]
kstool在*nix下支持从stdin读入,Windows版不支持。从kstool.cpp看看,它曾经打算支持多行输入,但未能真正实现。
为了利用Keystone,很简单,大致这么几步:
ks_open() 指定CPU
ks_asm() 指定基址、汇编指令
printf() 输出机器码
ks_free() 释放动态分配的机器码空间
ks_close() 关闭
kstool_arm_sample.cpp如下:
(参txt原文)
(http://scz.617.cn:8/misc/201811071803.txt)
$ kstool_arm_sample <assembly> [base]
$ ./kstool_arm_sample "bl 0x8080df70" 0x8080dee0
000000008080dee0 [ 22 00 00 eb ] bl 0x8080df70
说个与ARM汇编无关与x64汇编相关的事。keystone汇编某条x64指令时(见后)生成的机器码与IDA显示不符,我以为是BUG,唆使bluerust跟作者联系一下,然后他较了一下真,有了后续内容。
就"mov rax, qword ptr gs:[188h]"而言,有两种编码方案:
mov rax, qword ptr gs:[dword 188h] [ 65 48 8b 04 25 88 01 00 00 ]
mov rax, qword ptr gs:[qword 188h] [ 65 48 a1 88 01 00 00 00 00 00 00 ]
此处的188h是displacement,IDA、gas对此做了优化,使用32-bits displacement,ks_asm()死活使用64-bits displacement。
$ ./kstool x64 "mov rax, qword ptr gs:[0x188]"
mov rax, qword ptr gs:[0x188] = [ 65 48 a1 88 01 00 00 00 00 00 00 ]
nasm语法可以对立即数指定位宽描述符:
BITS 64
mov rax, [dword gs:0x188]
mov rax, [qword gs:0x188]
$ nasm -f bin -o test.bin test.nasm
$ xxd -g 1 test.bin
00000000: 65 48 8b 04 25 88 01 00 00 65 48 a1 88 01 00 00 eH..%....eH.....
00000010: 00 00 00 00 ....
$ rasm2 -a x86 -b 64 -s intel -D 65488b0425880100006548a18801000000000000
0x00000000 9 65488b042588010000 mov rax, qword gs:[0x188]
0x00000009 11 6548a18801000000000000 movabs rax, qword gs:[0x188]
rasm2反汇编"mov rax, [qword gs:0x188]"时将mov显示成movabs,gas处理movabs时使用64-bits displacement。cdb则对两种情况都显示成mov,但为了强调64-bits,将立即数显示成"gs:[0000000000000188h]"。
> eb rip 65 48 8b 04 25 88 01 00 00 65 48 a1 88 01 00 00 00 00 00 00
> u rip l 2
00000000`ff662ff8 65488b042588010000 mov rax,qword ptr gs:[188h]
00000000`ff663001 6548a18801000000000000 mov rax,qword ptr gs:[0000000000000188h]
上面两条指令效果完全一样,但机器码不同。
keystone支持nasm语法,但支持得不完整,比如不支持立即数位宽描述符:
$ ./kstool x64nasm "mov rax, qword gs:[0x188]"
= [ 65 48 a1 88 01 00 00 00 00 00 00 ]
$ ./kstool x64nasm "mov rax, qword gs:[dword 0x188]"
ERROR: failed on ks_asm()