安卓逆向——初读smali代码改写(二)

上一篇 

查阅 Dalvik虚拟机操作码  来阅读 smail 代码和改写

# instance fields 实例字段
# instance (标识字段)
# private (访问权限修饰符,表示私有的)
# :(冒号是字段名 与 字段类型的分隔符)
# Ljava/lang/String 表示字段类型

注意点:

  • invoke-direct 调用 直接方法 
  • 这种就相当于  net/bluelotus/tomorrow/easyandroid/Crackme 类 调用 GetFlag 方法 
  • Ljava/lang/String; 参数 ,形参类型-String 类型的参数 
  • {p0, v0} 这个 相当于实参,有俩个参数,但是形参只有一个参数,这种情况 第一个参数相当于this
invoke-direct {p0, v0}, Lnet/bluelotus/tomorrow/easyandroid/Crackme;->GetFlag(Ljava/lang/String;)Ljava/lang/String;
  • invoke-virtual 调用虚拟方法
  • 执行虚拟方法 ,就是 参数调用后面的方法getBytes
  • v2.getBytes() 
invoke-virtual {v2}, Ljava/lang/String;->getBytes()[B

Smail Code:


# .class 表示当前类  public 当前的类的修饰符
#  类的表示方式= L + 包名 + 类名
# L net/bluelotus/tomorrow/easyandroid/  Crackme
.class public Lnet/bluelotus/tomorrow/easyandroid/Crackme;
.super Ljava/lang/Object;  # .super 父类
.source "Crackme.java"     #  .source 表示源文件 .java文件


# instance fields 实例字段
# instance (标识字段)
# private (访问权限修饰符,表示私有的)
# str2(字段名)
# :(冒号是字段名 与 字段类型的分隔符)
# Ljava/lang/String 表示字段类型

# instance fields
.field private str2:Ljava/lang/String;


# direct methods
.method public constructor <init>()V
    .locals 1 # 局部使用 寄存器个数是 1,当前方法的v寄存器  v局部寄存器,p参数寄存器

    .prologue # 方法代码的逻辑开始的位置
    .line 22  # .java 文件的行号‘
    # 调用直接方法,构造方法  默认的 p0.<>init
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 21 # .java 文件的行号‘
    # 定义字符串,base64 编码
    const-string v0, "cGhyYWNrICBjdGYgMjAxNg=="

    # i 表示 操作实例字段  put 表示 set,设置字段的值
    # v0   p0表示 this指向 当前对象
    # p0.str2 = v0
    iput-object v0, p0, Lnet/bluelotus/tomorrow/easyandroid/Crackme;->str2:Ljava/lang/String;

    .line 23 # 行号
    # 定义字符串,base64 编码
    const-string v0, "sSNnx1UKbYrA1+MOrdtDTA=="

    # invoke-direct 表示调用 直接方法
    # {p0 , v0 } 需要传入的 参数
    # p0.GetFlag(v0)
    invoke-direct {p0, v0}, Lnet/bluelotus/tomorrow/easyandroid/Crackme;->GetFlag(Ljava/lang/String;)Ljava/lang/String;

    .line 24
    return-void
.end method

.method private GetFlag(Ljava/lang/String;)Ljava/lang/String;
    .locals 4           # 表示 当前方法 使用到的 局部变量 寄存器个数 是 4
    .param p1, "str"    # Ljava/lang/String;  方法参数     p 参数寄存器  v 局部寄存器

    .prologue           # 代码开始的位置
    const/4 v3, 0x0     # v3 = 0

    .line 27
    # invoke-virtual 调用虚拟方法
    # byte[] v2 = p1.getBytes();
    # {p1} 是 参数列表
    invoke-virtual {p1}, Ljava/lang/String;->getBytes()[B

    move-result-object v2  # move-result-object 移动上一次方法调用的对象引用返回值到vx。

    invoke-static {v2, v3}, Landroid/util/Base64;->decode([BI)[B

    move-result-object v0  # move-result-object 移动上一次方法调用的对象引用返回值到vx。

    .line 29
    .local v0, "content":[B # 给 v0 一个 别名
    new-instance v1, Ljava/lang/String;  # 实例化对象 v1 = String()

    # i 表示 操作实例字段  get,表示获取字段的值
    # v0   p0表示 this指向 当前对象
    # v2 = p0.str2
    iget-object v2, p0, Lnet/bluelotus/tomorrow/easyandroid/Crackme;->str2:Ljava/lang/String;

    # {v2} 这里的 v2 是参数,因为String是类对象,所以实例化调用getBytes --> new String(v2).getBytes();
    # virtual 执行虚拟方法  v2.getBytes()  虚拟方法是接参数后面
    invoke-virtual {v2}, Ljava/lang/String;->getBytes()[B

    move-result-object v2  # move-result-object 移动上一次方法调用的对象引用返回值到 v2(接受上一个方法的返回值)

    # static 执行静态方法 Base64.decode()  直接方法是接类后面,参数是直接方法的参数
    # Base64.decode(字节列表,整型)
    invoke-static {v2, v3}, Landroid/util/Base64;->decode([BI)[B

    move-result-object v2 # move-result-object 移动上一次方法调用的对象引用返回值到 v2 (接受上一个方法的返回值)

    # 直接看方法参数个数,如果不一致,第一过就是this或者某个类的对象
    # v1 是上面的String 实例化对象,这里init是初始化  new String(v2)
    invoke-direct {v1, v2}, Ljava/lang/String;-><init>([B)V

    .line 30 # java代码的 行号
    .local v1, "kk":Ljava/lang/String;  # 给 v1寄存器 设置 别名

    # 根据字段ID读取静态对象引用字段到vx。 v2 = System.out()
    sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;

    # direct 执行直接方法 Crackme.decrypt()  直接方法是接类后面,参数是直接方法的参数
    invoke-direct {p0, v0, v1}, Lnet/bluelotus/tomorrow/easyandroid/Crackme;->decrypt([BLjava/lang/String;)Ljava/lang/String;

    move-result-object v3   # move-result-object 移动上一次方法调用的对象引用返回值到 v3。

    # 虚拟方法就是第一个参数调用 后面的方法,其他的参数都是 方法的参数
    # virtual 执行 虚拟方法   v3 是 参数
    # v2.println(v3)
    invoke-virtual {v2, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 31 # java的代码 行数
    const/4 v2, 0x0  # 存入4位常量到vx。

    return-object v2 # 返回  0x0 常量 0

.end method

.method private decrypt([BLjava/lang/String;)Ljava/lang/String;
    .locals 8  # 表示 方法内 用到了 8个 寄存器
    .param p1, "content"    # [B  字节列表
    .param p2, "password"    # Ljava/lang/String;  # 字符串

    .prologue
    .line 35
    const/4 v4, 0x0 #存入4位常量到v4。

    .line 37
    .local v4, "m":Ljava/lang/String; # 设置 v4 寄存器的 别名

    :try_start_0  # try 异常机制  try_start 异常机制 开始的地方,后面会有 end 结束的地方

    # 虚拟方法就是第一个参数调用 后面的方法,其他的参数都是 方法的参数
    # virtual 执行 虚拟方法
    # p2.getBytes()
    invoke-virtual {p2}, Ljava/lang/String;->getBytes()[B
    # v3 来接受 v3 = p2.getBytes()
    move-result-object v3 #  # move-result-object 移动上一次方法调用的对象引用返回值到 v3(接受上一个方法的返回值)

    .line 38
    .local v3, "keyStr":[B   # 设置 v3 寄存器的 别名  keyStr

    # instance 实例化对象 SecretKeySpec 类
    new-instance v2, Ljavax/crypto/spec/SecretKeySpec;

    const-string v7, "AES" # 设置 常量 v7 为 AES

    # 直接方法  就是 类->方法  {方法的参数}
    # direct 执行 直接方法
    # v2 是 前面 SecretKeySpec 的实例化。v2.init() 初始化
    # v3 是 字节列表([B)  v7 是 字符串(Ljava/lang/String)  方法的返回是 V(代表没有返回值 )
    # v2 = SecretKeySpec(v3,v7)
    invoke-direct {v2, v3, v7}, Ljavax/crypto/spec/SecretKeySpec;-><init>([BLjava/lang/String;)V

    .line 39
    .local v2, "key":Ljavax/crypto/spec/SecretKeySpec; # 给 v2 寄存器一个 别名key:后面是寄存器的类型

    const-string v7, "AES/ECB/NoPadding"  # 定义一个 常量 v7 为  "AES/ECB/NoPadding"  字符串

    # invoke-static 执行静态方法
    # Cipher.getInstance(v7)    # 返回一个 Cipher 类型
    invoke-static {v7}, Ljavax/crypto/Cipher;->getInstance(Ljava/lang/String;)Ljavax/crypto/Cipher;

    # # move-result-object 移动上一次方法调用的对象引用返回值到 v0 (接受上一个方法的返回值)
    # V0 =   Cipher.getInstance(v7)
    move-result-object v0

    .line 40
    .local v0, "cipher":Ljavax/crypto/Cipher;  # V0 寄存器 设置变量  cipher = Cipher.getInstance(v7)

    const/4 v7, 0x2 # 存入4位常量到v7。

    # 虚拟方法就是第一个参数调用 后面的方法,其他的参数都是 方法的参数
    # virtual 执行 虚拟方法
    # v0 就是 cipher  v7 是 个 0x2 的整型int ,v2 是 Ljava/security/Key 类型  V 方法没有返回值
    # cipher.init(v7,v2)   ##### --->>>> 注意 :初始化 是 <init>  ,这里 init 是方法
    invoke-virtual {v0, v7, v2}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;)V

    .line 41
    # 虚拟方法就是第一个参数调用 后面的方法,其他的参数都是 方法的参数
    # virtual 执行 虚拟方法
    # v0 就是 cipher  p1 是最开始 decrypt方法 擦传入的 字节列表 content
    # cipher.doFinal(p1)
    invoke-virtual {v0, p1}, Ljavax/crypto/Cipher;->doFinal([B)[B

    # # move-result-object 移动上一次方法调用的对象引用返回值到 v6 (接受上一个方法的返回值)
    # v6 = cipher.doFinal(p1)
    move-result-object v6

    .line 42
    .local v6, "result":[B  # 寄存器 v6 别名为 result ,类型是 [B 字节列表

    # 实例化的对象 ,v5 为 String实例对象
    new-instance v5, Ljava/lang/String;

    # 直接方法  就是 类->方法  {方法的参数}
    # init 执行 直接方法,初始化
    # v5 = new String(v6)
    invoke-direct {v5, v6}, Ljava/lang/String;-><init>([B)V


    :try_end_0 #  # try 异常机制  try_start 异常机制 结束的地方,于前面的start开始地方对应
    .catch Ljava/security/NoSuchAlgorithmException; {:try_start_0 .. :try_end_0} :catch_1
    .catch Ljavax/crypto/NoSuchPaddingException; {:try_start_0 .. :try_end_0} :catch_0
    .catch Ljava/security/InvalidKeyException; {:try_start_0 .. :try_end_0} :catch_4
    .catch Ljavax/crypto/IllegalBlockSizeException; {:try_start_0 .. :try_end_0} :catch_2
    .catch Ljavax/crypto/BadPaddingException; {:try_start_0 .. :try_end_0} :catch_3

    .end local v4    # "m":Ljava/lang/String;  # 起别名 结束

    .local v5, "m":Ljava/lang/String; # v5 别名 m

    move-object v4, v5  # 移动v5寄存器中的对象引用到v5。

    .line 46
    .end local v0    # "cipher":Ljavax/crypto/Cipher;
    .end local v2    # "key":Ljavax/crypto/spec/SecretKeySpec;
    .end local v3    # "keyStr":[B
    .end local v5    # "m":Ljava/lang/String;
    .end local v6    # "result":[B   # 起别名 结束

    .restart local v4    # "m":Ljava/lang/String;

    # 下面 都是 catch 的 异常出来跳转
    :goto_0
    return-object v4

    .line 43
    :catch_0
    move-exception v1

    .line 44
    .local v1, "e":Ljava/security/GeneralSecurityException;
    :goto_1
    invoke-virtual {v1}, Ljava/security/GeneralSecurityException;->printStackTrace()V

    goto :goto_0

    .line 43
    .end local v1    # "e":Ljava/security/GeneralSecurityException;
    :catch_1
    move-exception v1

    goto :goto_1

    :catch_2
    move-exception v1

    goto :goto_1

    :catch_3
    move-exception v1

    goto :goto_1

    :catch_4
    move-exception v1

    goto :goto_1
.end method

Java Code :

package com.smail_;

import java.util.Base64;
//import android.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Crackme01 {
    // 私有成员变量
    private String str2;

    public Crackme01(){
        str2 = "cGhyYWNrICBjdGYgMjAxNg==";
        GetFlag("sSNnx1UKbYrA1+MOrdtDTA==");
    }

    private String GetFlag(String str){
        byte[] v2 = str.getBytes();
        byte[] content =  Base64.getDecoder().decode(v2);
        byte[] bytes2 = this.str2.getBytes();
        String kk = new String(Base64.getDecoder().decode(bytes2));
        String flag = decrypt(content,kk);
        System.out.println(flag);
        return "";
    }

    private String decrypt(byte[] content,String password){
        try{
            byte[] keyStr = password.getBytes();
            SecretKeySpec key = new SecretKeySpec(keyStr,"AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
            cipher.init(2,key);
            byte[] result = cipher.doFinal(content);
            return new String(result);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    // 测试 程序入口
    public static void main(String[] args) {
        new Crackme01();
    }

}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.含笑.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值