遇到过这种场景吗?

什么样的场景需要从 smali 语言层面接入第三方 sdk 呢?

一般都是 java 语言接口,一目了然,搞个 smali 不是没事找事?

其实不然

场景不同,我还真就遇到这种常规操作了 ———— 从 smali 接入 sdk

有一天

Leader 给你发一个 apk 文件

Leader:需要把我们的 sdk 接入到这个 apk 里面源码编辑器下载,但是没有 apk 的 Android 工程,能实现吗?

Programmer:应该可以,给我点时间研究下

Leade接口类型r:好,两天可以吧?我排期一下

Programmer:行

就这么简单:一个 apk,把我们的 sdk 接入到里面

不如写个 Demo 看看?

1、准备一个 sdk: gcsdk-1.0.0.jar

2、准备一个 apk: app-0.apk【这个是需要你接入 sdk 的 apk】

3、创建一个空白 Android 项目,预备接入 sdk: 生成的 apk 备用 app-1.apk

我们简单实现一个 sdk,实际上接入的第三方 sdk 可能会更复杂一点

  • gcsdk-1appearance.0.0 接入示例
// 初始化
GCSDK.getInstance().init(new InitCallback() {
            @Override
            public void initSuccess() {
                System.out.println("gcsdk-初始化成功");
            }
            @Override
            public void initFail(int code, String error) {
                System.out.println("gcsdk-初始化失败:code = " + code + "  error = " + error);
            }
});
//登录
GCSDK.getInstance().login(new LoginCallback() {
            @Override
            public void onLoginSuccess() {
                System.out.println("登录-成功");
            }
            @Override
            public void inLoginFail(int code, String error) {
                System.out.println("登录-失败:code = " + code + " error = " + error);
            }
});
//广告        
AdParams adParams = new AdParams();
GCSDK.getInstance().openAd(adParams, new AdCallback() {
            @Override
            public void onClick() {
                System.out.println("广告-点击");
            }
            @Override
            public void onClickSkip() {
                System.out.println("广告-点击跳过");
            }
            @Override
            public void onClose() {
                System.out.println("广告-关闭");
            }
            @Override
            public void onOpenFaild(int code, String error) {
                System.out.println("广告-打开失败:code = " + code + " error = " + error);
            }
            @Override
            public void onOpenSuccess() {
                System.out.println("广告-打开成功");
            }
            @Override
            public void onLoadBegin() {
                System.out.println("广告-加载开始");
            }
            @Override
            public void onLoadFaild(int code, String error) {
                System.out.println("广告-加载失败:code = " + code + " error = " + error);
            }
            @Override
            public void onLoadComplete() {
                System.out.println("广告-加载完成");
            }
            @Override
            public void onDownloadBegin() {
                System.out.println("广告-下载开始");
            }
            @Override
            public void onDownloadFail(int code, String error) {
                System.out.println("广告-下载失败:code =" + code + " error = " + error);
            }
            @Override
            public void onDownloadComplete() {
                System.out.println("广告-下载完成");
            }
});
//支付
PayParams payParams = new PayParams();
PayManager.getInstance().pay(payParams, new PayCallback() {
            @Override
            public void onPaySuccess() {
                System.out.println("支付-成功");
            }
            @Override
            public void onPayFail(int code, String error) {
                System.out.println("支付-失败:code = " + code + " error = " + error);
            }
});
  • 创建一个空白的 Android 项目,开始接入 gcsdk接口卡,接入完成后appointment打包生成 apkAPP 留着备用,用于获取接入 sdk 的 smali 代码

以下是接入示例:

// App.java
package com.example.gcsdkdemo;
import android.app.Application;
import android.util.Log;
import com.primer.jsonlili.callback.InitCallback;
import com.primer.jsonlili.core.GCSDK;
public class App extends Application {
    private final String TAG = "cunzhang";
    //初始化回调
    private InitCallback mInitCallback = new InitCallback() {
        @Override
        public void initSuccess() {
            Log.d(TAG, "initSuccess: ");
        }
        @Override
        public void initFail(int i, String s) {
            Log.d(TAG, "initFail: ");
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();
        //gcsdk 初始化
        GCSDK.getInstance().init(mInitCallback);
    }
}
//MainActivity.java
package com.example.gcsdkdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.primer.jsonlili.callback.AdCallback;
import com.primer.jsonlili.callback.LoginCallback;
import com.primer.jsonlili.callback.PayCallback;
import com.primer.jsonlili.core.GCSDK;
import com.primer.jsonlili.params.AdParams;
import com.primer.jsonlili.params.PayParams;
public class MainActivity extends AppCompatActivity {
    private final String TAG = "cunzhang";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void onPay(View view) {
        Log.d(TAG, "onPay: ");
        //支付接口
        PayParams payParams = new PayParams();
        GCSDK.getInstance().pay(payParams, new PayCallback() {
            @Override
            public void onPaySuccess() {
                Log.d(TAG, "onPaySuccess: ");
            }
            @Override
            public void onPayFail(int i, String s) {
                Log.d(TAG, "onPayFail: ");
            }
        });
    }
    public void onLogin(View view) {
        Log.d(TAG, "onLogin: ");
        //登录接口
        GCSDK.getInstance().login(new LoginCallback() {
            @Override
            public void onLoginSuccess() {
                Log.d(TAG, "onLoginSuccess: ");
            }
            @Override
            public void inLoginFail(int i, String s) {
                Log.d(TAG, "inLoginFail: ");
            }
        });
    }
    public void onOpenAd(View view) {
        Log.d(TAG, "onOpenAd: ");
        //广告接口
        AdParams adParams = new AdParams();
        GCSDK.getInstance().openAd(adParams, new AdCallback() {
            @Override
            public void onClick() {
                Log.d(TAG, "onClick: ");
            }
            @Override
            public void onClickSkip() {
                Log.d(TAG, "onClickSkip: ");
            }
            @Override
            public void onClose() {
                Log.d(TAG, "onClose: ");
            }
            @Override
            public void onOpenFaild(int i, String s) {
                Log.d(TAG, "onOpenFaild: ");
            }
            @Override
            public void onOpenSuccess() {
                Log.d(TAG, "onOpenSuccess: ");
            }
            @Override
            public void onLoadBegin() {
                Log.d(TAG, "onLoadBegin: ");
            }
            @Override
            public void onLoadFaild(int i, String s) {
                Log.d(TAG, "onLoadFaild: ");
            }
            @Override
            public void onLoadComplete() {
                Log.d(TAG, "onLoadComplete: ");
            }
            @Override
            public void onDownloadBegin() {
                Log.d(TAG, "onDownloadBegin: ");
            }
            @Override
            public void onDownloadFail(int i, String s) {
                Log.d(TAG, "onDownloadFail: ");
            }
            @Override
            public void onDownloadComplete() {
                Log.d(TAG, "onDownloadComplete: ");
            }
        });
    }
}

接入完毕后,以此触发按钮点源码网站击事件,效果如下:

从 smali 接入第三方 sdk

  • Leader 的 apk:

也模拟创建一个 apk,比较简单,每一个 fragment 有一个按钮,我们的目的也很明确 为每个按钮的点击事件实现对应 sd初始化失败是怎么解决k 的接口调用

从 smali 接入第三方 sdk


了解下项目中的 smali?

1、获得 smaliandroidstudio安装教程

java -jar apktool_2.6.0.jar [-r] d app-0.apk
java -jar apktool_2.6.0.jar [-r] d app-1.apk

Leader 的 apk:

从 smali 接入第三方 sdk

模拟接入 sdk 的 apk:

从 smali 接入第三方 sdk

2、简单了解项目的 smali 实APP

本次使用 VSCode 查看 smali 代码,同时安装插件 smali、smali2java

模拟接入 sdk 的 apk

  • App.java & App.smali

以下 smali 主要是列举与 sdk 相关,主要关注的是——sdk 接入相关代码生成的 s源码1688mali 具体有哪些、是如何结接口类型合使用的

package com.example.gcsdkdemo;
import android.app.Application;
import android.util.Log;
import com.primer.jsonlili.callback.InitCallback;
import com.primer.jsonlili.core.GCSDK;
public class App extends Application {
    private final String TAG = "cunzhang";
    //属性定义:.field private mInitCallback:Lcom/primer/jsonlili/callback/InitCallback;
    //类的 <init> 方法中创建对象并赋值:
    //new-instance v0, Lcom/example/gcsdkdemo/App$1;
    //invoke-direct {v0, p0}, Lcom/example/gcsdkdemo/App$1;-><init>(Lcom/example/gcsdkdemo/App;)V
    //iput-object v0, p0, Lcom/example/gcsdkdemo/App;->mInitCallback:Lcom/primer/jsonlili/callback/InitCallback;
    private InitCallback mInitCallback = new InitCallback() {
        @Override
        public void initSuccess() {
            Log.d(TAG, "initSuccess: ");
        }
        @Override
        public void initFail(int i, String s) {
            Log.d(TAG, "initFail: ");
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();
        //初始化,v0 当前类 this 引用传入内部类中,为内部类初始化赋值
        //invoke-static {}, Lcom/primer/jsonlili/core/GCSDK;->getInstance()Lcom/primer/jsonlili/core/GCSDK;
        //move-result-object v0
        //iget-object v1, p0, Lcom/example/gcsdkdemo/App;->mInitCallback:Lcom/primer/jsonlili/callback/InitCallback;
        //invoke-virtual {v0, v1}, Lcom/primer/jsonlili/core/GCSDK;->init(Lcom/primer/jsonlili/callback/InitCallback;)V
        GCSDK.getInstance().init(mInitCallback);
    }
}
  • InitCallback 内部类实现
.class Lcom/example/gcsdkdemo/App$1;
.super Ljava/lang/Object;
.source "App.java"
# interfaces
.implements Lcom/primer/jsonlili/callback/InitCallback;
# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
value = Lcom/example/gcsdkdemo/App;
.end annotation
# 内部类
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation
# 内部类持有外部类 this 引用
# instance fields
.field final synthetic this$0:Lcom/example/gcsdkdemo/App;
# direct methods
.method constructor <init>(Lcom/example/gcsdkdemo/App;)V
.locals 0
.param p1, "this$0" # Lcom/example/gcsdkdemo/App;
.line 13
iput-object p1, p0, Lcom/example/gcsdkdemo/App$1;->this$0:Lcom/example/gcsdkdemo/App;
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public initFail(ILjava/lang/String;)V
# 指定方法中可用的非参寄存器数量
.locals 2
.param p1, "i" # I
.param p2, "s" # Ljava/lang/String;
.line 21
const-string v0, "cunzhang"
const-string v1, "initFail: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 22
return-void
.end method
.method public initSuccess()V
.locals 2
.line 16
const-string v0, "cunzhang"
const-string v1, "initSuccess: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 17
return-void
.end method
  • MainActivity.java $ Mai源码时代nActivity.smali
package com.example.gcsdkdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.primer.jsonlili.callback.AdCallback;
import com.primer.jsonlili.callback.LoginCallback;
import com.primer.jsonlili.callback.PayCallback;
import com.primer.jsonlili.core.GCSDK;
import com.primer.jsonlili.params.AdParams;
import com.primer.jsonlili.params.PayParams;
public class MainActivity extends AppCompatActivity {
    private final String TAG = "cunzhang";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void onPay(View view) {
        Log.d(TAG, "onPay: ");
        //创建 PayParams 对象并存储到 v0,
        //new-instance v0, Lcom/primer/jsonlili/params/PayParams;
        //invoke-direct {v0}, Lcom/primer/jsonlili/params/PayParams;-><init>()V
        //.local v0, "payParams":Lcom/primer/jsonlili/params/PayParams;
        //invoke-static {}, Lcom/primer/jsonlili/core/GCSDK;->getInstance()Lcom/primer/jsonlili/core/GCSDK;
        //move-result-object v1
        //new-instance v2, Lcom/example/gcsdkdemo/MainActivity$1;
        //invoke-direct {v2, p0}, Lcom/example/gcsdkdemo/MainActivity$1;-><init>(Lcom/example/gcsdkdemo/MainActivity;)V
        //invoke-virtual {v1, v0, v2}, Lcom/primer/jsonlili/core/GCSDK;->pay(Lcom/primer/jsonlili/params/PayParams;Lcom/primer/jsonlili/callback/PayCallback;)V
        PayParams payParams = new PayParams();
        GCSDK.getInstance().pay(payParams, new PayCallback() {
            @Override
            public void onPaySuccess() {
                Log.d(TAG, "onPaySuccess: ");
            }
            @Override
            public void onPayFail(int i, String s) {
                Log.d(TAG, "onPayFail: ");
            }
        });
    }
    public void onLogin(View view) {
        Log.d(TAG, "onLogin: ");
        //调用 getInstance,把 getInstance 返回的对象存储到 v0,创建内部类 LoginCallback 对象,调用内部类初始化,调用登录方法
        //invoke-static {}, Lcom/primer/jsonlili/core/GCSDK;->getInstance()Lcom/primer/jsonlili/core/GCSDK;
        //move-result-object v0
        //new-instance v1, Lcom/example/gcsdkdemo/MainActivity$2;
        //invoke-direct {v1, p0}, Lcom/example/gcsdkdemo/MainActivity$2;-><init>(Lcom/example/gcsdkdemo/MainActivity;)V
        //invoke-virtual {v0, v1}, Lcom/primer/jsonlili/core/GCSDK;->login(Lcom/primer/jsonlili/callback/LoginCallback;)V
        GCSDK.getInstance().login(new LoginCallback() {
            @Override
            public void onLoginSuccess() {
                Log.d(TAG, "onLoginSuccess: ");
            }
            @Override
            public void inLoginFail(int i, String s) {
                Log.d(TAG, "inLoginFail: ");
            }
        });
    }
    public void onOpenAd(View view) {
        Log.d(TAG, "onOpenAd: ");
        //new-instance v0, Lcom/primer/jsonlili/params/AdParams;
        //invoke-direct {v0}, Lcom/primer/jsonlili/params/AdParams;-><init>()V
        //.line 60
        //.local v0, "adParams":Lcom/primer/jsonlili/params/AdParams;
        //invoke-static {}, Lcom/primer/jsonlili/core/GCSDK;->getInstance()Lcom/primer/jsonlili/core/GCSDK;
        //move-result-object v1
        //new-instance v2, Lcom/example/gcsdkdemo/MainActivity$3;
        //invoke-direct {v2, p0}, Lcom/example/gcsdkdemo/MainActivity$3;-><init>(Lcom/example/gcsdkdemo/MainActivity;)V
        //invoke-virtual {v1, v0, v2}, Lcom/primer/jsonlili/core/GCSDK;->openAd(Lcom/primer/jsonlili/params/AdParams;Lcom/primer/jsonlili/callback/AdCallback;)V
        AdParams adParams = new AdParams();
        GCSDK.getInstance().openAd(adParams, new AdCallback() {
            @Override
            public void onClick() {
                Log.d(TAG, "onClick: ");
            }
            @Override
            public void onClickSkip() {
                Log.d(TAG, "onClickSkip: ");
            }
            @Override
            public void onClose() {
                Log.d(TAG, "onClose: ");
            }
            @Override
            public void onOpenFaild(int i, String s) {
                Log.d(TAG, "onOpenFaild: ");
            }
            @Override
            public void onOpenSuccess() {
                Log.d(TAG, "onOpenSuccess: ");
            }
            @Override
            public void onLoadBegin() {
                Log.d(TAG, "onLoadBegin: ");
            }
            @Override
            public void onLoadFaild(int i, String s) {
                Log.d(TAG, "onLoadFaild: ");
            }
            @Override
            public void onLoadComplete() {
                Log.d(TAG, "onLoadComplete: ");
            }
            @Override
            public void onDownloadBegin() {
                Log.d(TAG, "onDownloadBegin: ");
            }
            @Override
            public void onDownloadFail(int i, String s) {
                Log.d(TAG, "onDownloadFail: ");
            }
            @Override
            public void onDownloadComplete() {
                Log.d(TAG, "onDownloadComplete: ");
            }
        });
    }
}
  • PayCallbaandroid平板电脑价格ck 内部类实现
.class Lcom/example/gcsdkdemo/MainActivity$1;
.super Ljava/lang/Object;
.source "MainActivity.java"
# interfaces
.implements Lcom/primer/jsonlili/callback/PayCallback;
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
value = Lcom/example/gcsdkdemo/MainActivity;->onPay(Landroid/view/View;)V
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation
# instance fields
.field final synthetic this$0:Lcom/example/gcsdkdemo/MainActivity;
# direct methods
.method constructor <init>(Lcom/example/gcsdkdemo/MainActivity;)V
.locals 0
.param p1, "this$0" # Lcom/example/gcsdkdemo/MainActivity;
.line 28
iput-object p1, p0, Lcom/example/gcsdkdemo/MainActivity$1;->this$0:Lcom/example/gcsdkdemo/MainActivity;
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public onPayFail(ILjava/lang/String;)V
.locals 2
.param p1, "i" # I
.param p2, "s" # Ljava/lang/String;
.line 36
const-string v0, "cunzhang"
const-string v1, "onPayFail: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 37
return-void
.end method
.method public onPaySuccess()V
.locals 2
.line 31
const-string v0, "cunzhang"
const-string v1, "onPaySuccess: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 32
return-void
.end method
  • 内部类 LoginCallback 实现
.class Lcom/example/gcsdkdemo/MainActivity$2;
.super Ljava/lang/Object;
.source "MainActivity.java"
# interfaces
.implements Lcom/primer/jsonlili/callback/LoginCallback;
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
value = Lcom/example/gcsdkdemo/MainActivity;->onLogin(Landroid/view/View;)V
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation
# instance fields
.field final synthetic this$0:Lcom/example/gcsdkdemo/MainActivity;
# direct methods
.method constructor <init>(Lcom/example/gcsdkdemo/MainActivity;)V
.locals 0
.param p1, "this$0" # Lcom/example/gcsdkdemo/MainActivity;
.line 43
iput-object p1, p0, Lcom/example/gcsdkdemo/MainActivity$2;->this$0:Lcom/example/gcsdkdemo/MainActivity;
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public inLoginFail(ILjava/lang/String;)V
.locals 2
.param p1, "i" # I
.param p2, "s" # Ljava/lang/String;
.line 51
const-string v0, "cunzhang"
const-string v1, "inLoginFail: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 52
return-void
.end method
.method public onLoginSuccess()V
.locals 2
.line 46
const-string v0, "cunzhang"
const-string v1, "onLoginSuccess: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 47
return-void
.end method
  • AdCallback 内部类实现
.class Lcom/example/gcsdkdemo/MainActivity$3;
.super Ljava/lang/Object;
.source "MainActivity.java"
# interfaces
.implements Lcom/primer/jsonlili/callback/AdCallback;
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
value = Lcom/example/gcsdkdemo/MainActivity;->onOpenAd(Landroid/view/View;)V
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation
# instance fields
.field final synthetic this$0:Lcom/example/gcsdkdemo/MainActivity;
# direct methods
.method constructor <init>(Lcom/example/gcsdkdemo/MainActivity;)V
.locals 0
.param p1, "this$0" # Lcom/example/gcsdkdemo/MainActivity;
.line 60
iput-object p1, p0, Lcom/example/gcsdkdemo/MainActivity$3;->this$0:Lcom/example/gcsdkdemo/MainActivity;
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public onClick()V
.locals 2
.line 63
const-string v0, "cunzhang"
const-string v1, "onClick: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 64
return-void
.end method
.method public onClickSkip()V
.locals 2
.line 68
const-string v0, "cunzhang"
const-string v1, "onClickSkip: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 69
return-void
.end method
.method public onClose()V
.locals 2
.line 73
const-string v0, "cunzhang"
const-string v1, "onClose: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 74
return-void
.end method
.method public onDownloadBegin()V
.locals 2
.line 103
const-string v0, "cunzhang"
const-string v1, "onDownloadBegin: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 104
return-void
.end method
.method public onDownloadComplete()V
.locals 2
.line 113
const-string v0, "cunzhang"
const-string v1, "onDownloadComplete: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 114
return-void
.end method
.method public onDownloadFail(ILjava/lang/String;)V
.locals 2
.param p1, "i" # I
.param p2, "s" # Ljava/lang/String;
.line 108
const-string v0, "cunzhang"
const-string v1, "onDownloadFail: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 109
return-void
.end method
.method public onLoadBegin()V
.locals 2
.line 88
const-string v0, "cunzhang"
const-string v1, "onLoadBegin: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 89
return-void
.end method
.method public onLoadComplete()V
.locals 2
.line 98
const-string v0, "cunzhang"
const-string v1, "onLoadComplete: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 99
return-void
.end method
.method public onLoadFaild(ILjava/lang/String;)V
.locals 2
.param p1, "i" # I
.param p2, "s" # Ljava/lang/String;
.line 93
const-string v0, "cunzhang"
const-string v1, "onLoadFaild: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 94
return-void
.end method
.method public onOpenFaild(ILjava/lang/String;)V
.locals 2
.param p1, "i" # I
.param p2, "s" # Ljava/lang/String;
.line 78
const-string v0, "cunzhang"
const-string v1, "onOpenFaild: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 79
return-void
.end method
.method public onOpenSuccess()V
.locals 2
.line 83
const-string v0, "cunzhang"
const-string v1, "onOpenSuccess: "
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 84
return-void
.end method

“日久生情”,代码看多了自然掌握的 smali 知识的就更多吧

Leader 的 apk

假如我们已知代码插入点(调用点源码编程器)位置————对应按钮的点击事件;那么我们更应该的点是——找到对应按钮的点击事件所在位置、如何正确插入 smali 代初始化电脑的后果

从 smali 接入第三方 sdk

要不 smali 接入看看?

1、先把 sdk 相关的所有 smali 源码复制到预备接入 apk 反编译的工程目录适当位置处

这里我新建 smali_classes9 目录,gcsdk 比初始化是什么意思较简单,只接口测试用例设计有纯代码,没有资源、soappstore 文件等;如果有,也需要复制到工程的相应目录下,确保项目能够编译成功、运行期间能加载到代码,这是项目能够运行的前提。

从 smali 接入第三方 sdk

下面就开始往预备接入的 apk 代码中合适位置插入 sdk smali 代码。

2、LeaderApp.java & LeaderApp.smali

.class public Lcom/example/leaderapp/ui/LeaderApp;
.super Landroid/app/Application;
.source "LeaderApp.java"
# instance fields
.field private final TAG:Ljava/lang/String;
# -----1、定义初始化回调字段 
.field private mInitCallback:Lcom/primer/jsonlili/callback/InitCallback;
# direct methods
.method public constructor <init>()V
 .locals 1
 .line 6
 invoke-direct {p0}, Landroid/app/Application;-><init>()V
 .line 7
 const-string v0, "leader"
 iput-object v0, p0, Lcom/example/leaderapp/ui/LeaderApp;->TAG:Ljava/lang/String;
# -----2、初始化方法中创建内部类对象
 new-instance v0, Lcom/example/leaderapp/ui/LeaderApp$1;
 invoke-direct {v0, p0}, Lcom/example/leaderapp/ui/LeaderApp$1;-><init>(Lcom/example/leaderapp/ui/LeaderApp;)V
 iput-object v0, p0, Lcom/example/leaderapp/ui/LeaderApp;->mInitCallback:Lcom/primer/jsonlili/callback/InitCallback;
 return-void
.end method
# virtual methods
.method public onCreate()V
 .locals 2
 .line 11
 invoke-super {p0}, Landroid/app/Application;->onCreate()V
 .line 12
 const-string v0, "leader"
 const-string v1, "onCreate: "
 invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
# -----3、调用初始化方法
 invoke-static {}, Lcom/primer/jsonlili/core/GCSDK;->getInstance()Lcom/primer/jsonlili/core/GCSDK;
 move-result-object v0
 iget-object v1, p0, Lcom/example/leaderapp/ui/LeaderApp;->mInitCallback:Lcom/primer/jsonlili/callback/InitCallback;
 invoke-virtual {v0, v1}, Lcom/primer/jsonlili/core/GCSDK;->init(Lcom/primer/jsonlili/callback/InitCallback;)V
 return-void
.end method
# -----4、创建 LeaderApp$1.smali,并更新路径、类等

插入完毕之后我们可以使用 VSCODE smali 插件代码转换查看是否插入初始化磁盘正确,可以做对比

从 smali 接入第三方 sdk

代码转换,原始项目和插入后的代码看效果是一致的

从 smali 接入第三方 sdk

手动签名、安装运行看日志,能看到 sdk 初始化正确,再次说明上述接入是无误的。

jarsigner -verbAPPose -keystore aa.keystore sign-app0.apk app-android平板电脑价格0.apk key0

从 smali 接入第三方 sdk

Tips:

  • 之前使用初始化电脑时出现问题 java -jar apktool.jar d ***.apk 反编译源码编辑器安装包,但在回编译时候可能回出现下面的错误,导致打包失败,日志中提到 res/目录,看日志像是资源问题。

W: invalid resource directory nam接口crc错误计数e: >/Users/jso源码nli/Desktop接口是什么/demo/0603/app-0/app-0/res navigati接口和抽象类的区别on brut.androlib.AndrolibExcep接口测试用例设计tion: brut.common.Br接口和抽象类的区别utException: could not exec (exit code = 1):

/Users/jsonli/Library/apktool/framework/1.apk, -S,

/Users/jsonli/Desktop/demo/0603/app-0/app-0/res, -M,

/Users/jsonli/Desktop/demo/0603/app-0/app-0/AndroidManifest.xml]

那么反编译时候不处理资源试试,apktool 加上 -r 参数java -jar apktool.jar -r d ***.apk,接着打包正常。

  • apktool 使用说明

-advance,–advanced prints advaapplence information.

-veandroid什么意思rsion,–version prints the接口类型 version then exits usage: apk源码编程器tool if|install-framework [options] <framewo初始化失败是怎么解决rk.apk&approachgt;

-p,–frame-pat源码精灵永久兑换码h

Storandroid下载安装es framework fil接口crc错误计数es into .

-t,–tag Tag frameworks using . usage: apktool d[ecode] [options] <file_apk>

-f,–force Fapproveorce delete destinandroid是什么手机牌子ation directory.

-o,–output

The name of folder that gets written. Default is apk.android是什么系统out

-p,–frame-path

Uses framework files located in .

-r,–no-res Do not decode resources.【对资源不做处理】

-s,–no-src Do not decode sources.

-tandroid什么意思,–frame-tag Uses framework files tagged by .usage: apktool b[uild] [options] <app_path>

-f,–force-all Skip changes detection and buildappstore all files.

-o,–接口测试ou初始化失败是怎么解决tput

The n接口是什么ame of apk that gets初始化电脑的后果 written. Default is d源码精灵永久兑换码ist/name.apk

-p,–frame-path

Uses framework files located in .

那继续完成剩下的接入吧 :)

3、HomeFandroid/harmonyosragment1源码1688.smali、HomeFandroid平板电脑价格rgment1.smali、HomeFrgment2.smali

HomeFragment$1.smali: button 点击事件实现类,在此调用登录

HomeFragment$2.smali: gcsdk 登录回调

.line: 行号说明,可以去掉,不影响代码执行,主要作用android的drawable类是说明代码在原始 Java 文件中的具体位置

.class Lcom/example/leaderapp/ui/home/HomeFragment$1;
.super Ljava/lang/Object;
.source "HomeFragment.java"
# interfaces
.implements Landroid/view/View$OnClickListener;
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
 value = Lcom/example/leaderapp/ui/home/HomeFragment;->onCreateView(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View;
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
 accessFlags = 0x0
 name = null
.end annotation
# instance fields
.field final synthetic this$0:Lcom/example/leaderapp/ui/home/HomeFragment;
# direct methods
.method constructor <init>(Lcom/example/leaderapp/ui/home/HomeFragment;)V
 .locals 0
 .param p1, "this$0" # Lcom/example/leaderapp/ui/home/HomeFragment;
 .line 40
 iput-object p1, p0, Lcom/example/leaderapp/ui/home/HomeFragment$1;->this$0:Lcom/example/leaderapp/ui/home/HomeFragment;
 invoke-direct {p0}, Ljava/lang/Object;-><init>()V
 return-void
.end method
# virtual methods
.method public onClick(Landroid/view/View;)V
 .locals 3
 .param p1, "view" # Landroid/view/View;
 .line 43
 invoke-virtual {p1}, Landroid/view/View;->getContext()Landroid/content/Context;
 move-result-object v0
 const-string v1, "u767bu5f55"
 const/4 v2, 0x0
 invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
 move-result-object v0
 invoke-virtual {v0}, Landroid/widget/Toast;->show()V
 .line 44
 const-string v0, "leader"
 const-string v1, "onClick: login"
 invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
# -----1、调用登录接口,修改类路径、内部类(登录回调实现类)
 invoke-static {}, Lcom/primer/jsonlili/core/GCSDK;->getInstance()Lcom/primer/jsonlili/core/GCSDK;
 move-result-object v0
 new-instance v1, Lcom/example/leaderapp/ui/home/HomeFragment$2;
# ----- 内部类持有外部类引用,这里的外部类是 button 的点击事件实现类,因此传入 HomeFragment$1,而不是 HomeFragment
 invoke-direct {v1, p0}, Lcom/example/leaderapp/ui/home/HomeFragment$2;-><init>(Lcom/example/leaderapp/ui/home/HomeFragment$1;)V
 invoke-virtual {v0, v1}, Lcom/primer/jsonlili/core/GCSDK;->login(Lcom/primer/jsonlili/callback/LoginCallback;)V
 return-void
.end method

.class Lcom/example/leaderapp/ui/home/HomeFragment$2;
.super Ljava/lang/Object;
.source "HomeFragment.java"
# -----2、创建内部类文件,并把对应的登录回调代码复制过来
# -----3、修改类路径 .class、.source、
# interfaces
.implements Lcom/primer/jsonlili/callback/LoginCallback;
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
 value = Lcom/example/leaderapp/ui/home/HomeFragment;->onLogin(Landroid/view/View;)V
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
 accessFlags = 0x0
 name = null
.end annotation
# 这里传入的外部类是 button 点击事件实现类,因此初始化函数和 this 类型应该是 
# instance fields
.field final synthetic this$0:Lcom/example/leaderapp/ui/home/HomeFragment$1;
# direct methods
.method constructor <init>(Lcom/example/leaderapp/ui/home/HomeFragment$1;)V
 .locals 0
 .param p1, "this$0"
# 这里也是 HomeFragment$1
 iput-object p1, p0, Lcom/example/leaderapp/ui/home/HomeFragment$2;->this$0:Lcom/example/leaderapp/ui/home/HomeFragment$1;
 invoke-direct {p0}, Ljava/lang/Object;-><init>()V
 return-void
.end method
# virtual methods
.method public inLoginFail(ILjava/lang/String;)V
 .locals 2
 .param p1, "i" # I
 .param p2, "s" # Ljava/lang/String;
 .line 51
 const-string v0, "cunzhang"
 const-string v1, "inLoginFail: "
 invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
 .line 52
 return-void
.end method
.method public onLoginSuccess()V
 .locals 2
 .line 46
 const-string v0, "cunzhang"
 const-string v1, "onLoginSuccess: "
 invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
 .line 47
 return-void
.end method

4、Notific接口crc错误计数ationsFragment.smali、D源码时代ashboardAndroidFragmeappreciatent.smali

因为代码简单,且逻辑一致,剩下的支付和接口类型广告接口代码就不贴出来了,跟上述接入十分相似,没application有比较特别的地方。

检验结果与思考

结果:

  • smali 代码android/harmonyos插入完毕、保存
  • 使用 apktool 重新打包
  • 使用签名工具重新签名

依此点击按钮,触发点击事件,运行结果如我所愿,一切正常。

从 smali 接入第三方 sdk


思考:

A:为什么搞 smali 接入这么复杂,这不是给自己找坑吗?源码编程器

B:有时候真的存在这种场景,也是无措之举…

A:既然你已有 apk,可以把它转换为 java 代码,在 java 代接口测试用例设计码上接入不更清晰、省事,免除遇到很接口是什么多未知的坑,咋不这么干?

Bapprove:好像初始化是什么意思…也 可 以?

A:我觉得可以,借助 Androidandroid平板电脑价格Fk 工初始化游戏启动器失败具可以直接从 apk 中反编译出 java 代码,当然————如果是加固、加密的 apk 可能就没那么容易了!!!

B:我觉得也是,在 java 代码上接入方便多了。

B:我是有点想不明白,我遇到的一家 CP 就是直接在 smali 上面接入的!难道说是技术过硬,哪里接入都一样?或者有什么特接口和抽象类的区别殊的场景初始化失败是怎么解决???

A:想不明白就不想了,有时间你可以试试我说的转 java 之后接入

B:好的,村长


A:如果接入的第三方 sdk 是一个 aAPPar,包含一些资源文件,接入会不初始化英文会遇到其他问题?

B:这不是没可能,实操过方知晓

A:那下次准备下

B:… …

smali 可能没有想象中的那么简单,

也可能就是这么简单,

之间的差距可能就是对 smali 的认识和理解深浅之别。