android逆向-动态调试1

本文采用过时apktool动态调试功能.请采用apktool2.10以下版本(高版本废弃,后面第二篇文章在讲新方法).


目录


  • 工具:
  • apk签名知识储备:
  • 用Eclipse编写一个程序:
  • apktools反编译:
    • 资源文件更新(可跳过此步,有问题回过头来看看)
    • 反编译
  • 修改文件使得其支持调试
    • 修改清单文件(AndroidManifest.xml)
    • 修改启动类(Launch Activity)
  • apktool进行回编译成apk
  • 给生成的apk签名
  • 安装对应模拟器即可

工具:

  1. apktool2.10
  2. Android studio 3.0.1(下文简写AS)
  3. Android studio自带的模拟器 (android 7.1版本)
  4. signapk.jar或者jarsigner.exe(mac或linux忽略后缀)
  5. 当然还有电脑一台
  6. Eclipse 用来写一个旧程序 因为apktool太久了

备注
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)

apk签名知识储备:

其实我们用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系统默认给我们的pempk8即可(testkey.pk8testkey.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

用Eclipse编写一个程序:

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>

对应视图:
android逆向-动态调试1_第1张图片

apktools反编译:

资源文件更新(可跳过此步,有问题回过头来看看)

这里我们需要更新处理一下apktools的资源版本过低问题.
什么是资源版本?
答:在你编译某个Android 项目你会用Android某个版本的资源图片等.这些资源我们在
做逆向的时候是需要的.那么这个资源你可以从一部Android手机系统下的/system/framework/framework-res.apk导出.
为什么要更新?
假设你的apk用到android7.1的一些系统资源,而我们本地没有.

这里你可以用AS的文件管理器来帮助我们导出一个模拟器这个文件.

android逆向-动态调试1_第2张图片

如何更新到apktool?

java -jar apktool if framework-res.apk

此时会将这个文件的资源更新到 你C:\Users\你的系统用户名\apktool\framework\1.apk

注意更新资源文件 需要你反编译的apk对应,我这里apk只用用到Api25的资源,所以用Api25的模拟器的资源文件

这里写图片描述

反编译

我们先来看看apktools有哪些命令参数
android逆向-动态调试1_第3张图片

其中少了 -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路径

修改文件使得其支持调试

我们打开输出目录如下图:

android逆向-动态调试1_第4张图片

修改清单文件(AndroidManifest.xml)

我们用记事本(或者其他文本编辑器如Vim)打开.在application标签添加属性android:debuggable="true"
如下图:
android逆向-动态调试1_第5张图片

扩展知识:
有人问我打开默认就有android:debuggable="true" 那么不用改,是因为你在用AS的时候Build Variants 下选择moudle对应变量为 debug. 然后直接点击AS菜单栏的build->build Apk(s) 此时清单文件会自动加上此标签属性.如果选择build->generate signed apk自动为release版本,也就是会自动android:debuggable="false"
android逆向-动态调试1_第6张图片

修改启动类(Launch Activity)

本文的启动类为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

android逆向-动态调试1_第7张图片

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调试使用的方法.

apktool进行回编译成apk

命令如下

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"

给生成的apk签名

signapk.jar签名方式:

java -jar signapk.jar cert.x509.pem private.pk8 unsigned.apk signed.apk

这一步大家自己尝试吧 我就不截图了

安装对应模拟器即可

注意本Demo 运行版本在2.2-4.4之间,请用低版本模拟器,或者用夜神模拟器也行.android 5 以下模拟器可正常调试.
安装打开应用,你会发现应用卡在白屏.表示正在等候连接调试器.
我们打开ddms
android逆向-动态调试1_第8张图片

然后把鼠标放在调试的应用上,会自动为应用打开8700调试端口
android逆向-动态调试1_第9张图片

打开AS打开 反编译的输出目录
android逆向-动态调试1_第10张图片

开始设置AS远程调试的配置
android逆向-动态调试1_第11张图片

android逆向-动态调试1_第12张图片
android逆向-动态调试1_第13张图片

然后点击debug按钮即可
android逆向-动态调试1_第14张图片

后面不用再说了吧

介绍几个命令

//查看正在可以调试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有问题

你可能感兴趣的:(安卓)