freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

安卓学习思路方法总结(六)
FreeBuf_363224 2021-08-30 18:12:32 48008

安卓学习(一)
安卓学习思路方法总结(二)
安卓学习思路方法总结(三)
安卓学习思路方法总结(四)
安卓学习思路方法总结(五)

我们继续

NDK动态

image-20210825204958774

Eclipse创建工程

image-20210825180830127

image-20210825205317633

然后一路默认 Finish即可

这个 前面讲过

image-20210825205443962

去掉Hello world

我们今天的目标是做一个计算机

计算机

放入两个Pl*in Test

image-20210825205718536

在放入两个TestView

image-20210825205838196

选中之后 修改文本

image-20210825205950979

image-20210825210016724

导入四个Button 做运算符

image-20210825210137726

同样修改名字

image-20210825210318474

进入xml底层 进行查看

双击即可

image-20210825210350292

修改ID信息

+:add
-:sub
*:mul
/:div

image-20210825210722930

进行保存

把位置重新规划好

image-20210825210927558

image-20210825210945400

Java代码编写

创建一个方法进行初始化控件

image-20210825211133103

init()初始化控件

image-20210825211520439

绑定编辑框的变量

定义first变量

绑定编辑框

定义好类型:EditText

image-20210825212217300

对应xml文件

image-20210825212159046

定义第二个编辑框

image-20210825212520802

对应xml文档

image-20210825212543543

然后绑定Button类型的运算符

image-20210825213053128

对应xml文档

image-20210825213128145

一样的方法 绑定其他三个运算符

image-20210825213814141

对应xml文档

image-20210825213303479

定义两个全局变量

image-20210825213848855

定义native方法

native方法的实现是在so

image-20210825213954695

获取编辑框中的值

one

使用getText转到toString

image-20210825214150172

two也是一样的

image-20210825214558480

定义运算方法

image-20210825214722217

监听绑定按钮

new一个OnClickListener

通过onClick进行监听

逻辑是通过ID来执行命令操作

image-20210825220650830

接收按钮Id

key->v中getId的按钮

value是add按钮的ID

image-20210825220737284

继续添加其他三个运算符

image-20210825220943594

显示界面

需要去调用方法Toast

需要三个参数

之前讲述过 这里就不展开说了

image-20210825221119018

context参数:上下文使用 MainActivity中的
text参数:我们获取的add(one,tWo)
duration参数:使用show()进行显示

注:add这个类型是float类型,需要转换为string类型

加个字符串即可

image-20210825221417471

进行其他三个运算符的显示

image-20210826094931849

默认值的绑定

使用OnClickListener进行默认值的绑定

定义变量cli

修饰符:final

image-20210826095618006

我们在加减乘除的时候 都要用到编辑框

最后把编辑框放入case

image-20210826100028910

保存 即可

JNI层编写

创建JNI文件夹

image-20210826100503029

image-20210826100535091

image-20210826100552766

创建.c文件

image-20210826100645799

image-20210826100707250

写入头文件

image-20210826101131721

jfloat

使用jfloat 前两个参数是固定的 查万能表即可

然后使用方法add 后面两个是传入的参数

jfloat add(JNIEvn* env, jobject obj, jfloat a, jfloat b)

image-20210826101351992

方法体写入+

image-20210826101631094

一样的方法写入其他三个 运算符

image-20210826101814174

注:

方法名是不需要一样的

因为会使用JNI的接口进行两边函数的关联

image-20210826101832635

C层和Java层的绑定

使用结构体定义结构体数组:JNINativeMethod

JNINativeMethod nativeMethod[]{};

写入结构体:struct

查万能表

//定义一个动态注册JNINativeMethod结构体,这个与动态注册有关,里面有三个元素
typedef struct {
    const char* name; //第一个参数 name 是java 方法名;
    const char* signature; //第二个参数 signature 用于描述方法的参数与返回值,也就是java方法签名信息,
    void*       fnPtr; //第三个参数 fnPtr 是函数指针,指向 jni 函数;
} JNINativeMethod;

可以看到需要三个参数

1、java方法名称

2、参数、返回值、签名信息

3、函数指针
{"add","(FF)F",(void*)add},
{"sub","(FF)F",(void*)sub},
{"mul","(FF)F",(void*)mul},
{"div","(FF)F",(void*)div},

image-20210826102910356

定义函数

使用RegisterNatives

查万能表

jint        (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);

image-20210826103305811

四个参数 进行指出

image-20210826103421370

第二个参数jclass 需要调用FindClass

重新定义一个方法aaa

继续指出,定义参数,被修饰的方法的类路径

image-20210826103804176

image-20210826103913168

第三个参数 是定义的数组名称

第四个参数 是数组的数量

image-20210826104059864

条件判断

写入一个if条件判断

和0比较

满足条件,返回-1

不满足条件,返回0

查万能表可知

#define JNI_OK          (0)
#define JNI_ERR         (-1) 

image-20210826105409914

注:这里的数组个数jnit为4位

严谨的写法是:

sizeof(nativeMethod)/sizeof(nativeMethod[0])

image-20210826105613836

这个时候 注册函数 就写完了

写动态注册

JNI_Onload

继续 开始

写动态注册

使用JNI_Onload

查万能表可知

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);

image-20210826105853394

给方法体定义一个jni

jint        (*GetEnv)(JavaVM*, void**, jint);

GetEnv有三个参数

第一个参数VM:补全

第二个参数void**:二级指针,指向(&)env的地址

第三个参数jint:版本

最后 还需要进行指出

image-20210826110512020

条件判断

判断env是否获取成功

不等于0就返回-1

image-20210826110849838

继续对注册函数 进行条件判断

不等于0就返回-1

加上env参数

image-20210826111215210

最后添加一个返回版本

image-20210826111338421

so库

编写两个.mk文件即可

把之前的拿过来 就可以

Android.mk

LOCAL_PATH := $(call my-dir)   
include $(CLEAR_VARS)  
LOCAL_MODULE    := jsq 	#模块名称  
LOCAL_SRC_FILES := jsq.c #源文件  .c或者.cpp
LOCAL_ARM_MODE := arm #编译后的指令集 ARM指令
LOCAL_LDLIBS += -llog #依赖库    
include $(BUILD_SHARED_LIBRARY) #指定编译文件的类型

image-20210826122517544

注:so库要调用的编译文件类型是:BUILD_SHARED_LIBRARY

Application.mk

APP_ABI := x86 armeabi-v7a
APP_PL*TFORM := android-8

image-20210826111944458

简单排错

image-20210826123112338

image-20210826122839886

image-20210826124014949

编译

image-20210826124028143

刷新

image-20210826124044154

image-20210826124106700

java层调用so库

image-20210826124239000

ARM汇编

要去熟悉so库了,冲冲冲

丢入一个so文件

image-20210829230113402

so文件的位置:一个项目的libs目录下

image-20210829230302570

进制转换对应表

image-20210829232050612

一、跳转指令

B:表示程序跳转

1.B:无条件跳转
2.BL:带链接的无条件跳转
3.BX:带状态切换的无条件跳转 根据目标地址最低位切换状态(arm/thumb)
4.BLX:带链接和状态切换的无条件跳转
5.B 1oc_:地址
6.BEQ,BNE

二、存储器与寄存器交互数据指令核心

存储器、寄存器:用来存储数据

小数据:寄存器;

大数据:栈
存储器(主存,内存)
寄存器中放的数据:可以是字符串,可以是数,也可以是一个地址,它可以放各种类型的数据

存储地址单元:地址与地址中存在的值

1、LDR、STR

LDR:从存储器中加载数据到寄存器 ⬅ Load(从右到左)

STR:将寄存器的数据存储到存储器 ➡ Store(从左往右)

LDRR8,[R9,#4]
R8为待加载数据的寄存器,加载值为R9+0×4所指向的存储单元

就是把右边放到左边

[]如果没括号就是直接把值直接放入R8

举个例子

LDR PC, [SP+4+var_4]

解析

SP 栈指针寄存器,地址数据SP+4+var_4将被放进PC
STR R8,[R9,#4]:和LDR相反
将R8寄存器的数据存储到R9+0x4指向的存储单元

2、LDM、STM

LDM:将存储器的数据加载到一个寄存器列表 ➡ (从左往右)

LDM RO,{R1-R3}将R0指向的存储单元的数据`依次`加载到R1,R2,R3寄存器

LDR:从存储器中加载数据到寄存器←Load 
LDR:用在函数快结尾的地方,添加对象的地方

STM:将一个寄存器列表的数据存储到指定的存储器 ⬅(从右到左)

3、PUSH、POP

PUSH:将寄存器值推入堆栈   压栈
POP:将堆栈值推出到寄存器  岀栈

4、SWP SWP

SWP:将寄存器与存储器之间的数据进行交换

SWP R1,R1 [RO]将R1寄存器与R0指向的存储单元的内容进行交换

堆,队列:数据结构,栈是竖的,后进先岀,且只能从栈顶依次填入数据

三、数据传送指令

MOV:move 
MOV:将立即数或寄存器的数据传送到目标寄存器


MOV RO,#8  R0=8
把后面的#8值给前面的RO

四、数据算术运算指令

ADD(+),SUB(-),MUL(*),DIV(/)

涉及到:有符号,无符号运算,带进位运算

五、数据逻辑运算指令

与:AND    全1出1

两边为真,结果才为真

或:ORR    有1出1

有一个为真,结果就为真

异或:EOR  相同为零,不同为1
如果两个值相等就为零,不相等就为1
移位:实质是乘,除,类似于小数点移位

但与平常算数相反,小数点左移,数变小;小数点右移,数变大
逻辑移位

小数点左移数变大,小数点右移数变小,且按2的倍数进行,因为是2进制

LSL:逻辑左移
LSR:逻辑右移

LSL R0,R1҅#2 R0=R1*4 
LSR R0,R1҅#2 R0=R1*2

六、比较指令

CMP:比较
CMP R0 #0 
R0寄存器中的值与0比较

标志位:如z位,这个都可以在动态调试时,寄存器窗口看到

七、其他指令

协处理器指令:SWT(切换用户模式、
伪指令:DCB

八、寄存器寻址方式

用到的是数据传送指令

立即寻址:MOV RO,#1234
R0=0×1234

寄存器寻址:MOV R0,R1 
R0=R1

寄存器移位寻址:MOV RO,R1,LSL #2
R0=R1*4

寄存器间接寻址:LDR R0,[R1]
将R1寄存器中的值作为地址,取出地址中的值赋R0

寄存器间接基址偏移寻址:LDR R0,[R1,#-4]
将R1寄存器的值(-0×4)的值作为地址,取出地址中的值给RO

简单分析so库

image-20210830010140978

1、从存储器中加载括号里面的RO值给R3寄存器中
2、没括号就是数据,把括号內的数据直接放入R1寄存器里面
3、把{R4,LR}这两个寄存器里面的值依次放到SP指向的地址,有STM就会有LDM对应
4、ADD是加,R1+PC在把值放入R1里面
5、把(R3+#0x18)数据作为一个地址,然后取这个地址里面的值放入R3里面
6、MOV把R0里面的值放入R4里面
7、BLX是进行跳转
8、把R4作为一个地址取地址里面的值,放入R12
9、把(byte_DA4)数据放入R2里面
10、MOV这里是把#4的值放入R3里
11、BLX进行跳转,SUB减
12、最后MOV给值,LDM返回

ARM处理器寻址方式

前言

寻址方式是根据指令中给出的地址码字段来实现寻找真实操作数地址的方式
了解其中一些基础的

1、寄存器寻址

把R2值给R1,或者是把R1给R2在给R0

2、立即寻址

立即数,只能放在寄存器里面存放,立即数要以#为前缀,表示16进制的0x

3、寄存器偏移寻址

LSR:逻辑右移,寄存器中字的高端空出的位补0
ASR:算术右移,移位过程中保持符号位不变,即如果源操作数为正数,则字的高端空出的位补0,否则补1
ROR:循环右移,由字的低端移出的位填入字的高端空岀的位
RRX:带扩展的循环右移,操作数右移一位,高端空出的位用原C标志值填充。

image-20210830012146889

4、寄存器间接寻址

第二个参数都是方括号:[]

一个地址

直接取这个地址叫:直接寻址

取在该地址中取某个值叫:间接寻址

5、基址寻址

:#

后数字如果是1,就取该地址加1后的数值

后数字如果是-1,就是数值減1作为地址

6、多寄存器寻址

多寄存器寻址就是一次可以传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。

多寄存器寻址指令举例如下:

LDMIA R1!,{R2-R7,R12}->将R1单元中的数据读出到R2-R7,R12,R1自动加1
STMIA R0!,{R3-R6,R10}->将R3-R6,R10中的数据保存到R0指向的地址,R0自动加1

使用多寄存器寻址指令时,寄存器子集的顺序时由小到大的顺序排列,连续的寄存器可用一连接,否则,用,分隔书写

简单来讲

就是需要跳转的时候,把寄存器的值放在一个特定的寄存器里面,把这些值暂时存放起来

执行完特定的函数在将特定的寄存器里面的值取出来,然后在执行下一步操作

请注意:这里使用的技术仅用于学习知识目的,如果列出的技术用于其他任何目标,我概不负责

OK 这次就到这里

# android安全 # 移动安全 # Android # 安卓逆向
本文为 FreeBuf_363224 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
移动攻防
FreeBuf_363224 LV.4
这家伙太懒了,还未填写个人描述!
  • 16 文章数
  • 106 关注者
实战案例分析:安卓逆向反调试
2021-11-02
安卓学习思路方法总结(七)
2021-09-16
安卓学习思路方法总结(五)
2021-08-16
文章目录