这几天由于支持ipjone的需要,做了arm平台上的反汇编工作,由于汇编的东西较为复杂,所以需要形成文档,以备查阅。
首先,使用的反编译工具是ndk提供的toolchains里的交叉编译工具gcc。我们选取目标平台是arm的arm-linux-androideabi-4.7,工具路径是%ANDROID_NDK%\toolchains\arm-linux-androideabi-4.7\prebuilt\windows\arm-linux-androideabi\bin,里面有编译用的gcc和反编译用的objdump,我们就使用这个objdump进行下一步的工作。
在windows系统中,需要装一下linux环境cygwin。把路径cd到/home/bin下面,然后把前面找到的那个objdump.exe改个名字放到bin目录下面,我改成objdump_arm.exe用来使用。然后找到我们的android工程下面的jni文件夹,就是将c源代码编译成.o库的地方,生成的obj文件在obj文件夹里,各种找后找到.c文件对应的.o文件,将其copy到cygwin的bin文件夹下,用来做反编译。我这里用的是test.o。
然后就可以开始反编译了。往cygwin里面敲一个objdump_arm.exe,就可以看到帮助信息,如下:
$ objdump_arm.exe
Usage: C:\cygwin\bin\objdump_arm.exe <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, --archive-headers Display archive header information
-f, --file-headers Display the contents of the overall file header
-p, --private-headers Display object format specific file header contents
-P, --private=OPT,OPT... Display object format specific contents
-h, --[section-]headers Display the contents of the section headers
-x, --all-headers Display the contents of all headers
-d, --disassemble Display assembler contents of executable sections
-D, --disassemble-all Display assembler contents of all sections
-S, --source Intermix source code with disassembly
-s, --full-contents Display the full contents of all sections requested
-g, --debugging Display debug information in object file
-e, --debugging-tags Display debug information using ctags style
-G, --stabs Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoRt] or
--dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
=frames-interp,=str,=loc,=Ranges,=pubtypes,
=gdb_index,=trace_info,=trace_abbrev,=trace_aranges]
Display DWARF info in the file
-t, --syms Display the contents of the symbol table(s)
-T, --dynamic-syms Display the contents of the dynamic symbol table
-r, --reloc Display the relocation entries in the file
-R, --dynamic-reloc Display the dynamic relocation entries in the file
@<file> Read options from <file>
-v, --version Display this program's version number
-i, --info List object formats and architectures supported
-H, --help Display this information
从上面这些中选取我们需要的即可,这次的工作中用到的主要有-d,--source和-W三个。
首先-d可以直接给出大片大片的反汇编结果,举个栗子:
objdump_arm.exe -d test.o结果如下:
Disassembly of section .text.neon_short:
00000000 <neon_short>:
0: e0610000 rsb r0, r1, r0
4: e3a0c000 mov ip, #0
8: e92d4030 push {r4, r5, lr}
c: f2c00050 vmov.i32 q8, #0 ; 0x00000000
10: e1a03001 mov r3, r1
14: e3a04078 mov r4, #120 ; 0x78
18: f26021f0 vorr q9, q8, q8
1c: f26061f0 vorr q11, q8, q8
20: f26081f0 vorr q12, q8, q8
24: e2835d0f add r5, r3, #960 ; 0x3c0
28: e2544001 subs r4, r4, #1
2c: f463574f vld1.16 {d21}, [r3]
30: f465a74f vld1.16 {d26}, [r5]
34: e2835d1e add r5, r3, #1920 ; 0x780
38: f465b74f vld1.16 {d27}, [r5]
3c: e2835d2d add r5, r3, #2880 ; 0xb40
40: f465c74f vld1.16 {d28}, [r5]
44: e0835000 add r5, r3, r0
48: e2833008 add r3, r3, #8
4c: f465474f vld1.16 {d20}, [r5]
50: f2d508a4 vmlal.s16 q8, d21, d20
54: f2da28a4 vmlal.s16 q9, d26, d20
58: f2db68a4 vmlal.s16 q11, d27, d20
5c: f2dc88a4 vmlal.s16 q12, d28, d20
60: 1affffef bne 24 <neon_short+0x24>
64: f26141b1 vorr d20, d17, d17
68: e1a03002 mov r3, r2
6c: e2800d0f add r0, r0, #960 ; 0x3c0
70: f2644bb0 vpadd.i32 d20, d20, d16
74: f26301b3 vorr d16, d19, d19
78: f2644bb4 vpadd.i32 d20, d20, d20
7c: f2602bb2 vpadd.i32 d18, d16, d18
80: f26701b7 vorr d16, d23, d23
84: f2606bb6 vpadd.i32 d22, d16, d22
88: ee144b90 vmov.32 r4, d20[0]
8c: f26901b9 vorr d16, d25, d25
90: f2622bb2 vpadd.i32 d18, d18, d18
94: e7a3400c str r4, [r3, ip]!
98: f2608bb8 vpadd.i32 d24, d16, d24
9c: e2834a02 add r4, r3, #8192 ; 0x2000
a0: f2666bb6 vpadd.i32 d22, d22, d22
a4: e28cc004 add ip, ip, #4
a8: f2688bb8 vpadd.i32 d24, d24, d24
ac: f4c4280f vst1.32 {d18[0]}, [r4]
b0: e35c0a02 cmp ip, #8192 ; 0x2000
b4: e2834901 add r4, r3, #16384 ; 0x4000
b8: e2833a06 add r3, r3, #24576 ; 0x6000
bc: f4c4680f vst1.32 {d22[0]}, [r4]
c0: f4c3880f vst1.32 {d24[0]}, [r3]
c4: 1affffd0 bne c <neon_short+0xc>
c8: e8bd8030 pop {r4, r5, pc}
直接把这个结果粘到编辑器里,修改一下,就可以使用了。
另外,--source选项可以得到和源代码对应的反汇编结果,在一定程度上能够帮助你稍微看懂一点汇编代码,但是作用并不是太大,而且看起来很乱。
还有就是-W选项,它可以查到一些变量和寄存器的对应关系,比方说我面对着一堆令人费解的汇编代码,想找到正确的寄存器把输入参数传进去,就可以使用-W选项碰碰运气,一旦使用了之后,会出现好大一片结果,在浩如烟海的字符里,可能就会找到你需要的变量名和寄存器的对应关系。我就是从里面找到的。三个输入参数分别对应于r0、r1和r2。
现在有了汇编代码,我们就只需要修改它就好了,还有就是最后的集成工作,怎么样把我们的汇编代码嵌入到C代码中运行。首先要找到函数的输入和输出,确定下来对应的寄存器,准备传参。
嵌入格式如下:
__asm__ __volatile__(
"push {r4, r5, lr}\n\t"
"mov r0, %[pWeight]\n\t"
"mov r1, %[pValueIn]\n\t"
"mov r2, %[pValueOut]\n\t"
"rsb r0, r1, r0\n\t"
"mov ip, #0\n\t"
"1:vmov.i32 q8, #0\n\t"
"mov r3, r1\n\t"
"mov r4, #120\n\t"
"vorr q9, q8, q8\n\t"
"vorr q11, q8, q8\n\t"
"vorr q12, q8, q8\n\t"
"2:add r5, r3, #960\n\t"
"subs r4, r4, #1\n\t"
"vld1.16 {d21}, [r3]\n\t"
"vld1.16 {d26}, [r5]\n\t"
"add r5, r3, #1920\n\t"
"vld1.16 {d27}, [r5]\n\t"
"add r5, r3, #2880\n\t"
"vld1.16 {d28}, [r5]\n\t"
"add r5, r3, r0\n\t"
"add r3, r3, #8\n\t"
"vld1.16 {d20}, [r5]\n\t"
"vmlal.s16 q8, d21, d20\n\t"
"vmlal.s16 q9, d26, d20\n\t"
"vmlal.s16 q11, d27, d20\n\t"
"vmlal.s16 q12, d28, d20\n\t"
"bne 2b\n\t"
"vorr d20, d17, d17\n\t"
"mov r3, r2\n\t"
"add r0, r0, #960\n\t"
"vpadd.i32 d20, d20, d16\n\t"
"vorr d16, d19, d19\n\t"
"vpadd.i32 d20, d20, d20\n\t"
"vpadd.i32 d18, d16, d18\n\t"
"vorr d16, d23, d23\n\t"
"vpadd.i32 d22, d16, d22\n\t"
"vmov.32 r4, d20[0]\n\t"
"vorr d16, d25, d25\n\t"
"vpadd.i32 d18, d18, d18\n\t"
"str r4, [r3, ip]!\n\t"
"vpadd.i32 d24, d16, d24\n\t"
"add r4, r3, #8192\n\t"
"vpadd.i32 d22, d22, d22\n\t"
"add ip, ip, #4\n\t"
"vpadd.i32 d24, d24, d24\n\t"
"vst1.32 {d18[0]}, [r4]\n\t"
"cmp ip, #8192\n\t"
"add r4, r3, #16384\n\t"
"add r3, r3, #24576\n\t"
"vst1.32 {d22[0]}, [r4]\n\t"
"vst1.32 {d24[0]}, [r3]\n\t"
"bne 1b\n\t"
"pop {r4, r5, pc}\n\t"
:
:[pWeight] "r" (weight), [pValueIn] "r" (value_in), [pValueOut] "r" (value_out)
);
从这个可以看出,前后各加了一坨代码,用来声明和传参。后面带冒号那两行是输入输出的声明,第一行表示输出,没有就空着,有的话需要写成:
:[pValueOut] "=r" (value_out)
注意,和输入的写法不同,需要加一个等号。
然后前面的赋值没什么可说的,只要找准寄存器就好了。
现在汇编代码就可以嵌入到源代码里去编译运行了,但是没准还是会遇到编译不通过的问题。比方说我就遇到了。因为有一个指令叫strhle和strhgt,明明是反汇编出来的,却打死都编译不过,直接报error,说那是“Bad Instrution”,令我百思不得其解。后来各种查,改成strleh和strgth才好使了。实在搞不懂为什么自己反汇编出来的代码自己都编不过,弄不懂。
在这个工作当中,学到了一些东西,比如cmp指令,用来做if跳转分支,cmp后面会接一些条件判断指令,le和gt表示<=和>还有两个我记不清了,反正都挺无聊的。前面的那个错误也是在cmp后面做判断用的,str指令用来保存数据到内存,h表示是半字,le和gt表示大于还是小于,所以是顺序没排对,而不是两条不同的指令。