zoukankan      html  css  js  c++  java
  • Android apk逆向实战

    简介

    逆向Android apk其实是一个分析Android apk的一个过程,必须了解Android程序开发的流程、结构、语句分支、解密原理等等。

    功能

    破解一个注册验证程序(自写一个简单的注册验证程序,然后分析它,再破解它)。

    步骤

    1、编写一个简单的注册验证apk,关键代码如下:

        private boolean checkSN(String userName, String sn) { //确认验证
            try {
                if ((userName == null) || (userName.length() == 0))
                    return false;
                if ((sn == null) || (sn.length() != 16))
                    return false;
                MessageDigest digest = MessageDigest.getInstance("MD5");
                digest.reset();
                digest.update(userName.getBytes());
                byte[] bytes = digest.digest();     //采用MD5对用户名进行Hash
                String hexstr = toHexString(bytes, ""); //将计算结果转化成字符串
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hexstr.length(); i += 2) {
                    sb.append(hexstr.charAt(i));
                }
                String userSN = sb.toString(); //计算出的SN        
                //Log.d("crackme", hexstr);
                //Log.d("crackme", userSN);
                if (!userSN.equalsIgnoreCase(sn))   //比较注册码是否正确
                    return false;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return false;
            }        
            return true;
        }
        
        private static String toHexString(byte[] bytes, String separator) { //转为十六进制
            StringBuilder hexString = new StringBuilder();
            for (byte b : bytes) {
                String hex = Integer.toHexString(0xFF & b);
                if(hex.length() == 1){
                    hexString.append('0');
                }
                hexString.append(hex).append(separator);
            }
            return hexString.toString();
        }

    这个方法的主要功能是计算用户名与注册码是否匹配。

    运行效果如图:



    2、分析、破解

    下载开源工具ApkTool:http://code.google.com/p/android-apktool/

    破解Android apk的方法是将apk文件利用ApkTool反编译,生成Smali格式的反汇编代码,然后分析Smali文件的代码运行机制,找到程序的突破口进程修改,最后使用ApkTool

    重新编译生成apk文件并签名。

    命令格式如下:

    反编译apk:apktool d[ecode] [opts] <file.apl> [<dir>]

    编译apk:apktool b[uild] [opts] [<app_path>] [<out_file>]

    运行效果如图:



    反编译apk文件成功后,会在当前的outdir目录下(默然是apk文件名)生成一系列目录与文件,其中smali目录下存放了程序所有的反汇编代码,res目录则是程序中所有的资源文件,这些目录的子目录和文件与原程序的源码目录结构是一致的。

    如何寻找突破口是分析一个程序的关键,一般来说,错误提示信息通常是指引关键代码的风向标,在错误提示附近一般是程序的核心验证码。

    错误提示是Android 程序中的字符串资源,这些字符串可能硬编码到源代码中,也可能引用自 “res目录下的string.xml文件,apk打包时,string.xml中的字符串被加密存储为resources.arsc文件保存到apk程序包中。

    string.xml 详情如下:

    <resources>
    
        <string name="app_name">Crackme0201</string>
        <string name="menu_settings">Settings</string>
        <string name="title_activity_main">Crackme0201</string>
        <string name="info">Android程序破解演示实例</string>
        <string name="username">用户名:</string>
        <string name="sn">注册码:</string>
        <string name="register">注    册</string>
        <string name="hint_username">请输入用户名</string>
        <string name="hint_sn">请输入16位的注册码</string>
        <string name="unregister">程序未注册</string>
        <string name="registered">程序已注册</string>
        <string name="unsuccessed">无效用户名或注册码</string>
        <string name="successed">恭喜您!注册成功</string>
    
    </resources>


    对应加密文件(public.xml)详情:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <public type="drawable" name="ic_launcher" id="0x7f020001" />
        <public type="drawable" name="ic_action_search" id="0x7f020000" />
        <public type="layout" name="activity_main" id="0x7f030000" />
        <public type="dimen" name="padding_small" id="0x7f040000" />
        <public type="dimen" name="padding_medium" id="0x7f040001" />
        <public type="dimen" name="padding_large" id="0x7f040002" />
        <public type="string" name="app_name" id="0x7f050000" />
        <public type="string" name="hello_world" id="0x7f050001" />
        <public type="string" name="menu_settings" id="0x7f050002" />
        <public type="string" name="title_activity_main" id="0x7f050003" />
        <public type="string" name="info" id="0x7f050004" />
        <public type="string" name="username" id="0x7f050005" />
        <public type="string" name="sn" id="0x7f050006" />
        <public type="string" name="register" id="0x7f050007" />
        <public type="string" name="hint_username" id="0x7f050008" />
        <public type="string" name="hint_sn" id="0x7f050009" />
        <public type="string" name="unregister" id="0x7f05000a" />
        <public type="string" name="registered" id="0x7f05000b" />
        <public type="string" name="unsuccessed" id="0x7f05000c" />
        <public type="string" name="successed" id="0x7f05000d" />
        <public type="style" name="AppTheme" id="0x7f060000" />
        <public type="menu" name="activity_main" id="0x7f070000" />
        <public type="id" name="textView1" id="0x7f080000" />
        <public type="id" name="edit_username" id="0x7f080001" />
        <public type="id" name="edit_sn" id="0x7f080002" />
        <public type="id" name="button_register" id="0x7f080003" />
        <public type="id" name="menu_settings" id="0x7f080004" />
    </resources>


    unsuccessed的id值为0x7f05000c,在smali目录中搜索含有内容为0x7f05000c的文件,最后发现只有MainActivity$1.smali文件一处调用,代码如下:

    .class Lcom/droider/crackme0201/MainActivity$1;
    .super Ljava/lang/Object;
    .source "MainActivity.java"
    
    # interfaces
    .implements Landroid/view/View$OnClickListener;
    
    
    # annotations
    .annotation system Ldalvik/annotation/EnclosingMethod;
        value = Lcom/droider/crackme0201/MainActivity;->onCreate(Landroid/os/Bundle;)V
    .end annotation
    
    .annotation system Ldalvik/annotation/InnerClass;
        accessFlags = 0x0
        name = null
    .end annotation
    
    
    # instance fields
    .field final synthetic this$0:Lcom/droider/crackme0201/MainActivity;
    
    
    # direct methods
    .method constructor <init>(Lcom/droider/crackme0201/MainActivity;)V
        .locals 0
        .parameter
    
        .prologue
        .line 1
        iput-object p1, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        .line 29
        invoke-direct {p0}, Ljava/lang/Object;-><init>()V
    
        return-void
    .end method
    
    
    # virtual methods
    .method public onClick(Landroid/view/View;)V
        .locals 4
        .parameter "v"
    
        .prologue
        const/4 v3, 0x0
    
        .line 32
        iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        iget-object v1, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        #getter for: Lcom/droider/crackme0201/MainActivity;->edit_userName:Landroid/widget/EditText;
        invoke-static {v1}, Lcom/droider/crackme0201/MainActivity;->access$0(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/EditText;
    
        move-result-object v1
    
        invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    
        move-result-object v1
    
        invoke-interface {v1}, Landroid/text/Editable;->toString()Ljava/lang/String;
    
        move-result-object v1
    
        invoke-virtual {v1}, Ljava/lang/String;->trim()Ljava/lang/String;
    
        move-result-object v1
    
        .line 33
        iget-object v2, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        #getter for: Lcom/droider/crackme0201/MainActivity;->edit_sn:Landroid/widget/EditText;
        invoke-static {v2}, Lcom/droider/crackme0201/MainActivity;->access$1(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/EditText;
    
        move-result-object v2
    
        invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    
        move-result-object v2
    
        invoke-interface {v2}, Landroid/text/Editable;->toString()Ljava/lang/String;
    
        move-result-object v2
    
        invoke-virtual {v2}, Ljava/lang/String;->trim()Ljava/lang/String;
    
        move-result-object v2
    
        .line 32             #调用checkSN函数
        #calls: Lcom/droider/crackme0201/MainActivity;->checkSN(Ljava/lang/String;Ljava/lang/String;)Z
        invoke-static {v0, v1, v2}, Lcom/droider/crackme0201/MainActivity;->access$2(Lcom/droider/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z
    
        move-result v0     
    
        if-neqz v0, :cond_0    #关键跳转  程序的破接口  把if-eqz v0  ---->if-eqz v0 即可
        .line 34#获取实例的引用
        iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        .line 35                                   字符串压人v1寄存器
        const v1, 0x7f05000c注意!!!!!!!!!!!!
    
        .line 34
        invoke-static {v0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
    
        move-result-object v0
    
        .line 35
        invoke-virtual {v0}, Landroid/widget/Toast;->show()V
    
        .line 42
        :goto_0
        return-void
    
        .line 37
        :cond_0
        iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        .line 38
        const v1, 0x7f05000d
    
        .line 37
        invoke-static {v0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
    
        move-result-object v0
    
        .line 38
        invoke-virtual {v0}, Landroid/widget/Toast;->show()V
    
        .line 39
        iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        #getter for: Lcom/droider/crackme0201/MainActivity;->btn_register:Landroid/widget/Button;
        invoke-static {v0}, Lcom/droider/crackme0201/MainActivity;->access$3(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/Button;
    
        move-result-object v0
    
        invoke-virtual {v0, v3}, Landroid/widget/Button;->setEnabled(Z)V
    
        .line 40
        iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
    
        const v1, 0x7f05000b
    
        invoke-virtual {v0, v1}, Lcom/droider/crackme0201/MainActivity;->setTitle(I)V
    
        goto :goto_0
    .end method


    分析完毕,重新编译生成apk文件并签名





    签名成功后会在同目录下生成signed.apk文件,如图:



    破解完成,放入模拟器运行下,可以了。

    小结

    通过实战破解一个简单的验证程序,了解Android程序的一般分析与破解流程,但在实际的分析过程中,接触的代码远比这些要复杂得多。


    下载

    实例及工具下载

  • 相关阅读:
    Android开发之Sqlite的使用
    ZOJ 3607 Lazier Salesgirl
    ZOJ 3769 Diablo III
    ZOJ 2856 Happy Life
    Ural 1119 Metro
    Ural 1146 Maximum Sum
    HDU 1003 Max Sum
    HDU 1160 FatMouse's Speed
    Ural 1073 Square Country
    Ural 1260 Nudnik Photographer
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3294060.html
Copyright © 2011-2022 走看看