乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

Charles抓取Https请求的相关配置Windows篇

发表于 2020-11-17

第一步添加SSL代理配置

添加代理主机

第二步 添加根证书

最后重启Charles即可抓包

补充

1.Charles Map rRemote

关于https的抓包, 我们在使用charles工具时还可以使用它的路径映射功能, 也就是自动将https替换成http协议

image-20220805142316685

image-20220805142357260

2.反编译全局替换

另外, 如果你是为了给App抓包, 刚好在反编译该App, 在条件允许的情况下, 可以在反编译smali后,批量对源码中的https进行替换 然后重新打包, 效果也不错

3.使用模拟器抓包

有些网站在手机上可以访问但是在电脑浏览器无法访问 大家懂的, 此时我们若通想通过浏览器控台提取相应的资源无法实现, 而且真机上的浏览器默认屏蔽了一部分https抓包功能 尤其是Android7.0以上版本机器

此时 我们可以考虑使用模拟器 比如网易模拟器 默认配备了Android6.0的系统, 低版本的系统相对来说限制要少很多

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Android进阶之逆向安全反编译视频教程-胖薯出品

发表于 2020-11-06

适用人群

  • 对安卓逆向反编译有兴趣的同学
  • 安卓逆向从业者

课程要求

适用于有安卓开发基础的同学

课程概述

学完本课程后可以实现对App进行逆向破解操作, 其中包括:

  1. app内容篡改
  2. app运行流程篡改
  3. app数据拦截和替换
  4. https数据拦截以及xposed hook操作

课程特色

15课时 帮你快速入门安卓逆向反编译

课程亮点

  • 适合新手入门 只要有一定的安卓开发基础就能快速掌握

  • 内容清晰循序渐进 不拖沓 全程干货

  • 配套工具 快速上手 一边学习一边实战

课程大纲

  • 第一部分: 反编译工具的基本介绍和使用

  • 第二部分: App启动入口替换

  • 第三部分: Https请求抓包

  • 第四部分: JNI数据分析

课程观看

  • 网易云课堂
  • 51CTO课堂
  • 面包多付费观看
  • Udemy课堂
  • B站免费观看
  • YouTube观看
  • 淘宝/拼多多赞助观看

作者介绍

移动端讲师、作家、开发者、独立音乐人

《smali语言从门到精通》《安卓进阶之逆向安卓反编译从0到1》《硬件进阶之三大架构汇编语言入门》系列视频作者 九年移动开发经验 旨在将复杂的事情简单化

欢迎大家来学习 一块进步~

1b954d951bca464294b5761a77fae70d

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

smali基本语法

发表于 2020-11-06

注释

1
2


类声明

1
2


方法声明

1
2


字段声明

1
2


字段取值赋值

1
2


方法调用

1
2


方法取值

1
2


包名和签名

发表于 2020-11-06

包名作用?

1
唯一标识

一台机器能否同时运行两个相同包名的不同应用?

1
2
3
不可以
原因: 一个包名代表一个应用, 相同包名系统认为是同一个应用, 会进行覆盖安装
原理: 签名不一致

一台机器能否同时运行两个相同签名的不同应用?

1
可以

历史问题:反编译为什么必须要修改包名?

1
成为一款独立的应用, 方便用户下载安装

Smali语言从入门到精通视频教程-胖薯出品

发表于 2020-11-06

适用人群

  • 对Smali语言以及逆向反编译有兴趣的同学
  • 安卓逆向从业者

课程要求

有任意一门编程语言的同学均可学习, 有Java或者安卓基础最佳

课程概述

安卓逆向必备基础知识 学完后快速掌握smali语法

课程特色

23课时 帮你快速入门Smali语言

课程亮点

  • 从0到1快速入门 基础语法开始 循序渐进

  • 内容清晰流畅 不废话不拖沓

  • 配套工具 快速上手 一边学习一边实战

课程大纲

  • 第一部分: Smali语言和Java语言的对比分析

  • 第二部分: 类和各种方法以及属性的定义

  • 第三部分: 寄存器的声明和定义

  • 第四部分: 手写Smali代码打印输出

课程观看

  • 网易云课堂
  • 51CTO课堂
  • 面包多付费观看
  • Udemy课堂
  • B站免费观看
  • YouTube观看
  • 淘宝/拼多多赞助观看

作者介绍

移动端讲师、作家、开发者、独立音乐人

《smali语言从门到精通》《安卓进阶之逆向安卓反编译从0到1》《硬件进阶之三大架构汇编语言入门》系列视频作者 九年移动开发经验 旨在将复杂的事情简单化

欢迎大家来学习 一块进步~

image-20240409102103326

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

smali语言之locals和registers的区别

发表于 2020-11-04

介绍

对于dalviks字节码寄存器都是32位的,它能够表示任何类型,2个寄存器用于表示64位的类型(Long and Double)。

作用

声明于方法内部(必须)

1
2
3
4
5
.method public getName()V
.registers 6

return-void
.end method

.registers和locals基本区别

在一个方法(method)中有两中方式指定有多少个可用的寄存器。指令.registers指令指定了在这个方法中有多少个可用的寄存器,

指令.locals指明了在这个方法中非参(non-parameter)寄存器的数量。然而寄存器的总数也包括保存方法参数的寄存器。

参数是如何传递的?

1.如果是非静态方法

例如,你写了一个非静态方法LMyObject;->callMe(II)V。这个方法有2个int参数,但在这两个整型参数前面还有一个隐藏的参数LMyObject;也就是当前对象的引用,所以这个方法总共有3个参数。
假如在一个方法中包含了五个寄存器(V0-V4),如下:

1
2
3
4
5
6
.method public callMe(II)V
const-string v0,"1"
const-string v1,"1"

return-void
.end method

那么只需用.register指令指定5个,或者使用.locals指令指定2个(2个local寄存器+3个参数寄存器)。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.method public callMe(II)V
.registers 5
const-string v0,"1"
const-string v1,"1"
v3==>p0
V4==>P1
V5==>P2

return-void
.end method

或者
.method public callMe(II)V
.locals 2
const-string v0,"1"
const-string v1,"1"
return-void
.end method

该方法被调用的时候,调用方法的对象(即this引用)会保存在V2中,第一个参数在V3中,第二个参数在v4中。

2.如果是静态方法

那么参数少了对象引用,除此之外和非静态原理相同,registers为4 locals依然是2

关于寄存器命名规则

v命名法

上面的例子中我们使用的是v命名法,也就是在本地寄存器后面依次添加参数寄存器,

但是这种命名方式存在一种问题:假如我后期想要修改方法体的内容,涉及到增加或者删除寄存器,由于v命名法需要排序的局限性,那么会造成大量代码的改动,有没有一种办法让我们只改动registers或者locals的值就可以了呢, 答案是:有的

除v命名法之外,还有一种命名法叫做p命名法

p命名法

p命名法只能给方法参数命名,不能给本地变量命名

假如有一个非静态方法如下:

1
.method public print(Ljava/lang/String;Ljava/lang/String;I)V

以下是p命名法参数对应表:

p0 this
p1 第一个参数Ljava/lang/String;
p2 第二个参数Ljava/lang/String;
p3 第三个参数I

如前面提到的,long和double类型都是64位,需要2个寄存器。当你引用参数的时候一定要记住,例如:你有一个非静态方法

1
LMyObject;->MyMethod(IJZ)V

方法的参数为int、long、bool。所以这个方法的所有参数需要5个寄存器。

p0 this
p1 I
p2, p3 J
p4 Z

另外当你调用方法后,你必须在寄存器列表,调用指令中指明,两个寄存器保存了double-wide宽度的参数。

注意:在默认的baksmali中,参数寄存器将使用P命名方式,如果出于某种原因你要禁用P命名方式,而要强制使用V命名方式,应当使用-p/–no-parameter-registers选项。

总结

  • locals和registers都可以表示寄存器数量,locals指定本地局部变量寄存器个数,registers是locals和参数寄存器数量的总数,两者使用任选其一
  • 同时,寄存器命名一共分两种,一种是v命名法,另一种是p命名法
v0 the first local register
v1 the second local register
v2 p0 the first parameter register
v3 p1 the second parameter register
v4 p2 the third parameter register

Android逆向开发之smali语言的学习

发表于 2020-11-03

smali和java基本数据类型对比

smali java
B byte
S short
I int
J long
F float
D double
C char
Z boolean
V void
[ 数组
L+全类名路径用/分割 object

注释

在smali语言中注释使用#表示

1
# 我是注释

类声明

1
.class +权限修饰符 +类名;

比如以下java代码:

1
2
3
public class Test
{
}

用smali代码表示为:

1
2
3
4
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.implements Ljava/lang/CharSequence; #如果实现了接口 则添加接口代码
.source "Test.java" # 源码文件 (非必须)

关于分号;

凡是L开头全包名路径结尾都需要加分号

字段声明(成员/全局变量)

1
.field 权限修饰符+静态修饰符 +变量名:变量全类名路径;

比如以下java代码:

1
2
3
4
public class Test
{
private static String a;
}

用smali代码表示为:

1
2
3
4
5
6
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.source "Test.java" # 源码文件 (非必须)

# 如果是非静态,只需将static去掉即可
.field private static a:Ljava/lang/String;

补充:

1
2
3
4
5
6
7
8
9
10
基本数据类型示例:
.method public final pubFinalMethod()V //返回值
.field private boType:Z // boolean
.field private byteType:B // byte
.field private shortType:S // short
.field private charType:C // char
.field private intType:I // int
.field private longType:J //long
.field private floatType:F // float
.field private doubleType:D // double

常量声明

1
.field 权限修饰符+静态修饰符 +final+变量名:变量全类名路径;=常量值

比如以下java代码:

1
2
3
4
public class Test
{
private static final String a=”hello“;
}

用smali代码表示为:

1
2
3
4
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)

.field public static final a:Ljava/lang/String; = "hello"

成员方法/函数声明

1
2
3
.method 权限修饰符+静态修饰符 +方法名(参数类型)返回值类型
#方法体
.end method #方法结尾标志

比如以下java代码:

1
2
3
4
5

public class Test
{
public static void getName(){}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)

# 如果是非静态,只需将static去掉即可
.method public static getName()V

return-void
.end method

如果是带参并且带有返回值的方法

比如以下java代码:

1
2
3
4
5
6
public class Test
{
public String getName(String p){
return "hello";
}
}

用smali代码表示为:

1
2
3
4
5
6
.method public getName(Ljava/lang/String;)Ljava/lang/String;

const-string v0, "hello"

return-object v0
.end method

关于方法返回关键字

主要有以下四种

1
2
3
4
return-void
return-object
return
return-wide

数据类型对应关系表如下:

smali方法返回关键字 java
return byte
return short
return int
return-wide long
return float
return-wide double
return char
return boolean
return-void void
return-object 数组
return-object object

构造方法/构造函数声明

1
2
3
.method 权限修饰符 +constructor <init>(参数类型)返回值类型
#方法体
.end method #方法结尾标志

比如以下java代码:

1
2
3
4
5
public class Test
{
public Test(String a){
}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public constructor <init>(Ljava/lang/String;)V

invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法

return-void
.end method

静态代码块的声明

1
2
3
.method static +constructor <clinit>()V
#方法体
.end method #方法结尾标志

比如以下java代码:

1
2
3
4
5
6
7
8
public class Test
{
public static String a="a";

static{

}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public static constructor <clinit>()V


return-void
.end method

方法调用

关键字

1
2
3
4
5
invoke-virtual //用于非私有实例方法的调用
invoke-direct //用于构造方法以及私有方法的调用
invoke-static //调用静态方法
invoke-super //调用父类的方法
invoke-interface //调用接口方法

非私有实例方法的调用

1
invoke-virtual {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

比如以下java代码:

1
2
3
4
5
6
7
8
9
public class Test
{
public Test(String a){
getName();
}
public String getName(){
return "hello";
}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public constructor <init>( Ljava/lang/String;)V

invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法
invoke-virtual {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法

return-void
.end method

#声明getName方法
.method public getName()Ljava/lang/String;

const-string v0, "hello"# 定义局部字符串常量

return-object v0 # 返回常量
.end method

私有方法或者构造方法的调用

1
invoke-direct {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

私有方法调用:

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
public class Test
{
public Test(String a){
getName();
}
//私有方法
private String getName(){
return "hello";
}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public constructor <init>(Ljava/lang/String;)V

invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法
invoke-direct {p0}, LTest;->getName()Ljava/lang/String;# 调用私有getName方法

return-void
.end method

#声明getName方法
.method private getName()Ljava/lang/String;

const-string v0, "hello"# 定义局部字符串常量

return-object v0 # 返回常量
.end method

构造方法调用:

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
11
public class Test
{
public Test(String a){
new Test2("hello");
}
public class Test2
{
public Test2(String a){
}
}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)

# 匿名内部类的声明
.annotation system Ldalvik/annotation/MemberClasses;
value = {
LTest$Test2;
}
.end annotation


# 构造方法
.method public constructor <init>(Ljava/lang/String;)V
# 初始化父类构造方法
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
# 创建对象
new-instance v0, LTest$Test2;
# 定义常量
const-string v1, "hello"
# 调用构造方法
invoke-direct {v0, p0, v1}, LTest$Test2;-><init>(LTest;Ljava/lang/String;)V

return-void
.end method

静态方法的调用并获取返回值(不区分私有公有 静态优先)

1
invoke-static {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
11
public class Test
{
public Test(String a){
String b=getName();
System.out.print(b);
}
private static String getName(){
return "hello";
}

}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public constructor <init>(Ljava/lang/String;)V

invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法
invoke-static {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法
move-result-object v0 #将返回值赋给v0
return-void
.end method

#声明getName方法
.method public getName()Ljava/lang/String;

const-string v0, "hello"# 定义局部字符串常量

return-object v0 # 返回常量
.end method

父类成员的方法调用

1
invoke-super

比如以下java代码

1
2
3
4
5
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

}

用smali代码表示为

1
2
3
4
5
6
.method protected onCreate(Landroid/os/Bundle;)V
.registers 2

invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
return-void
.end method

接口的调用

1
invoke-interface  {参数}, 方法所属类名;->方法名(参数类型)返回值类型;

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test
{
private InterTest a=new Test2();
public Test(String a){
}
public void setAa(){
InterTest aa=a;
# 调用接口方法
aa.est2();
}
public class Test2 implements InterTest
{
public Test2(){}

public void est2(){}
}
interface InterTest
{
public void est2();
}
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)


.method public constructor <init>(Ljava/lang/String;)V

invoke-direct {p0}, Ljava/lang/Object;-><init>()V #调用父类构造方法
invoke-static {p0}, LTest;->getName()Ljava/lang/String;# 调用普通成员getName方法

return-void
.end method

#声明setAagetName方法
.method public setAa()V
.registers 2


iget-object v0, p0, LTest;->a:LTest$InterTest;
# 调用接口方法
invoke-interface {v0}, LTest$InterTest;->est2()V

return-void
.end method

创建对象

对象的创建分多步进行:

1
2
3
4
# 声明实例
new-instance +变量名, 对象全包名路径;
# 调用构造方法 (如果构造方法内还定义了成员变量,那么在调用之前需要提前声明,然后在invoke的时候当作参数一并传入)
invoke-direct {变量名}, 对象全包名路径;-><init>(参数)返回类型
数组的创建
const/4 v0, 0x4
new-array v0, v0, [I

fill-array-data v0, :array_a

  :array_a
    .array-data 4 # 表示占用四个字节
        0x0
        0x1
        0x2
        0x3
    .end array-data

数据的定义

分三大类

1
2
3
字符串类型数据
字节码数据
数值型数据

数值类型数据拆分

1
2
3
4
5
6
7
8
9
10
第一种 const开头 占用一个容器(寄存器) 32位/容器
const v0,30
* const/4 最大只允许存放4位数值(4个二进制位) 1 111 7
* const/16 最大值允许存放16位数值 第一位默认为符号位 所以计算后15位的数值
* const 32位 最大32位
* const/high16 v0,0xFF7f0000


第二种 const-wide 占用两个容器 64位
const-wide v0,30 #占用v0和v1

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const-string  v0 , "hello"# 定义字符串 将字符串hello赋值给v0

const-class v0,LGoActivity; # 定义字节码对象 将GoActivity.class对象赋值给v0

# 以下数据定义高位默认为符号位
const/4 v0,0x2 # 定义一个容器 最大只允许存放半字节4位数据 取值范围为 -8 and 7
const/16 v0 , 0xABCD # 定义定义一个容器 最大只允许存放16位数据 比如short类型数据 取值范围为-32768~32767
const v0 , 0xA# 定义一个容器 最大只允许存放32位数据,比如int类型数据 将数字10赋值给v0 取值范围-2147483647~2147483647
const/high16 #定义一个容器 最大只允许存放高16位数值 比如0xFFFF0000末四位补0 存入高四位0XFFFF

# const-wide 占用两个寄存器vx和vx+1, 数值必须以L结尾 否则编译不通过
const-wide/16 # 定义两个相连容器 最大只允许存放16位数据
const-wide/32 # 定义两个相连容器 最大只允许存放32位数据
const-wide # 定义两个相连容器 最大只允许存放64位数据
const-wide/high16 # 定义两个相连容器 只允许存放高16位数据

数据取值范围算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1000 → -8;
1001 → -7;
1010 → -6;
1011 → -5;
1100 → -4;
1101 → -3;
1110 → -2;
1111 → -1;

0000 → 0;
0001 → 1;
0010 → 2;
0011 → 3;
0100 → 4;
0101 → 5;
0110 → 6;
0111 → 7。

算法:正数的符号位是0,负数的符号位是1。正数的反码、补码与原码一样。负数的反码是让符号位不变,数据位按位取反;补码是将反码加1。

静态字段赋值

分多步进行 关键代码:

1
sput-object # s代指static

比如以下java代码:

1
2
3
4
public class Test
{
private static String a=”hello“;
}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.source "Test.java" # 源码文件 (非必须)

# 声明静态字段
.field private static a:Ljava/lang/String;

#类初始化方法 被jvm执行 优先于构造方法
.method static constructor <clinit>()V

const-string v0, "hello"# 定义常量值

sput-object v0, LTest;->a:Ljava/lang/String;#常量赋值

return-void
.end method

类非静态字段赋值

分多步进行 关键代码:

1
iput-object  # i代表instance

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
11
public class Test
{
private String a="g";
public Test(String a){

}
public void setAa(){
a="b";
}

}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.source "Test.java" # 源码文件 (非必须)

# 声明字段
.field private a:Ljava/lang/String;

# 构造方法初始化值a="g"
.method public constructor <init>(Ljava/lang/String;)V
.registers 3
# 初始化父类构造方法
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
# 声明字符串内容
const-string v0, "g"
# 赋值
iput-object v0, p0, LTest;->a:Ljava/lang/String;


return-void
.end method

# 成员方法修改变量a="b"
.method public setAa()V
.registers 2

.prologue

const-string v0, "b"

iput-object v0, p0, LTest;->a:Ljava/lang/String;

return-void
.end method

静态字段取值

关键代码

1
sget-object # s代指static

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
public class Test
{
private static String a="hello";
public Test(String a){
}
public void getA(){
String aa=a;
}

}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.source "Test.java" # 源码文件 (非必须)

# 声明静态字段
.field private static a:Ljava/lang/String;

#类初始化方法 被jvm执行 优先于构造方法
.method static constructor <clinit>()V

const-string v0, "hello"# 定义常量值

sput-object v0, LTest;->a:Ljava/lang/String;#常量赋值

return-void
.end method

# 取值方法
.method public getA()V
.registers 2

# 静态字段取值
sget-object v0, LTest;->a:Ljava/lang/String;

return-void
.end method

类非静态字段取值

关键代码:

1
iget-object  # i代表instance

比如以下java代码:

1
2
3
4
5
6
7
8
9
10
public class Test
{
private String a="hello";
public Test(String a){
}
public void getA(){
String aa=a;
}

}

用smali代码表示为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
.class public LTest;#声明类 (必须)
.super Ljava/lang/Object;#声明父类 默认继承Object (必须)
.source "Test.java" # 源码文件 (非必须)

# 声明静态字段
.field private static a:Ljava/lang/String;

#构造方法
.method public constructor <init>(Ljava/lang/String;)V
.registers 3

.prologue

invoke-direct {p0}, Ljava/lang/Object;-><init>()V

const-string v0, "hello"
# 初始化成员变量
iput-object v0, p0, LTest;->a:Ljava/lang/String;

return-void
.end method


# 取值方法
.method public getA()V
.registers 2

# 类非静态字段取值
iget-object v0, LTest;->a:Ljava/lang/String;

return-void
.end method

注意:以上取值赋值方法都是以String对象举例,如果是基本数据类型,那么按照如下表处理:

值定义

1
const/4 v0, 0x1 # 实例变量值内容定义 值皆为十六进制

取值:

1
2
iget #实例变量int型取值
sget #静态变量int型取值

赋值

1
2
iput #实例变量int型赋值
sput #静态变量int型赋值

下表以实例变量举例:

smali取值赋值和值定义关键字 java
iget-byte
iput-byte
const/4
byte
iget-short
iput-short
const/4
short
iget
iput
const/4
int
iget-wide
iput-wide
const-wide/16
long
iget-
iput
const/high16
float
iget-wide-
iput-wide
const/high16
double
iget-char-
iput-char
const/16
char
iget-boolean-
iput-boolean
const/4
boolean
#### 如果是基本数据类型,那么按照如下表处理:
smali取值赋值和值定义关键字 java
iget-object-
iput-object
new-array v0, v0, [数据类型签名

fill-array-data v0, :array_c
数组
iget-object-
iput-object

以下两步为类对象定义
new-instance v0, 全包名类路径;

invoke-direct #调用构造方法
类和接口
iget-object-
iput-object
sget-object
枚举
iget-object-
iput-object
const-string
String

以上表结果示例java代码如下,可自行试验:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test
{
private Test2 a=Test2.a;
public Test(String a){
}
public void setAa(){
Test2 aa=a;
}
public enum Test2
{
a,b;
}
}

$$

$$

1
2
3
4
5
6
7
8
9
10
public class Test
{
private String a="a";
public Test(String a){
}
public void setAa(){
String aa=a;
}

}

逻辑语句之条件跳转分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"if-eq vA, vB, :cond_**"  如果vA等于vB则跳转到:cond_** #equal

"if-ne vA, vB, :cond_**" 如果vA不等于vB则跳转到:cond_** # not equal

"if-lt vA, vB, :cond_**" 如果vA小于vB则跳转到:cond_** #less than

"if-ge vA, vB, :cond_**" 如果vA大于等于vB则跳转到:cond_** # greater equal

"if-gt vA, vB, :cond_**" 如果vA大于vB则跳转到:cond_** # greater than

"if-le vA, vB, :cond_**" 如果vA小于等于vB则跳转到:cond_** # less equal

"if-eqz vA, :cond_**" 如果vA等于0则跳转到:cond_** #zero
"if-nez vA, :cond_**" 如果vA不等于0则跳转到:cond_**
"if-ltz vA, :cond_**" 如果vA小于0则跳转到:cond_**
"if-gez vA, :cond_**" 如果vA大于等于0则跳转到:cond_**
"if-gtz vA, :cond_**" 如果vA大于0则跳转到:cond_**
"if-lez vA, :cond_**" 如果vA小于等于0则跳转到:cond_**

逻辑语句之循环

比如以下java代码

1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {

for(int i=0; i<3;i++){
}
}
}

对应的smali代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.method public static main([Ljava/lang/String;)V

const/4 v0, 0x0

:goto_1
const/4 v1, 0x3

if-ge v0, v1, :cond_7

add-int/lit8 v0, v0, 0x1 # 加法运算符 v0=v0+0x1

goto :goto_1

:cond_7
return-void
.end method

如果将int改成long, 结果又不一样,这里使用到了比较运算符cmp(comparable)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.method public static main([Ljava/lang/String;)V
.registers 5

.prologue
.line 4
const-wide/16 v0, 0x0

:goto_2
const-wide/16 v2, 0x3

cmp-long v2, v0, v2 # cmp-long为固定写法 如果v0大于v2 则返回1 赋值给v2 等于为0 小于则为-1

if-gez v2, :cond_c

const-wide/16 v2, 0x1

add-long/2addr v0, v2

goto :goto_2

.line 6
:cond_c
return-void
.end method

smali语法关键字

.line

表示与java源文件代码的映射关系,比如:

1
2
3
4
.line 3  # 代表以下代码还原成java代码在源文件第三行 
const/4 v0, 0x1

iput v0, p0, LTest;->a:I

删除该关键字不影响程序执行,该关键字在反编译时能很好地帮助我们阅读smali代码,以该关键字当作代码块的分割线,方便快速阅读执行内容

:cond_0

条件分支,配合if使用

.prologue

表示程序的开始 可省略

:goto_0

goto跳转分支,配合goto关键字使用

.local

显示局部变量别名信息,作用等同.line

1
2
move-result-object v0 # 调用方法后结果储存在v0中
.local v0, "b":Ljava/lang/String; # 局部变量v0别名为b 是一个String类型 也就是 String b=v0

.locals N

注意这个和上面local的区别多加了一个s

标明了你在这个函数中最少要用到的本地寄存器的个数 也即是指明了在这个方法中非参(non-parameter)寄存器的数量

locals和registers具体区别参见:点击跳转

.registers N

在Smali中,如果需要存储变量,必须先声明足够数量的寄存器,1个寄存器可以存储32位长度的类型,比如Int,而两个寄存器可以存储64位长度类型的数据,比如Long或Double

声明可使用的寄存器数量的方式为:.registers N,N代表需要的寄存器的总个数

示例:

1
2
3
4
5
6
7
8
9
.method private test(I)V
.registers 4 # 声明总共需要使用4个寄存器

const-string v0, "LOG" # 将v0寄存器赋值为字符串常量"LOG"

move v1, p1 # 将int型参数的值赋给v1寄存器

return-void
.end method

那么,如何确定需要使用的寄存器的个数?

由于非static方法,需要占用一个寄存器以保存this指针,那么这类方法的寄存器个数,最低就为1,如果还需要处理传入的参数,则需要再次叠加,此时还需要考虑Double和Float这种需要占用两个寄存器的参数类型,举例来看:

如果一个Java方法声明如下:

1
myMethod(int p1, float p2, boolean p3)1

那么对应的Smali则为:

1
method LMyObject;->myMethod(IJZ)V1

此时,寄存器的对应情况如下:

寄存器名称 对应的引用
p0 this
p1 int型的p1参数
p2, p3 float型的p2参数
p4 boolean型的p3参数

那么最少需要的寄存器个数则为:5

如果方法体内含有常量、变量等定义,则需要根据情况增加寄存器个数,数量只要满足需求,保证需要获取的值不被后面的赋值冲掉即可,方法有:存入类中的字段中(存入后,寄存器可被重新赋值),或者长期占用一个寄存器

寄存器数量只能多不能少

Dalvik指令集

如果需要使用Smali编写程序,还需要掌握常用的Dalvik虚拟机指令,其合集称为Dalvik指令集。这些指令有点类似x86汇编的指令,但指令更多,使用也非常简单方便。最详尽的介绍,可以参考Android官方的Dalvik相关文档:

https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions

一般的指令格式为:[op]-[type](可选)/[位宽,默认4位] [目标寄存器],[源寄存器](可选),比如:move v1,v2,move-wide/from16 v1,v2

这里也列举一些常用的指令,并结合Smali进行说明:

  • 移位操作:

此类操作常用于赋值

指令 说明
move v1,v2 将v2中的值移入到v1寄存器中(4位,支持int型)
move/from16 v1,v2 将16位的v2寄存器中的值移入到8位的v1寄存器中
move/16 v1,v2 将16位的v2寄存器中的值移入到16位的v1寄存器中
move-wide v1,v2 将寄存器对(一组,用于支持双字型)v2中的值移入到v1寄存器对中(4位,猜测支持float、double型)
move-wide/from16 v1,v2 将16位的v2寄存器对(一组)中的值移入到8位的v1寄存器中
move-wide/16 v1,v2 将16位的v2寄存器对(一组)中的值移入到16位的v1寄存器中
move-object v1,v2 将v2中的对象指针移入到v1寄存器中
move-object/from16 v1,v2 将16位的v2寄存器中的对象指针移入到v1(8位)寄存器中
move-object/16 v1,v2 将16位的v2寄存器中的对象指针移入到v1(16位)寄存器中
move-result v1 将这个指令的上一条指令计算结果,移入到v1寄存器中(需要配合invoke-static、invoke-virtual等指令使用)
move-result-object v1 将上条计算结果的对象指针移入v1寄存器
move-result-wide v1 将上条计算结果(双字)的对象指针移入v1寄存器
move-exception v1 将异常移入v1寄存器,用于捕获try-catch语句中的异常
  • 返回操作:

用于返回值,对应Java中的return语句

指令 说明
return-void 返回void,即直接返回
return v1 返回v1寄存器中的值
return-object v1 返回v1寄存器中的对象指针
return-wide v1 返回双字型结果给v1寄存器

另外

android studio自带的.class转smali就非常好用,如图:

从错误总学习

1
SLog.smali[24,4] Invalid register: v-1. Must be between v0 and v15, inclusive.

寄存器命名从v0-v15 一共15个

1
SLog.smali[17,0] A .registers or .locals directive must be present for a non-abstract/non-final method

.registers或者.locals必须存在, 除非是抽象方法或者final方法

1
2
java.lang.VerifyError: Rejecting class com.pangshu.SLog because it failed compile-time verification (declaration of 'com.pangshu.SLog' appears in /sdcard/ex.dex)
at com.pangshu.HelloTest.main(HelloTest.java)

这种错误一般很难定位,因为没有提示具体原因或者具体的行数,有可能是静态方法调用你写成了虚方法的调用,或者是构造函数调用没有加尖括号, 甚至是寄存器数量过少 等等

思考

为什么方法中包括参数在内需要3个寄存器,但是在定义的时候只写了两个却也不报错呢?

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.method public static print(Ljava/lang/String;)V
.registers 2 #不报错
.prologue

invoke-static {p0},Lcom/pangshu/SLog;->wrapTag(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1 #如果这个地方改成v2以上那么报错

# log---System.out.print()
sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;


# 方法调用
invoke-virtual {v0,v1},Ljava/io/PrintStream;->print(Ljava/lang/String;)V

return-void
.end method

答案是:系统会更具最大寄存器的位置进行判断,从v0到vN,数量必须大于N,

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

smali语言之const const/4 const/16 const/high16

发表于 2020-11-03

数据的定义

分三大类

1
2
3
字符串类型数据
字节码数据
数值型数据

数值类型数据拆分

1
2
3
4
5
6
7
8
9
10
第一种 const开头 占用一个容器(寄存器) 32位/容器
const v0,30
* const/4 最大只允许存放4位数值(4个二进制位) 1 111 7
* const/16 最大值允许存放16位数值 第一位默认为符号位 所以计算后15位的数值
* const 32位 最大32位
* const/high16 v0,0xFF7f0000


第二种 const-wide 占用两个容器 64位
const-wide v0,30 #占用v0和v1

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const-string  v0 , "hello"# 定义字符串 将字符串hello赋值给v0

const-class v0,LGoActivity; # 定义字节码对象 将GoActivity.class对象赋值给v0

# 以下数据定义高位默认为符号位
const/4 v0,0x2 # 定义一个容器 最大只允许存放半字节4位数据 取值范围为 -8 and 7
const/16 v0 , 0xABCD # 定义定义一个容器 最大只允许存放16位数据 比如short类型数据 取值范围为-32768~32767
const v0 , 0xA# 定义一个容器 最大只允许存放32位数据,比如int类型数据 将数字10赋值给v0 取值范围-2147483647~2147483647
const/high16 #定义一个容器 最大只允许存放高16位数值 比如0xFFFF0000末四位补0 存入高四位0XFFFF

# const-wide 占用两个寄存器vx和vx+1, 数值必须以L结尾 否则编译不通过
const-wide/16 # 定义两个相连容器 最大只允许存放16位数据
const-wide/32 # 定义两个相连容器 最大只允许存放32位数据
const-wide # 定义两个相连容器 最大只允许存放64位数据
const-wide/high16 # 定义两个相连容器 只允许存放高16位数据

详情参考:Android逆向开发之smali语言的学习

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

二进制与十六进制和八进制的快速转换

发表于 2020-11-02

进制转换属于计算机基础,虽然是基础,但是想要熟练计算,需要花点时间

二进制举例

1
0101001001001101    //转成十进制值是2^0+2^2+2^3+2^6+2^9+2^12+2^14=21069

二进制转十六进制

从低到高每四位进行分割,也就是:

1
0101 0010 0100 1101   //结果 524D

二进制转八进制

从低到高每三位进行分割,也就是:

1
0 101 001 001 001 101

不足三位的补0, 也就是:

1
000 101 001 001 001 101 结果051115

思考

二进制每四位(1111)最大值是15, 按照四位分割转成十六进制

二进制每三位(111)最大值是7,按照三位分割转八进制

那么延伸:

二进制每两位(11)最大值是3,按照两位分割转成四进制

二进制每五位(11111)最大值是31,按照五位分割转成三十二进制

二进制每六位(111111)最大值是63,按照六位分割转成六十四进制

二进制每七位(1111111)最大值是127,按照七位分割转成一百二十八进制

……..

如果快速转换

十六进制和二进制一一对应关系表:

十六进制 二进制
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
A 1010
B 1011
C 1100
D 1101
E 1110
F 1111

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

android逆向反编译工具包下载

发表于 2020-11-02

前言

该压缩包为《Android进阶之逆向安全反编译视频教程-胖薯出品》的配套工具包

工具包

该压缩包包含以下工具:

命令行工具

  • apktool2.4
  • dex2jar2.0

集成工具

  • jadx0.9
  • jd-gui.exe
  • apkIDE
  • 安卓逆向助手2.0

工具下载

点击进入

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

1…313233…50

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

491 日志
143 标签
RSS
© 2025 乱码三千
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%