Android反编译(MAC下)+为apk添加button+二次打包

反编译一般apk并添加控件(新手)–笔记篇

最近了解了一下逆向,想实现如题的效果,但是从网上搜了半天,没有找到详细的教程,最后只好参考一些相关博客来实现自己的目标,功夫不负有心人,有了一点的收获。本人接触Android时间不长,如有一些东西讲的不对,望各位大佬指点。同时,非喜勿喷O(∩_∩)O
  在这里不得不吐槽一下网上许多的博客都一样,有些博客又大同小异,查找起来真心累。

  • 基本知识和准备:
    1.懂得一点Android的基础知识。
    2.Android Studio的简单操作。
    3.使用的工具是: apktooldex2jarjd-gui
    所用的工具上面都有下载链接。本人是在Mac下进行的开发,为了大家开发方便我也上传到了csdn
    https://download.csdn.net/download/qq_36462112/10350760
  • 工具的环境搭建
    apktool的配置
    (1).将wrapper script的内容拷贝下来,并将文件命名为apktool。(或者将光标移到wrapper script上右键—链接另存为—文件名apktool)
    (2).将apktool2.3.1.jar下载下来并命名为apktool.jar
    (3).将apktool 和apktool.jar 移到/usr/local/bin (要打开隐藏文件,否则看不到user)
    apktool的位置
    这里写图片描述
    (4).为apktool添加权限chmod +x apktool
    在lib的文件夹(也就是apktool 的父级文件夹,后面就不在细说了)下右键—新建位于文件夹位置的终端窗口(没有就去服务设置里面进行设置),输入命令chmod +x apktool
    (也就是通过终端到该文件的父级目录,然后执行命令。由于mac支持在任何文件夹下都可以打开终端,而Ubuntu14.04不行,在这里说明下。没有打开终端的选项,就直接通过cd 命令到达该目录就行了。)

    勾选新建位于文件夹位置的终端窗口
    dex2jar-2.0配置:
    (1).下载并解压dex2jar-2.0
    (2). 为dex2jar-2.0添加权限chmod -R 777 dex2jar-2.0
    同样也是在dex2jar-2.0文件夹的父级文件夹下 右键—新建位于文件夹位置的终端窗口,输入命令chmod -R 777 dex2jar-2.0

jd-gui 没有什么配置,直接点击图标后打开使用即可。

反编译
反编译市场上的一些apk有点不好,所以我这里准备了一个appText.apk 来进行演示。
1.将apk文件放入一个文件夹中,如我这里的reverse。打开终端
在文件位置打开终端
2.在终端下输入命令 apktool d appText.apk
执行成功后出现了一个appText的文件夹 第一次如果有wrong:/Users/lj/Library/apktool/framework/
可以在资源,也就是Library中新建一个apktool文件夹,再在里面新建framework文件夹就行了
这里写图片描述
(3 )将appText.app的后缀改为.zip 使用解压工具解压(系统自带的有可能解压不了), 将解压后的class.dex拷贝到dex2jar-2.0文件夹下
(4)在dex2jar-2.0文件夹下打开终端,执行命令sh d2j-dex2jar.sh classes.dex得到classes-dex2jar.jar
这里写图片描述

以上就是反编译的过程,查看源代码的话,就直接使用jd-gui 打开classes-dex2jar.jar就可以了

注入代码,添加button
这里只是主要讲一下怎么实现在主activity中加入一个弹框,其他的一些知识就顺便提下,如有兴趣可以自己百度。我后期会出相关笔记博客。
首先在反编译出来的appText文件夹中找到activity_main.xml 在appText —res—layout—activity_main.xml 打开后在布局文件中加入如下代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
<!--   添加的代码 -->
    <Button
        android:id="@+id/text_btn"
        android:text="反编译"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

之后再到values文件夹中找到public.xml 打开后将 <public type="id" name="text_btn" id="0x7f07007d" /> 加入。 ps: id值根据自己情况填写
public.xml 包含了所有的资源文件,所以在这里加入我们自己添加的id。 name是你上面自己定义的id名字,id的值根据前一个加1,如我的是0x7f07007c 加1 0x7f07007d 就是 注意资源文件是16进制递增的。一般在最后一个的基础上加1,就行了。不过有些可能不是顺序的,那就在同一个type中,即id 找到一个最大的加1就行。
这里写图片描述

同样是在values文件夹中找到ids.xml 在最后面加入<item type="id" name="text_btn">false</item>
这里写图片描述

在appText/smali/com/blue/demo 中找到 R$id.smali 打开后将下面的代码加入进去
.field public static final text_btn:I = 0x7f07007d
text_btn 之前的button的id 0x7f07007d 前面得到数值。
这里写图片描述

上面将资源部分的添加完了,接下来是代码部分

根据appText中的AndroidMainfest.xml 中的 package=”com.blue.demo” 包名使用Android studio新建一个一样的项目
注意一下,我添加的button点击后浏览百度网站,所以要加网络权限。你们在添加时,如果没有就在AndroidMainfest.xml 中加上。

这里写图片描述

我这里是测试所以xml使用的是样的,下面是Java代码

package com.blue.demo;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setShow();
    }

    private void setShow() {

        Button button = findViewById(R.id.text_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Context context = MainActivity.this;
                TextView title = new TextView(context);
                title.setText("提示");
                title.setPadding(10, 10, 10, 10);
                title.setGravity(Gravity.CENTER);
                title.setTextSize(25);
                title.setTextColor(Color.BLACK);
                TextView m = new TextView(context);
                m.setText("如何解决你的问题-->");
                m.setPadding(5, 5, 5, 5);
                m.setGravity(Gravity.CENTER);
                m.setTextColor(Color.BLACK);
                m.setTextSize(20);
                AlertDialog.Builder dialog = new AlertDialog.Builder(context);
                dialog.setCustomTitle(title);
                dialog.setView(m);
                dialog.setPositiveButton("是",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Uri uri = Uri.parse("http://www.baidu.com");
                                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                                startActivity(intent);
                            }
                        });
                dialog.setNegativeButton("否",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                                Uri uri = Uri.parse("https://upload-images.jianshu.io/upload_images/10343318-0e072b630eab8733.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240");
                                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                                startActivity(intent);
                            }
                        });
                dialog.show();
            }
        });

    }


}

直接Build–BuildAPK(s) 打包一个debug版本的apk,再使用上面的反编译方法得到app-debug文件夹,
将app-debug/smali/com/blue/demo中的MainActivity.smali 中的下面代码复制到 appText/smali/com/blue/demo的MainActivity.smali 中。

.method private setShow()V
    .locals 2

    .line 27
    const v0, 0x7f07007d   #修改为我们之前定义button时,算出来的id   0x7f07007d 

    invoke-virtual {p0, v0}, Lcom/blue/demo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/Button;

    #.line 28
    .local v0, "button":Landroid/widget/Button;
    new-instance v1, Lcom/blue/demo/MainActivity$1;

    invoke-direct {v1, p0}, Lcom/blue/demo/MainActivity$1;-><init>(Lcom/blue/demo/MainActivity;)V

    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    #.line 70
    return-void
.end method

// .method      .end method  标志的是函数的开始和结束    放在最后就行了。
// 使用#注释掉.line xx  或者直接删除,这个是起标志执行的步骤(对代码的实际运行不怎么影响)。我们是直接强加的,就不要加这个了。 

    #.line 22
    invoke-direct {p0}, Lcom/blue/demo/MainActivity;->setShow()V
//这句话是调用上面的函数,要放在onCreate()方法里面,同时也要在setContentView()方法之后,


//下面我的appText/smali/com/blue/demo的MainActivity.smali  全部代码(修改后)。给你们参看下,不同的项目是不一样的

.class public Lcom/blue/demo/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"


# direct methods
.method public constructor <init>()V
    .locals 0

    .prologue
    .line 6
    invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

    return-void
.end method


# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 1
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .prologue
    .line 10
    invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    .line 11
    const v0, 0x7f09001a

    invoke-virtual {p0, v0}, Lcom/blue/demo/MainActivity;->setContentView(I)V


    invoke-direct {p0}, Lcom/blue/demo/MainActivity;->setShow()V

    .line 12
    return-void
.end method

.method private setShow()V
    .locals 2

    const v0, 0x7f07007d   #修改为我们之前定义button时,算出来的id   0x7f07007d 

    invoke-virtual {p0, v0}, Lcom/blue/demo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/Button;

    .local v0, "button":Landroid/widget/Button;
    new-instance v1, Lcom/blue/demo/MainActivity$1;

    invoke-direct {v1, p0}, Lcom/blue/demo/MainActivity$1;-><init>(Lcom/blue/demo/MainActivity;)V

    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    return-void
.end method

可以看到在setShow()函数中有一个MainActivity$1      我的理解是当前类的实例     每一个button或者其他的监听之类的事件都会有一个MainActivity$– 与之对应。我之前的项目中没有一个监听,所以没有一个MainActivity$1,可以直接使用MainActivity$1 ,如果你反编译的项目有一个或者多个,在它的基础上加1就行了。如:反编译的项目中有MainActivity$1,MainActivity$2,MainActivity$3 那么你就把app-debug中的文件名改为MainActivity$4 ,其他的是两个也要一起改MainActivity4$1.smali MainActivity4$2.smali
注意:改了文件名后,这三个文件里面的MainActivity$1都要改为MainActivity$5

我的没有所以直接是将app-debug/smali/com/blue/demo中的MainActivity$1.smali MainActivity1$1.smali MainActivity1$2.smali中 三个文件直接放到了 appText/smali/com/blue/demo中。

这里写图片描述

以上所有的代码都改完了,下面二次打包。
在reverse文件下打开终端,输入指令apktool b appText -o text2.apk
得到text2.apk 这个apk还没签名无法安装。

首先,签名需要keystore文件,可以使用keytool工具生成,一般Java环境都带有keytool命令,可以在命令行测试。
keytool -genkey -alias demo.keystore -keyalg RSA -validity 40000 -keystore demo.keystore
回车之后设置密码
各个参数解释如下:
-genkey 产生证书文件
-alias 产生别名
-keystore 指定密钥库的.keystore文件中
-keyalg 指定密钥的算法,这里指定为RSA(非对称密钥算法)
-validity 为证书有效天数,这里我们写的是40000天

输入上述命令后,会有如下的提示:

输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
[Unknown]: test
您的组织单位名称是什么?
[Unknown]: test
您的组织名称是什么?
[Unknown]:
您所在的城市或区域名称是什么?
[Unknown]:
您所在的州或省份名称是什么?
[Unknown]:
该单位的两字母国家代码是什么
[Unknown]:
CN=test, OU=test, O=Unknown, L=Unknown, ST=Unknown, C=Unknown 正确吗?
[否]: y

输入demo.keystore的主密码
(如果和 keystore 密码相同,按回车):

执行完后会有一个demo.keystore文件,再使用它对apk进行签名
jarsigner也存在于Java JDK的安装包当中,所以安装好了Java环境的话,可以直接在命令行使用。
jarsigner -verbose -keystore demo.keystore text2.apk demo.keystore
回车之后输入密码
-verbose 指定生成详细输出
-keystore 指定数字证书存储路径

这样,就完成了对一个apk的签名过程,然后就可以安装使用了。注意如果你的手机上原来就有这个apk,需要卸载掉。因为新apk的签名已经改变了。
这里写图片描述

写的有点啰嗦,不喜勿喷哈?
以上就是这次的所有内容,觉得好可以点个赞

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值