这边没有目录 我们只以页码为读书标记
着实 读书是比较系统全面的学习方法,比如学习路线规划,弥补自己在哪些知识点的不足,从而针对性的学习。而不是盲目的CSDN搜索一篇,CSDN固然有全面的文章,有的部分也许只是点到为止,相信大家平时都有些CSDN的习惯,我们常常把他当做笔记来使用,以便以后翻阅,很难做到像教科书那样的全面讲解。
废话结束了。。。。入正题。
链接: https://pan.baidu.com/s/1jHWVR-At9qT3jn2zf1K4bw
提取码: veev
可以参考我的博客
直接参考这里即可 https://blog.csdn.net/qq_20330595/article/details/80995424
应smali将写好的smali文件转成dex
java -jar smali.jar -o Test.dex Test.smali
使用dex2jar 将Test.dex转成Java的jar包
使用dx将java的Test.jar转成可以dalvikvm虚拟机优化过的target.jar包
dx --dex --output=target.jar origin.jar
//将文件推到手机的指定路径下
adb push D:\下载\J2S2J1.3\work\target.jar /data/local
执行 dalvikvm虚拟机调试命令
adb shell dalvikvm -cp /data/local/target.jar Test
注意两点
dx路径参考https://blog.csdn.net/u012398902/article/details/50476580/
//Java2Smali
java <==javac==> class <==apkDB工具(放入新建文件夹Test中)==> jar(java) <==dx.bat==> jar(dalvikvm)
//相比上面这种更快
java <==javac==> class <==dx==> jar(dalvikvm)<==apkDB工具反编译==> jar文件夹<==apkDB工具E回编译==>Smali
//Smali2Java
Smali <==smali.jar==> dex <==baksmali-2.1.3.jar==> dex <==dex2jar==> jar <==jdg_ui==> java
以上涉及到的命令
dx --dex --output=
java -jar smali-2.1.3.jar smali/ -o classes.dex
d2j-dex2jar smaclasses.dex
java -jar smali.jar -o Test.dex Test.smali
java -jar baksmali-2.1.3.jar -o [输出文件夹] dex文件
这公司googl官方文档 不过需要科学上网
https://source.android.com/devices/tech/dalvik/dex-format
https://blog.csdn.net/u012889434/article/details/51848749
首先连接夜神模拟器
adb connect 127.0.0.1:62001
推送target.jar 文件
adb -s 127.0.0.1:62001 push D:\下载\J2S2J1.3\work\Test.dex /data/local
获取target.jar的dex头部信息
adb -s 127.0.0.1:62001 shell dexdump -f /data/local/target.jar
将信息导出到指定文件夹下(Windows下)
adb -s 127.0.0.1:62001 shell dexdump -f /data/local/target.jar >> D:\target\target.txt
PS:
Opened '/data/local/target.jar', DEX version '035'
DEX file header:
magic : 'dex\n035\0'
checksum : 7cbf5a00
signature : 8fc2...8228
file_size : 696
header_size : 112
link_size : 0
link_off : 0 (0x000000)
string_ids_size : 13
string_ids_off : 112 (0x000070)
type_ids_size : 7
type_ids_off : 164 (0x0000a4)
proto_ids_size : 3
proto_ids_off : 192 (0x0000c0)
field_ids_size : 1
field_ids_off : 228 (0x0000e4)
method_ids_size : 4
method_ids_off : 236 (0x0000ec)
class_defs_size : 1
class_defs_off : 268 (0x00010c)
data_size : 396
data_off : 300 (0x00012c)
Class #0 -
Class descriptor : 'LTest;'
Access flags : 0x0001 (PUBLIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in LTest;)
name : ''
type : '()V'
access : 0x10001 (PUBLIC CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
catches : (none)
positions :
locals :
#1 : (in LTest;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 3
ins : 1
outs : 2
insns size : 8 16-bit code units
catches : (none)
positions :
locals :
Virtual methods -
source_file_idx : -1 (unknown)
https://blog.csdn.net/qq_35874273/article/details/78524323
物理地址=段地址*16(10进制)+偏移地址。(2进制的话,也就是段地址<<4 | 偏移地址)。
https://blog.csdn.net/sinat_18268881/article/details/55832757
010Editor不仅自动寻找偏移地址 还能显示数据结构 这个是很多博主没有告诉大家的,也是初学者的隐痛
上面的大仙是郭霖推荐的深度好文
经分析 我的helloworld 偏移地址为
0C 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 00
OC为偏移起始位置 大小为12 表示有十二个字符
最后一位为00 表示字符串结尾 没有加入计算。
Ps 左边是[角标 10进制的哦
struct type_id_list | 7 types |
---|---|
2 | Test |
3 | java.io.PrintStream |
4 | java.lang.Object |
5 | java.lang.String |
6 | java.lang.System |
8 | void |
A | java.lang.String[] |
struct string_id_list | 14strings |
---|---|
0 | |
1 | Hello World! |
2 | LTest; |
3 | java.io.PrintStream; |
4 | java.lang.Object; |
5 | java.lang.String; |
6 | java.lang.System; |
8 | V(void;) |
9 | VL |
7 | Test.java |
A | [Ljava/lang/String; |
B | main |
C | out |
D | println |
这个不简单 下面看下数据结构可知共介绍了三个参数
shortyIdx 方法内总共的成员变量数目
returnTypeIdx 返回类型
parametersOff 参数列表 记录最终参数typeIdx的偏移地址
struct DexProtoId{
u4 shortyIdx; /*指向DexStringId列表的索引*/
u4 returnTypeIdx; /*指向DexTypeId列表的索引*/
u4 parametersOff; /*指向DexTypeList的位置偏移*/
}
struct DexTypeList{
u4 size; /*DexTypeItem的个数*/
DexTypeItem list[1]; /*DexTypeItem结构*/
}
struct DexTypeItem{
u2 typeIdx; /*指向DexTypeId列表的索引*/
}
struct proto_id_list | 3 prototypes |
---|---|
0 | void() |
1 | void (java.lang.String) |
2 | void (java.lang.String[]) |
最后一个struct class_def_item_list是正真的代码部分 可直接生成smali代码 书中80页曾指出在Android4.0系统源码目录下,dalvik/docs/instruction-formats.html 可查找35c相关指令
mapoff 指明DexMapitem结构文件偏移
其中kDexType是个枚举类型
/* map item type codes */
enum {
kDexTypeHeaderItem = 0x0000,
kDexTypeStringIdItem = 0x0001,
kDexTypeTypeIdItem = 0x0002,
kDexTypeProtoIdItem = 0x0003,
kDexTypeFieldIdItem = 0x0004,
kDexTypeMethodIdItem = 0x0005,
kDexTypeClassDefItem = 0x0006,
kDexTypeMapList = 0x1000,
kDexTypeTypeList = 0x1001,
kDexTypeAnnotationSetRefList = 0x1002,
kDexTypeAnnotationSetItem = 0x1003,
kDexTypeClassDataItem = 0x2000,
kDexTypeCodeItem = 0x2001,
kDexTypeStringDataItem = 0x2002,
kDexTypeDebugInfoItem = 0x2003,
kDexTypeAnnotationItem = 0x2004,
kDexTypeEncodedArrayItem = 0x2005,
kDexTypeAnnotationsDirectoryItem = 0x2006,
};
那么现在 我们大概掌握了DEX的基本结构以及分析方法和工具
来我们跟着作者玩一遍。
还是照文章看看操作先
这边操作失败了 大概是模拟器的问题 以后有空再看吧
注意以下几点
使用IDA pro 打开的是smali文件 需要有smali基础
checksum这个需要重新计算
MATE-INF手动删除,需要自己手动打包
关于Smali的静态语言分析可参考我的博客 smali语言入门 操作流
我们自己练习一下smali
public class Test {
public String name;
public void main(String[] args) {
Test test = new Test();
int i = 1;
switch(i){
case 0:
System.out.println("Hello World!");
break;
case 1:
test.name = "maqi1";
break;
case 2:
test.name = "maqi2";
break;
}
}
}
.line 4
new-instance v0, LTest;
invoke-direct {v0}, LTest;->()V
.line 5
const/4 v1, 0x1
.line 6
packed-switch v1, :pswitch_data_1c
.line 18
:goto_9
return-void
.line 8
:pswitch_a
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World!"
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
goto :goto_9
.line 11
:pswitch_12
const-string v1, "maqi1"
iput-object v1, v0, LTest;->name:Ljava/lang/String;
goto :goto_9
.line 14
:pswitch_17
const-string v1, "maqi2"
iput-object v1, v0, LTest;->name:Ljava/lang/String;
goto :goto_9
.line 6
:pswitch_data_1c
.packed-switch 0x0
:pswitch_a
:pswitch_12
:pswitch_17
.end packed-switch
注意以下几点
try{
Test test = new Test();
}catch(Exception e ){
e.getMessage();
}
.line 5
:try_start_0
new-instance v0, LTest;
invoke-direct {v0}, LTest;->()V
:try_end_5
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_5} :catch_6
.line 10
:goto_5
return-void
.line 6
:catch_6
move-exception v0
goto :goto_5
.line 7
invoke-virtual {v0}, Ljava/lang/Exception;->getMessage()Ljava/lang/String;
这里已经涉及到了汇编语言,所以想继续学习下去就必须先学习汇编语言。
这里分享我的 读书笔记-汇编语言第三版(王爽)给大家,非常全面,可供平时查阅。
这边的ARM和8086不同 又是新的指令 不过触类旁通
作者写了一行汇编代码 为了试下效果 我专门下了个MASM64
链接:https://pan.baidu.com/s/1C7aSw7zY6bjYY-M3sABi5A 密码:8liz
Export main ;表明main函数是被程序导出的
main ;函数名称
var_C = -0xC
bar_8 = -8 ;栈变量
STMFD = SP!,{R11,LR} ;堆栈寻址指令 入栈
ADD R11,SP,#4 ;(R11)=(SP)-4
SUB SP,SP,#8 ;SUB:减法指令 SP:堆栈指令 (SP) = (SP)-8 实际作用:分配8个字节的空间
STR R0,{R11,#var_8} ;写存储器访问指令(内存地址) R0保存到var_8
STR R1,{R11,#var_C} ;R1保存到var_C
LDR R3,={aHelloArm - 0x830} ;读存储器访问指令(内存地址)
ADD R3,PC,R3 ;"Hello ARM!"
MOV R0,R3 ;s
BL puts ;带链接的跳转指令 调用子程序功能,puts输入输出print
MOV R3,#0
MOV R0,R3
SUB SP,R11,#4
LDMFD SP!,{R11,PC} ;堆栈寻址指令 出栈
c语言到到可执行exe的过程
Android平台使用GNU ARM汇编格式,使用GAS(GNU Assembler)汇编器
手册地址 http://sourceware.org/binutils/docs/as/index.html
有人做了总结 直接当工具查看即可
https://blog.csdn.net/zqixiao_09/article/details/50726544
https://www.cnblogs.com/uestcbutcher/p/7244799.html
.global 函数名
.type 函数名,%function
函数名:
<...函数体...>
.global MyAdd
.type MyAdd,%function
MyAdd:
ADD r0,r0,r1 ;(r0) = (r0)+ ((r0)-(r1))
MOV pc,1r @函数返回
LSL:逻辑左移 空出低位补0,MOV R0,R1,LSL #2 :R1左移2位后赋值给R0寄存器,执行R0 = R1*4 这个不应该是16进制的吗?居然是二进制左移
LSR:逻辑右移 空出高位补0
ASR:算数右移 符号位不变
ROR:循环右移,低位填入高位
RRX:带扩展的循环右移 右移一位 高位用C标志的值填充
SWPB R1,R2,[R0]
B表示一个字节 高24位清空
(R1) = ([R0])
([R0]) = (R2)
type | 含义 |
---|---|
B | 无符号字节(零扩展成32位) |
SB | 有符号字节(符号扩展成32位) |
H | 无符号字节(零扩展成32位) |
SH | 有符号字节(符号展成32位) |
lable读取内存地址
label1 | 含义 |
---|---|
直接偏移量 | LDR R8,[R9,#04] |
寄存器偏移 | LDR R8,[R9,R10,#04] |
相对PC | LDR R8,label1 |
addr_mode | 含义 |
---|---|
IA | Increase After 基址寄存器执行后 增加 |
IB | Increase Before 基址寄存器执行前 增加 |
DA | Decrease After 基址寄存器执行后 减少 |
DB | Decrease After 基址寄存器执行前 减少 |
FD | 堆栈向低地址生长 SP– |
FA | 堆栈向高地址生长 SP++ |
ED | 堆栈向低地址生长 |
EA | 堆栈向高地址生长 |
指令 | 含义 | 模板 | 注意 |
---|---|---|---|
MOV | 传送指令 | MOV{cond}{s}Rd,operand2 | 将8位立即数或寄存器内容赋给目标寄存器 |
MVN | 数据非传送指令 | MVN{cond}{s}Rd,operand2 | 将8位立即数或寄存器内容取反赋给目标寄存器 |
ADD | 加法指令 | ADD{cond}{s}Rd,Rn,operand2 | 将Rn和operand2的值相加存入Rd中 |
ADC | 带进位的加法指令 | ADC{cond}{s}Rd,Rn,operand2 | 将Rn和operand2的值相加z再加上CPSR(汇编的FLAG)寄存器的C条件标志位的值最后存入Rd中 |
SUB | 减法指令 | SUB{cond}{s}Rd,Rn,operand2 | (Rd) = (Rn)-(operand2) 寄存器相减影响标志位 |
RSB | 逆向减法 | RSB{cond}{s}Rd,Rn,operand2 | (Rd) = (operand2)-(Rn) |
SBC | 带进位的减法指令 | SBC{cond}{s}Rd,Rn,operand2 | (Rd) = (Rn)-(operand2)-C |
RSC | 带进位的逆向减法 | RSC{cond}{s}Rd,Rn,operand2 | (Rd) = (Rn)-(operand2)-C |
MUL | 乘法指令 | MUL{cond}{S}Rd,Rm,Rn | 32位乘法 (Rd) = (Rm)*(Rn) S影响CPSR的N和X |
MLS | 先乘后减指令 | MLS{cond}{S}Rd,Rm,Rn,Ra | (Rd) =(Ra)- (Rm)*(Rn) |
UMULL | 先乘(当无符号数)后减指令 | MLS{cond}{S}Rd,Rm,Rn,Ra,低32位给Rd 高32位给Rm | |
UMLAL | 先乘(当无符号数)后加指令 | UMLAL{cond}{S}Rd,Rm,Rn,Ra,将结果于Rd和Rn组成的64位 | |
SMULL | 先乘(当有符号数)后减指令 | SMULL{cond}{S}Rd,Rm,Rn,Ra,低32位给Rd 高32位给Rm | |
SMLAL | 先乘(当有符号数)后加指令 | SMLAL{cond}{S}Rd,Rm,Rn,Ra,将结果于Rd和Rn组成的64位数相加,低32位给Rd 高32位给Rm | |
SMLAD | Rm的低半字节和Rn的低半字节乘,Rm的高半字节和Rn的高半字节乘,最后两个乘积与Ra相加 存入Rd | SMLAL{cond}{S}Rd,Rm,Rn,Ra | |
SMLSD | Rm的低半字节和Rn的低半字节乘,Rm的高半字节和Rn的高半字节乘,最后两个乘积相减最后与Ra相加 存入Rd | SMLSD{cond}{S}Rd,Rm,Rn,Ra | |
SDIV | 有符号的除法 | SDIV{cond}{S}Rd,Rm,Rn | (Rd) = (Rm)*(Rn) |
UDIV | 无符号的除法 | UDIV{cond}{S}Rd,Rm,Rn | |
ASR | 算数右移指令 将Rm右移operand2位,使用有符号数填充空位,保存到Rd | ASR{cond}{S}Rd,Rm,operand2 | |
AND | 逻辑与指令 将(Rd) = (Rm) & operand2 | AND{cond}{S}Rd,Rm,operand2 | 相同位1 |
ORR | 逻辑或指令 将(Rd) = (Rm) | operand2 | ORR{cond}{S}Rd,Rm,operand2 |
EOR | 逻辑异或指令 将(Rd) = (Rm) xor operand2 | EOR{cond}{S}Rd,Rm,operand2 | 不同为1 |
BIC | 清除指令 operand2取反后与Rm 存入 Rd | BIC{cond}{S}Rd,Rm,operand2 | |
LSL | 逻辑左移 Rm左移operand2位空位清零 存入 Rd | LSL{cond}{S}Rd,Rm,operand2 | |
LSR | 逻辑右移 Rm右移operand2位空位清零 存入 Rd | LSR{cond}{S}Rd,Rm,operand2 | |
ROR | 循环右移 Rm右移operand2位 右移出部分移到左边 结果存入 Rd | ROR{cond}{S}Rd,Rm,operand2 | |
RRX | 带扩展的循环右移 Rm右移1位 最高位用标志位值填充,结果存入 Rd | RRX{cond}{S}Rd,Rm | |
CMP | 比较指令 判断Rd-operand2的值是否为零 并设置标志位 结果不存入Rd | CMP{cond}Rd,operand2 | |
CMN | 判断Rd+operand2的值 并设置标志位 结果不存入Rd | CMN{cond}Rd,operand2 | |
TST | 位测试指令 判断Rd&operand2的值 并设置标志位 | TST{cond}Rd,operand2 | TST R0,#1 判断R0最低位是否为1 |
TEQ | 异或运算Rd xor operand2 并设置标志位 结果不存入Rd | TEQ{cond}Rd,operand2 | TEQ R0,R1 判断R0和R1的值是否相等 |
SWI | 软中断指令 实现用户模式到管理员模式的切换 | SWI{cond},immed_24 | immed_24为24位的中断号,R7存系统调用号,R0~R3传递系统调用的前四个参数,剩余使用堆栈传递 |
NOP | 空操作指令 用于空操作或字节对齐 | ||
MRS | 读状态寄存器指令 读取CPSR寄存器到Rd | MRS Rd CPSR | |
MSR | 写状态寄存器指令 | MSR Rd,psr_field,operand2 | psr 取值CPSR或者SPSR filed指定传送区域 |
field | 含义 |
---|---|
c | 控制域屏蔽字节 psr[7···0] |
x | 扩展域屏蔽字节 psr[15···8] |
s | 状态域屏蔽字节 psr[23···16] |
f | 标志域屏蔽字节 psr[31···24] |
MRS R0,CPSR @读取CPSR寄存器到R0寄存器中
BIC R0,R0,#0x80 @清除R0寄存器第7位
MSR CPSR_c,R0 @开启IRQ中断
MOV PC,LR @子程序返回
mov R0,#0 参数为0
MOV R7,#1 系统功能1为exit
SWI #0 执行exit(0)
D:\android_sdk\ndk-bundle\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin
D:\android_sdk\ndk-bundle\prebuilt\windows-x86_64\bin
make
make install
adb shell /data/local/hello
参考https://www.cnblogs.com/bingghost/p/5721423.html
#ndk根目录
NDK_ROOT=c:/android-ndk-r8
#编译器根目录
TOOLCHAINS_ROOT=$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows
#编译器目录
TOOLCHAINS_PREFIX=$(TOOLCHAINS_ROOT)/bin/arm-linux-androideabi
#头文件搜索路径
TOOLCHAINS_INCLUDE=$(TOOLCHAINS_ROOT)/lib/gcc/arm-linux-androideabi/4.4.3/include-fixed
#SDK根目录
PLATFORM_ROOT=$(NDK_ROOT)/platforms/android-14/arch-arm
#sdk头文件搜索路径
PLATFORM_INCLUDE=$(PLATFORM_ROOT)/usr/include
#sdk库文件搜索路径
PLATFORM_LIB=$(PLATFORM_ROOT)/usr/lib
#文件名称
MODULE_NAME=hello
#删除
RM=del
#编译选项 -nostdlib 是因为Google配置了自己的Bionic库
FLAGS=-I$(TOOLCHAINS_INCLUDE) \
-I$(PLATFORM_INCLUDE) \
-L$(PLATFORM_LIB) \
-nostdlib \
-lgcc \
-Bdynamic \
-lc
#所有obj文件
OBJS=$(MODULE_NAME).o \
$(PLATFORM_LIB)/crtbegin_dynamic.o \
$(PLATFORM_LIB)/crtend_android.o
#编译器链接
all:
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c $(MODULE_NAME).c -o $(MODULE_NAME).o
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) -o $(MODULE_NAME)
#删除所有.o文件
clean:
$(RM) *.o
#安装程序到手机
install:
adb push $(MODULE_NAME) /data/local/
adb shell chmod 755 /data/local/$(MODULE_NAME)
#运行程序
run:
adb shell /data/local/$(MODALE_NAME)
如上三个自定义的make命令步骤如下:
DOS下命令如下:
D:\gccTest>adb connect 127.0.0.1:62001
connected to 127.0.0.1:62001
D:\gccTest>make install
adb push hello /data/local/
hello: 1 file pushed. 3.0 MB/s (6188 bytes in 0.002s)
adb shell chmod 755 /data/local/hello
D:\gccTest> adb shell /data/local/tmp/hello
/system/bin/sh: /data/local/tmp/hello: not found
D:\gccTest> adb shell /data/local/hello
Hello ARM!
注意:
D:\gccTest>make
makefile:37: *** missing separator. Stop.
这个问题是run命令缩进必须为一个tab键,如下
参考 https://blog.csdn.net/limanjihe/article/details/52231243
run:
adb shell /data/local/$(MODALE_NAME)
如下使用真机错误 这个需要给root 并修改data目录chmod 777
D:\gccTest>make install
adb push hello /data/local/
adb: error: failed to copy 'hello' to '/data/local/hello': remote couldn't create file: Permission denied
hello: 0 files pushed. 0.1 MB/s (6188 bytes in 0.057s)
make: *** [install] Error 1
Android 命令行 目前只支持以下几种
Supported commands are:
android list target
android list avd
android list device
android create avd
android move avd
android delete avd
android list sdk
android update sdk
系统自带的.mk文件 D:\android-ndk-r10e\build\core
参考 点个灯
http://blog.sina.com.cn/s/blog_5de73d0b0102xql5.html
https://blog.csdn.net/yuanjize1996/article/details/54376228
LOCAL_PATH := $(call my-dir) ;定义本地源码路径 my-dir制定了调用my-dir宏 返回android.mk本身路径
include $(CLEAR_VARS) ;清理除"LOCAL_PATH"之外所有"LOCAL_",开头的变量CLEAR_VARS指向(NDK_ROOT)/build/core/clear-vars.mk;
LOCAL_ARM_MODE :=arm ;指定使用ARM指令模式 32位的arm指令系统
LOCAL_MODULE := hello ;指定模块名称 即原生程序生成后的文件名,如果是生成共享库模块,将会生成hello.so
LOCAL_SRC_FILES := hello.c ;指定C或者C++源文件列表,这里指hello.c 指定多个文件 用tab或空格隔开 或者用“\” 有层级关系需加上相应路径
include $(BUILD_EXECUTABLE) ;指定生成文件类型 分可执行(BUILD_EXECUTABLE) 动态(BUILD_SHARED_LIBRARY) (BUILD_STATIC_LIBRARY)静态库 指定生成文件类型,指向makefile((NDK_ROOT)/build/core/build-shared-library.mk)
在build_gradle中加入以下代码 自动编译生成so
sourceSets.main{
jni.srcDirs=[]//禁用as自动生成mk
}
task ndkBuild(type:Exec,description:'Compile JNI source via NDK'){
commandLine "E:\\android-ndk-r10b\\ndk-build.cmd",//配置ndk的路径
'NDK_PROJECT_PATH=build/intermediates/ndk',//ndk默认的生成so的文件
'NDK_LIBS_OUT=src/main/jniLibs',//配置的我们想要生成的so文件所在的位置
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',//指定项目以这个mk的方式
'NDK_APPLOCATION_MK=src/main/jni/Application.mk'//指定项目以这个mk的方式
}
tasks.withType(JavaCompile){//使用ndkBuild
compileTask ->compileTask.dependsOn ndkBuild
}
错误
call __ndk_info,Your APP_BUILD_SCRIPT points to an unknown file: $(APP_BUILD
//我自己写的jni目录写成jin了 我艹
编译错误
D:\androidSpace\HelloGcc\app\src\main\jin>ndk-build
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-14.
Android NDK: WARNING: APP_PLATFORM android-14 is higher than android:minSdkVersion 1 in D:/androidSpace/HelloGcc/app/src/main/AndroidManifest.xml. NDK
binaries will *not* be compatible with devices older than android-14. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_pro
blems.md for more information.
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: D:/androidSpace/HelloGcc/app/src/main/jni/Android.mk
D:/android_sdk/ndk-bundle/build//../build/core/add-application.mk:101: *** Android NDK: Aborting... . Stop.
查询参考 https://blog.csdn.net/xn4545945/article/details/9033925
ARM开发者中心
http://infocenter.arm.com/help/index.jsp
ELF官方文档
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf
两大工具
我的arm-linux-androideabi-objdump.exe路径
D:\android-ndk-r10e\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\arm-linux-androideabi-objdump.exe
反编译命令行
D:\android-ndk-r10e\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin>arm-linux-androideabi-objdump -S D:\gccTest\hello
输出
D:\gccTest\hello: file format elf32-littlearm
Disassembly of section .plt:
00008220 :
8220: e52de004 push {lr} ; (str lr, [sp, #-4]!)
8224: e59fe004 ldr lr, [pc, #4] ; 8230
8228: e08fe00e add lr, pc, lr
822c: e5bef008 ldr pc, [lr, #8]!
8230: 00001db8 ; instruction: 0x00001db8
00008234 :
8234: e28fc600 add ip, pc, #0, 12
8238: e28cca01 add ip, ip, #4096 ; 0x1000
823c: e5bcfdb8 ldr pc, [ip, #3512]! ; 0xdb8
00008240 <__libc_init@plt>:
8240: e28fc600 add ip, pc, #0, 12
8244: e28cca01 add ip, ip, #4096 ; 0x1000
8248: e5bcfdb0 ldr pc, [ip, #3504]! ; 0xdb0
0000824c <__cxa_atexit@plt>:
824c: e28fc600 add ip, pc, #0, 12
8250: e28cca01 add ip, ip, #4096 ; 0x1000
8254: e5bcfda8 ldr pc, [ip, #3496]! ; 0xda8
Disassembly of section .text:
00008258 :
8258: e92d4800 push {fp, lr}
825c: e28db004 add fp, sp, #4
8260: e24dd008 sub sp, sp, #8
8264: e50b0008 str r0, [fp, #-8]
8268: e50b100c str r1, [fp, #-12]
826c: e59f3018 ldr r3, [pc, #24] ; 828c
8270: e08f3003 add r3, pc, r3
8274: e1a00003 mov r0, r3
8278: ebffffed bl 8234
827c: e3a03000 mov r3, #0
8280: e1a00003 mov r0, r3
8284: e24bd004 sub sp, fp, #4
8288: e8bd8800 pop {fp, pc}
828c: 000000c8 .word 0x000000c8
00008290 <__atexit_handler_wrapper>:
8290: e3500000 cmp r0, #0
8294: e92d4008 push {r3, lr}
8298: 08bd8008 popeq {r3, pc}
829c: e12fff30 blx r0
82a0: e8bd8008 pop {r3, pc}
000082a4 <_start>:
82a4: e59fc05c ldr ip, [pc, #92] ; 8308 <_start+0x64>
82a8: e92d4800 push {fp, lr}
82ac: e59f2058 ldr r2, [pc, #88] ; 830c <_start+0x68>
82b0: e28db004 add fp, sp, #4
82b4: e24dd010 sub sp, sp, #16
82b8: e08fc00c add ip, pc, ip
82bc: e59f304c ldr r3, [pc, #76] ; 8310 <_start+0x6c>
82c0: e79c2002 ldr r2, [ip, r2]
82c4: e59f1048 ldr r1, [pc, #72] ; 8314 <_start+0x70>
82c8: e50b2014 str r2, [fp, #-20] ; 0xffffffec
82cc: e59f2044 ldr r2, [pc, #68] ; 8318 <_start+0x74>
82d0: e79c3003 ldr r3, [ip, r3]
82d4: e50b3010 str r3, [fp, #-16]
82d8: e59f303c ldr r3, [pc, #60] ; 831c <_start+0x78>
82dc: e79c1001 ldr r1, [ip, r1]
82e0: e50b100c str r1, [fp, #-12]
82e4: e79c2002 ldr r2, [ip, r2]
82e8: e50b2008 str r2, [fp, #-8]
82ec: e28b0004 add r0, fp, #4
82f0: e79c2003 ldr r2, [ip, r3]
82f4: e3a01000 mov r1, #0
82f8: e24b3014 sub r3, fp, #20
82fc: ebffffcf bl 8240 <__libc_init@plt>
8300: e24bd004 sub sp, fp, #4
8304: e8bd8800 pop {fp, pc}
8308: 00001d28 .word 0x00001d28
830c: ffffffec .word 0xffffffec
8310: fffffff0 .word 0xfffffff0
8314: fffffff4 .word 0xfffffff4
8318: fffffff8 .word 0xfffffff8
831c: fffffffc .word 0xfffffffc
00008320 :
8320: e1a01000 mov r1, r0
8324: e59f200c ldr r2, [pc, #12] ; 8338
8328: e59f000c ldr r0, [pc, #12] ; 833c
832c: e08f2002 add r2, pc, r2
8330: e08f0000 add r0, pc, r0
8334: eaffffc4 b 824c <__cxa_atexit@plt>
8338: 00001ccc .word 0x00001ccc
833c: ffffff58 .word 0xffffff58
源码
//
// Created by Administrator on 2018/7/31.
//
#include
int nums[5] = {1, 2, 3, 4, 5};
int for1(int n) {
int i;
int s = 0;
for (int i = 0; i < n; ++i) {
s += i * 2;
}
return s;
}
int for2(int n) {
int i;
int s = 0;
for (int i = 0; i < n; ++i) {
s += i *i+ nums[n-1];
}
return s;
}
int main(int argc, int **argv[]) {
printf("for1:%d\n",for1(5));
printf("for2:%d\n",for2(5));
return 0;
}
编译
make
报错
Cfors.c: In function 'for1':
Cfors.c:11:14: error: redeclaration of 'i' with no linkage
for (int i = 0; i < n; ++i) {
^
Cfors.c:9:9: note: previous declaration of 'i' was here
int i;
^
Cfors.c:11:5: error: 'for' loop initial declarations are only allowed in C99 or C11 mode
for (int i = 0; i < n; ++i) {
^
Cfors.c:11:5: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code
Cfors.c: In function 'for2':
Cfors.c:20:14: error: redeclaration of 'i' with no linkage
for (int i = 0; i < n; ++i) {
^
Cfors.c:18:9: note: previous declaration of 'i' was here
int i;
^
Cfors.c:20:5: error: 'for' loop initial declarations are only allowed in C99 or C11 mode
for (int i = 0; i < n; ++i) {
^
make: *** [all] Error 1
解决 在makefile的FLAGS中加入 -std=gnu11\(这里“\”起分隔符作用)
note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code
使用IDA打开Cfors文件
; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
EXPORT main
main
var_C= -0xC
var_8= -8 ;栈变量
STMFD SP!, {R11,LR} ;堆栈寻址指令 入栈
ADD R11, SP, #4 ;设置R11的值,作为栈帧指针使用
SUB SP, SP, #8 ;Sp = Sp-8 指针向下移动8个字节
STR R0, [R11,#var_8] ;将R0中的字数据写入以R11-8为地址的存储器中。
STR R1, [R11,#var_C]
MOV R0, #5 ;R0等于#5
BL for1 ;跳转for1处
MOV R2, R0 ;
LDR R3, =(aFor1D - 0x128);将(aFor1D - 0x128)的值直接赋给R3
ADD R3, PC, R3 ; "for1:%d\n" ;IDA双击aFor1D可查看该字符串
MOV R0, R3 ; format
MOV R1, R2
BL printf
MOV R0, #5
BL for2
MOV R2, R0
LDR R3, =(aFor2D - 0x148)
ADD R3, PC, R3 ; "for2:%d\n"
MOV R0, R3 ; format
MOV R1, R2
BL printf
MOV R3, #0
MOV R0, R3
SUB SP, R11, #4
LDMFD SP!, {R11,PC}
; End of function main
LDR r, =label 会把label表示的值加载到寄存器中,而LDR r,
label会把label当做地址,把label指向的地址中的值加载到寄存器中。
譬如 label的值是 0x8000,** LDR r, =label会将 0x8000加载到寄存器中**,而LDR r,
label则会将内存0x8000处的值加载到寄存器中。
for1方法区块如图;
我们对着C代码看
int for1(int n) {
int i;
int s = 0;
for (int i = 0; i < n; ++i) {
s += i * 2;
}
return s;
}
loc_44部分
loc_24部分
对应c代码
int for2(int n) {
int i;
int s = 0;
for (int i = 0; i < n; ++i) {
s += i *i+ nums[n-1];
}
return s;
}
在loc_94块中
var_C = i
var_10=n
var_8 =s
取nums
LDR R3, =(nums_ptr - _GLOBAL_OFFSET_TABLE_)
LDR R3, [R1,R3] ; nums
关于_GLOBAL_OFFSET_TABLE_这个参数 作者解释为:
静态常量统一保存在.got这样一个区块中。
R3定位到.got:0000019C
R2d定位到000001A0
.got:0000019C ; ===========================================================================
.got:0000019C
.got:0000019C ; Segment type: Pure data
.got:0000019C AREA .got, DATA, READONLY
.got:0000019C ; ORG 0x19C
.got:0000019C _GLOBAL_OFFSET_TABLE_ DCD 0 ; DATA XREF: for2+14↑o
.got:0000019C ; .text:off_F4↑o ...
.got:000001A0 nums_ptr DCD nums ; DATA XREF: for2+44↑r
.got:000001A0 ; .text:off_F8↑o
.got:000001A0 ; .got ends
.got:000001A0
nums的结构
.data:00000168 ; ===========================================================================
.data:00000168
.data:00000168 ; Segment type: Pure data
.data:00000168 AREA .data, DATA
.data:00000168 ; ORG 0x168
.data:00000168 EXPORT nums
.data:00000168 nums DCB 1 ; DATA XREF: for2+44↑o
.data:00000168 ; .got:nums_ptr↓o
.data:00000169 DCB 0
.data:0000016A DCB 0
.data:0000016B DCB 0
.data:0000016C DCB 2
.data:0000016D DCB 0
.data:0000016E DCB 0
.data:0000016F DCB 0
.data:00000170 DCB 3
.data:00000171 DCB 0
.data:00000172 DCB 0
.data:00000173 DCB 0
.data:00000174 DCB 4
.data:00000175 DCB 0
.data:00000176 DCB 0
.data:00000177 DCB 0
.data:00000178 DCB 5
.data:00000179 DCB 0
.data:0000017A DCB 0
.data:0000017B DCB 0
.data:0000017B ; .data ends
.data:0000017B
.bss:0000017C ; ===========================================================================
i*i
LDR R3, [R11,#var_C]
LDR R2, [R11,#var_C]
MUL R2, R3, R2
n-1
LDR R3, [R11,#var_10]
SUB R0, R3, #1
nums[n-1]
LDR R3, =(nums_ptr - _GLOBAL_OFFSET_TABLE_)
LDR R3, [R1,R3] ; nums
LDR R3, [R3,R0,LSL#2] ;R3 = R3+R0*4 看上面的角标就能够你理解角标移动n次等于偏移地址为n*4
s+=i*i+nums[n-1]
ADD R3, R2, R3 ;i*i+nums[n-1]
LDR R2, [R11,#var_8];R2 = ([R11,#var_8]) 即 R2 = s
ADD R3, R2, R3 ;R3 = (s += R2)
STR R3, [R11,#var_8] ;把新的s值存入[var_8]中
LDR R3, [R11,#var_C] ;R3的值重新置为i
ADD R3, R3, #1 ;i++
STR R3, [R11,#var_C] ;新的i存入[R11,#var_C]
main方法源码
int main(int argc, char* argv[]){
aclass *a = new aclass(3, 'c');
a->setM(5);
a->setC('a');
a->add(2, 8);
printf("%d\n", a->getM());
delete a;
return 0;
}
反编译代码如下:
CODE16
; int __cdecl main(int argc, const char **argv, const char **envp)
EXPORT main
main
; __unwind {
PUSH {R3-R5,LR} ;入栈 保护现场
MOVS R0, #8
BL sub_1BA8 ;operator new(uint)
MOVS R4, R0 ;保存分配的内存对象地址 也就是[R4]指向对象首地址
LDR R0, =(aConstructorCal - 0xCA6)
LDR R5, =(aD - 0xCAE)
ADD R0, PC ; "Constructor called."
BL sub_1BB8 ;operator new(uint)
MOVS R3, #5 ;m=5
ADD R5, PC ; "%d\n"
STR R3, [R4] ;([R4]) = R3 即 setM(5)
MOVS R3, #0x61 ; 'a'
STRB R3, [R4,#4] ;即设置对象aclass的第二个成员c,setC('a')
MOVS R0, R5 ;format
MOVS R1, #0xA ;add(2,8)=10=0xAH;
BL sub_1BC8 ;printf方法
LDR R1, [R4] ;
MOVS R0, R5
BL sub_1BC8
LDR R0, =(aDestructorCall - 0xCC8)
ADD R0, PC ; "Destructor called."
BL sub_1BB8
MOVS R0, R4
BL sub_1BD8
MOVS R0, #0
POP {R3-R5,PC} ;出栈 方法返回
; End of function main
这里的 sub_1BA8 即源代码的 new 操作 注意: _Znwj
.text:00001BA8 sub_1BA8 ; CODE XREF: main+4↑p
.text:00001BA8 BX PC
.text:00001BA8 ; ---------------------------------------------------------------------------
.text:00001BAA ALIGN 4
.text:00001BAC CODE32
.text:00001BAC
.text:00001BAC loc_1BAC ; CODE XREF: sub_1BA8↑j
.text:00001BAC LDR R12, =(_Znwj - 0x1BB8)
.text:00001BB0 ADD PC, R12, PC ; operator new(uint)
.text:00001BB0 ; End of function sub_1BA8
这里的 sub_1BC8 即源代码的printf
.text:00001BC8 sub_1BC8 ; CODE XREF: main+22↑p
.text:00001BC8 ; main+2A↑p
.text:00001BC8 BX PC
.text:00001BC8 ; ---------------------------------------------------------------------------
.text:00001BCA ALIGN 4
.text:00001BCC CODE32
.text:00001BCC
.text:00001BCC loc_1BCC ; CODE XREF: sub_1BC8↑j
.text:00001BCC LDR R12, =(printf - 0x1BD8)
.text:00001BD0 ADD PC, R12, PC ; printf
.text:00001BD0 ; End of function sub_1BC8
.text:00001BD0
.text:00001BD0 ; ---------------------------------------------------------------------------
这里的sub_1BD8 是析构函数 注意: _ZdlPv
.text:00001BD8 sub_1BD8 ; CODE XREF: main+38↑p
.text:00001BD8 BX PC
.text:00001BD8 ; ---------------------------------------------------------------------------
.text:00001BDA ALIGN 4
.text:00001BDC CODE32
.text:00001BDC
.text:00001BDC loc_1BDC ; CODE XREF: sub_1BD8↑j
.text:00001BDC LDR R12, =(_ZdlPv - 0x1BE8)
.text:00001BE0 ADD PC, R12, PC ; operator delete(void *)
.text:00001BE0 ; End of function sub_1BD8
答案:aclass总共两个成员变量,共占用8个字节的存储单元 其他的setM和setC方法
编程直接访问R4所指存储单元的代码。STRB R3, [R4,#4]即设置对象aclass的第二个成员c。