本文采用过时apktool动态调试功能.请采用apktool2.10以下版本(高版本废弃,后面第二篇文章在讲新方法).
目录
备注
apk签名方式有两种,一种是用 signapk.jar或者jarsigner.jar
signapk.jar 是android 自带的,此jar位于Android源码的\prebuilts\sdk\tools\lib下,
jarsigner.exe 是jdk自带的工具默认位于Jdk安装目录下的bin下.作者目录如下(C:\Program Files\Java\jdk1.8.0_161\bin)
其实我们用AS或者eclipse签名都是用以下方式,只是做了一层封装.
signapk.jar签名方式:
java -jar signapk.jar cert.x509.pem private.pk8 unsigned.apk signed.apk
cert.x509.pem:证书包含公钥信息
private.pk8 私钥
我们平常可以用android studio生成或者用keytool工具生成一个叫xxx.keystore的文件.这个文件可以转化为pem文件和pk8文件(参考地址:android默认签名文件转化为pem和pk8).
这里我们用android系统默认给我们的pem和pk8即可(testkey.pk8和testkey.x509.pem),系统默认的这两文件位于Android源码的\build\target\product\security
jarsigner.exe签名方式:
这个方法可以直接用keytool或者AS或者Eclipse工具生成的keystrore文件
jarsigner -verbose -keystore 签名文件路径 -signedjar apk 签名之后存放路径 未签名的apk文件路径 签名包的别称
或者直接在dos界面输入jarsigner查看帮助文档
关于v1 v2签名方式,请参阅以下文章
https://blog.csdn.net/qq_32115439/article/details/55520012
MainActivity.java
//MainActivity.java
package com.example.demo;
public class MainActivity extends ActionBarActivity {
private TextView tx;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view) {
tx = (TextView) findViewById(R.id.et);
Toast.makeText(this, tx.getText().toString(), Toast.LENGTH_SHORT).show();
}
}
界面
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.demo.MainActivity" >
<EditText
android:id="@+id/et"
android:layout_width="300dp"
android:layout_height="wrap_content"
/>
<Button
android:layout_below="@+id/et"
android:layout_width="200dp"
android:text="土司"
android:onClick="onClick"
android:layout_height="wrap_content"
/>
RelativeLayout>
这里我们需要更新处理一下apktools的资源版本过低问题.
什么是资源版本?
答:在你编译某个Android 项目你会用Android某个版本的资源图片等.这些资源我们在
做逆向的时候是需要的.那么这个资源你可以从一部Android手机系统下的/system/framework/framework-res.apk导出.
为什么要更新?
假设你的apk用到android7.1的一些系统资源,而我们本地没有.
这里你可以用AS的文件管理器来帮助我们导出一个模拟器这个文件.
如何更新到apktool?
java -jar apktool if framework-res.apk
此时会将这个文件的资源更新到 你C:\Users\你的系统用户名\apktool\framework\1.apk
下
注意更新资源文件 需要你反编译的apk对应,我这里apk只用用到Api25的资源,所以用Api25的模拟器的资源文件
其中少了 -d 选项.这里我补充下:-d 选项反编译的时候开启调试(debug) .加上后生成的smali文件有一点点区别,是允许我们将反编译的文件进行debug.
这里我们将前面写的Demo程序编译生成apk.名字为app-debug.apk
输入以下命令
java -jar apktools.jar d -d -o 反编译输出目录 需要反编译的apk路径
如下:
tip:
不需要debug可以去掉-d 如:java -jar apktools.jar d -o 反编译输出目录 需要反编译的apk路径
我们打开输出目录如下图:
我们用记事本(或者其他文本编辑器如Vim)打开.在application标签添加属性android:debuggable="true"
如下图:
扩展知识:
有人问我打开默认就有android:debuggable="true"
那么不用改,是因为你在用AS的时候Build Variants 下选择moudle对应变量为 debug. 然后直接点击AS菜单栏的build->build Apk(s) 此时清单文件会自动加上此标签属性.如果选择build->generate signed apk自动为release版本,也就是会自动android:debuggable="false"
本文的启动类为MainActivity.java
打开输出目录下smali下对应的包下的类
//MainActivity.java
package com.xiaomishangc.fmy.myapplication; class MainActivity { void a() { int a;
a=0;// .class public Lcom/xiaomishangc/fmy/myapplication/MainActivity;
a=0;// .super Landroid/support/v7/app/AppCompatActivity;
a=0;// .source "MainActivity.java"
a=0;//
a=0;//
a=0;// # instance fields
a=0;// .field private tx:Landroid/widget/TextView;
a=0;//
a=0;//
a=0;// # direct methods
a=0;// .method public constructor ()V
a=0;// .locals 0
a=0;//
a=0;// .prologue
a=0;// .line 9
a=0;// invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;->()V
a=0;//
a=0;// #p0=(Reference,Lcom/xiaomishangc/fmy/myapplication/MainActivity;);
a=0;// return-void
a=0;// .end method
a=0;//
a=0;//
a=0;// # virtual methods
a=0;// .method public onClick(Landroid/view/View;)V
a=0;// .locals 2
a=0;// .param p1, "view" # Landroid/view/View;
a=0;//
a=0;// .prologue
a=0;// .line 23
a=0;// const v0, 0x7f070029
a=0;//
a=0;// #v0=(Integer);
a=0;// invoke-virtual {p0, v0}, Lcom/xiaomishangc/fmy/myapplication/MainActivity;->findViewById(I)Landroid/view/View;
a=0;//
a=0;// move-result-object v0
a=0;//
a=0;// #v0=(Reference,Landroid/view/View;);
a=0;// check-cast v0, Landroid/widget/TextView;
a=0;//
a=0;// iput-object v0, p0, Lcom/xiaomishangc/fmy/myapplication/MainActivity;->tx:Landroid/widget/TextView;
a=0;//
a=0;// .line 25
a=0;// iget-object v0, p0, Lcom/xiaomishangc/fmy/myapplication/MainActivity;->tx:Landroid/widget/TextView;
a=0;//
a=0;// invoke-virtual {v0}, Landroid/widget/TextView;->getText()Ljava/lang/CharSequence;
a=0;//
a=0;// move-result-object v0
a=0;//
a=0;// invoke-interface {v0}, Ljava/lang/CharSequence;->toString()Ljava/lang/String;
a=0;//
a=0;// move-result-object v0
a=0;//
a=0;// const/4 v1, 0x0
a=0;//
a=0;// #v1=(Null);
a=0;// invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
a=0;//
a=0;// move-result-object v0
a=0;//
a=0;// invoke-virtual {v0}, Landroid/widget/Toast;->show()V
a=0;//
a=0;// .line 26
a=0;// return-void
a=0;// .end method
a=0;//
a=0;// .method protected onCreate(Landroid/os/Bundle;)V
a=0;// .locals 1
a=0;// .param p1, "savedInstanceState" # Landroid/os/Bundle;
a=0;//
a=0;// .prologue
a=0;// .line 15
a=0;// invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
a=0;//
a=0;// .line 16
a=0;// const v0, 0x7f09001a
a=0;//
a=0;// #v0=(Integer);
a=0;// invoke-virtual {p0, v0}, Lcom/xiaomishangc/fmy/myapplication/MainActivity;->setContentView(I)V
a=0;//
a=0;// .line 17
a=0;// invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
a=0;//
a=0;// .line 20
a=0;// return-void
a=0;// .end method
}}
上面就是smali语法,先不用管为什么格式有什么每行a=0.
直接在onCreate方法插入以下smali代码
a=0// invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V对应java代码如下
//此方法会阻塞线程,一直到debuger(调试器)连接
android.os.Debug.waitForDebugger();
知识扩展:
此步骤的用意是在用apktool回编译成apk的时候 ,apk一启动就自动等debuger连接,而不是马上运行.
当然我们也有其他方法让一个apk运行的时候等候一个调试器连接,否则进行阻塞操作.
使用如下命令
adb shell am start -n "apk应用名/启动类全类名" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -D
如本用例可以用如下命令
adb shell am start -n "com.xiaomishangc.fmy.myapplication/com.xiaomishangc.fmy.myapplication.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -D
后者其是AS调试使用的方法.
命令如下
java -jar apktools.jar -b -d -o 输出目录 反编译的时候的目录
本例子
java -jar "E:\反编译\新建文件夹\jarutil\apktool_2.0.3.jar" b -d "C:\Users\fmy\Desktop\逆向apk\Demoout" -o"C:\Users\fmy\Desktop\逆向apk\Demo.apk"
signapk.jar签名方式:
java -jar signapk.jar cert.x509.pem private.pk8 unsigned.apk signed.apk
这一步大家自己尝试吧 我就不截图了
注意本Demo 运行版本在2.2-4.4之间,请用低版本模拟器,或者用夜神模拟器也行.android 5 以下模拟器可正常调试.
安装打开应用,你会发现应用卡在白屏.表示正在等候连接调试器.
我们打开ddms
然后把鼠标放在调试的应用上,会自动为应用打开8700调试端口
后面不用再说了吧
介绍几个命令
//查看正在可以调试app端口
adb jdwp
其中$PID为要调程序的进程号。
adb -d forward tcp:8700 jdwp:$PID
使用本地端口号连接到终端的给定进程:
adb forward tcp:xxx jdwp:<pid>
具体可以参考 https://www.cnblogs.com/chyl411/archive/2014/06/14/3788504.html
总结:
旧版动态调试 ,请用eclipse编写程序.否则apktool有问题