Error[8]: Undefined offset: 65, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

[+++]是AndroID的build-tool中自带的,[+++]后接数字证书存储路径,[+++]后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

[+++]

再安装签名后的apk

[+++]

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下[+++],windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 66, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,[+++]后接数字证书存储路径,[+++]后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

[+++]

再安装签名后的apk

[+++]

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下[+++],windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 67, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,后接数字证书存储路径,[+++]后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

[+++]

再安装签名后的apk

[+++]

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下[+++],windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 68, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,后接数字证书存储路径,后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

[+++]

再安装签名后的apk

[+++]

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下[+++],windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 69, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,后接数字证书存储路径,后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

再安装签名后的apk

[+++]

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下[+++],windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 70, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,后接数字证书存储路径,后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

再安装签名后的apk

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下[+++],windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 71, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,后接数字证书存储路径,后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

再安装签名后的apk

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下,windows下[+++]

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
&lt;&lt;Android软件安全权威指南&gt;&gt;笔记1 第一个Android程序分析_app_内存溢出

&lt;&lt;Android软件安全权威指南&gt;&gt;笔记1 第一个Android程序分析

&lt;&lt;Android软件安全权威指南&gt;&gt;笔记1 第一个Android程序分析,第1张

概述<<Android软件安全权威指南>>笔记第二章如何分析Android程序编写一个Android程序新建一个Android工程,名称为Crackme0201吧主界面添加用户名和注册码输入框<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apkes/androi <<AndroID软件安全权威指南>>笔记第二章 如何分析AndroID程序编写一个AndroID程序

新建一个AndroID工程,名称为Crackme0201

主界面添加用户名和注册码输入框

<?xml version="1.0" enCoding="utf-8"?><linearLayout 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"    androID:orIEntation="vertical"    tools:context=".MainActivity"    androID:ID="@+ID/linearLayout">    <TextVIEw        androID:ID="@+ID/textVIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:text="@string/top_string"        androID:textAlignment="center"        androID:gravity="center_horizontal" />    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/username"/>        <EditText            androID:ID="@+ID/edit_username"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:layout_marginRight="10dp"            androID:layout_marginleft="10dp"            androID:layout_weight="1"            androID:hint="@string/hint_username"/>    </linearLayout>    <linearLayout        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="horizontal">        <TextVIEw            androID:layout_wIDth="wrap_content"            androID:layout_height="wrap_content"            androID:text="@string/regCode"/>        <EditText            androID:ID="@+ID/edit_regCode"            androID:layout_wIDth="0dp"            androID:layout_height="wrap_content"            androID:ems="10"            androID:hint="@string/hint_regCode"            androID:layout_marginleft="10dp"            androID:layout_marginRight="10dp"            androID:layout_weight="1" />    </linearLayout>    <button        androID:ID="@+ID/button_register"        androID:layout_wIDth="wrap_content"        androID:layout_height="wrap_content"        androID:text="@string/btn_register"        androID:layout_marginRight="10dp"        androID:layout_gravity="right"/></linearLayout>

然后,编写MainActivity类的代码,添加一个checkSN()方法

    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();            String hexstr = toHexString(bytes, "");            StringBuilder stringBuilder = new StringBuilder();            for (int i = 0; i < hexstr.length(); i+=2) {                stringBuilder.append(hexstr.charat(i));            }            String userSN = stringBuilder.toString();            //Log.d("crackme", hexstr);            //Log.d("crackme", userSN);            if (!userSN.equalsIgnoreCase(sn)) {                return false;            }        } catch (NoSuchAlgorithmException e) {            e.printstacktrace();            return false;        }        return true;    }

该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。

最后判断传入的注册码与计算得出的注册码是否匹配。

MainActivity中的onCreate()方法中添加注册按钮点击事件

    @OverrIDe    protected voID onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentVIEw(R.layout.activity_main);        setTitle(R.string.unregister);        edit_username = (EditText) findVIEwByID(R.ID.edit_username);        edit_sn = (EditText) findVIEwByID(R.ID.edit_regCode);        btn_register = (button) findVIEwByID(R.ID.button_register);        btn_register.setonClickListener(new VIEw.OnClickListener() {            @OverrIDe            public voID onClick(VIEw v) {                if (!checkSN(edit_username.getText().toString().trim(),                        edit_sn.getText().toString().trim())) {                    Toast.makeText(MainActivity.this, R.string.unsuccessed,                            Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(MainActivity.this, R.string.successed,                            Toast.LENGTH_SHORT).show();                    btn_register.setEnabled(false);                    setTitle(R.string.register);                }            }        });    }

如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示


编译APK文件

有两种编译方式,使用命令行和AS的Build菜单

命令行

gradlew是基于task来编译项目的,在命令行下执行gradlew tasks会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks

生成DeBUG调试版本的APK:./gradlew assembleDeBUG,会在Crackme0201/app/build/outputs/apk目录下生成app-deBUG.apk文件

安装DeBUG版本APK:./gradlew installDeBUG,会安装到与系统相连的设备/模拟器

adb devices,列出当前连接的设备,包括模拟器

AS编译

直接点击run app会自动编译安装

只编译不安装点make project


破解第一个AndroID程序

以Crackme0201为例

通常方法是,使用ApkTool反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool工具重新编译为APK并签名,进行测试

上述过程循环,直到完全破解

实际中还可以用IDA Pro直接分析APK文件,使用dex2jarjd-gui配合进行Java源码级分析

反编译APK文件

下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用

apktool d ./apk-deBUG.apk -o outdir

会在outdir文件夹下生成反编译文件,smali目录下是程序的所有反汇编代码,res目录下是程序所有资源文件,子目录的文件结构与开发源码一致。

分析APK文件

找到突破口,一般是错误提示信息,属于字符串资源

APK打包时,string.xml文件被加密存储为resources.arsc文件,反编译会解密

string.xml中的所有字符串资源都在gen/R.java文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml文件中

找到unsuccessed字符串和对应索引值

但是搜索文件夹下没有与索引值相关的信息

接下来生成Release版本APK文件

用AS进行签名生成app-release.apk,再用apktool进行反编译

找到unsuccessed对应的索引值,全局搜索,在MainActivity.smali中找到了索引值

.line 33处调用checkSN()(但是这里是access .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->accessBoolean0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_00,没有#备注,不知道为啥)

move-result p1

意思是,返回const/4 v0, 0x0if-nez p1, :cond_0类型值(哪里看出来的?)

返回结果保存在p1

    .line 35    iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35:Lorg/nuaa/crackme0201/MainActivity;    const v1, 0x7f0b0032    invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast;    move-result-object p1    .line 36    invoke-virtual {p1}, LandroID/Widget/Toast;->show()V    goto :goto_0

忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处

iget-object

继续往下看,不跳转会执行

MainActivity

-thisMainActivity处事要synthetic指令获取const v1, 0x7f0b0032的实例的引用,Toast;->maketoast()是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this,line 32:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voID中的一个if-eqz字段,存储父类MainActivity的引用

if-nez

该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0

如果跳转cond_0,则执行如下指令

apktool b outdir_rel

这里的代码是注册成功时候的逻辑

修改smali文件代码

通过上述分析,outdir_rel/dist处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]是跳转关键点

这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner,表示等于0时跳转

就用--ks代替--out,保存修改

adb uninstall org.nuaa.crackme0201
重新编译APK并签名

修改好以后,使用apktool重新编译打包

adb install signed.apk

AndroID-Crack-Tool目录下生成apk文件

要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名

AndroID Killer

是AndroID的build-tool中自带的,后接数字证书存储路径,后接签名后apk输出路径,最后是原apk路径

在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载

再安装签名后的apk

安装成功,测试随意的用户名+注册码都可以验证成功。


总结

一般流程

反编译 -> 分析 -> 修改 -> 回编译 -> 签名

集成工具:MAC下,windows下

总结

以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1058698.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存