动态调试破解Ali_android_1程序[第三天/3h]
静态调试
方法一【代码逻辑+图片隐写】
老方法反编译代码可以代码主要逻辑->用户输入的密码enPassword经过密码表table的转换同内置的密码pw进行比对
![h3JuRcOolTH76L9](https://s2.loli.net/2024/07/03/h3JuRcOolTH76L9.png)
转换逻辑->用户输入密码enPassword经utf-8编码后取低八位,作为table的寻址下标,逐个转化。
也可以看到从图片中获取正确密码,密码表同理
![image-20240703161519658](https://s2.loli.net/2024/07/03/dYHmogWRGKkrvuw.png)
图片隐写
图片本身就是一定格式的二进制串,利用图片细微变化不易引起人眼察觉的特点隐藏信息
![image-20240703161935035](https://s2.loli.net/2024/07/03/48APVF6HhwIxfUp.png)
CTF misc图片类总结(入门级)_ctf图片-CSDN博客
- 最低有效位隐写术:就像在书页的最后一行添加小字注释,变化微小但可以嵌入信息。
- 频域隐写术:就像在音乐的高频音中嵌入信息,整体听感不变但可以检测到细微变化。
- 调色板隐写术:就像调整调色板中的颜色顺序,实际颜色不变但可以解码隐藏的信息。
这里可以利用反编译的代码逻辑获取密码表和内置密码
![image-20240710200539658](https://s2.loli.net/2024/07/10/JxPcjWMtKQrLe9S.png)
根据加密代码逻辑,写出解码代码,得到581026,即破解成功
![image-20240710194633361](https://s2.loli.net/2024/07/10/fCRIMPSBFpWtVaw.png)
动态调试
环境配置
android studio动态调试apk最详细教程_apk调试-CSDN博客
AndroidStudio 动态调试apk(release版)_apk release 调试-CSDN博客
smali代码逻辑
.method public onClick(View)V
.registers 11
# 获取 MainActivity$1 类中的 val$edit 字段,这是一个 EditText 对象
iget-object v6, p0, MainActivity$1->val$edit:EditText
# 调用 EditText 的 getText 方法,返回 Editable 对象
invoke-virtual EditText->getText()Editable, v6
move-result-object v6
# 调用 Editable 的 toString 方法,将文本内容转换为 String 对象
invoke-interface Editable->toString()String, v6
move-result-object v3
# 获取 MainActivity$1 类中的 this$0 字段,这是 MainActivity 对象的引用
iget-object v6, p0, MainActivity$1->this$0:MainActivity
# 调用 MainActivity 的 getTableFromPic 方法,返回字符串 v5
invoke-virtual MainActivity->getTableFromPic()String, v6
move-result-object v5
# 获取 MainActivity$1 类中的 this$0 字段,这是 MainActivity 对象的引用
iget-object v6, p0, MainActivity$1->this$0:MainActivity
# 调用 MainActivity 的 getPwdFromPic 方法,返回字符串 v4
invoke-virtual MainActivity->getPwdFromPic()String, v6
move-result-object v4
# 打印日志,输出获取到的表格字符串 v5
const-string v6, "lil"
new-instance v7, StringBuilder
const-string v8, "table:"
invoke-direct StringBuilder-><init>(String)V, v7, v8
invoke-virtual StringBuilder->append(String)StringBuilder, v7, v5
move-result-object v7
invoke-virtual StringBuilder->toString()String, v7
move-result-object v7
invoke-static Log->i(String, String)I, v6, v7
# 打印日志,输出获取到的密码字符串 v4
const-string v6, "lil"
new-instance v7, StringBuilder
const-string v8, "pw:"
invoke-direct StringBuilder-><init>(String)V, v7, v8
invoke-virtual StringBuilder->append(String)StringBuilder, v7, v4
move-result-object v7
invoke-virtual StringBuilder->toString()String, v7
move-result-object v7
invoke-static Log->i(String, String)I, v6, v7
# 将字符串 v3 转换为 UTF-8 编码的字节数组,并存储到 v6 中
:try_80
const-string v6, "utf-8"
invoke-virtual String->getBytes(String)[B, v3, v6
move-result-object v6
# 调用 MainActivity 中的 access$0 方法,传递参数 v5 和 v6,并将返回值存储到 v2 中
invoke-static MainActivity->access$0(String, [B)String, v5, v6
move-result-object v2
# 打印日志,输出加密后的密码字符串 v2
const-string v6, "lil"
new-instance v7, StringBuilder
const-string v8, "enPassword:"
invoke-direct StringBuilder-><init>(String)V, v7, v8
invoke-virtual StringBuilder->append(String)StringBuilder, v7, v2
move-result-object v7
invoke-virtual StringBuilder->toString()String, v7
move-result-object v7
invoke-static Log->i(String, String)I, v6, v7
:tryend_BC
# 捕获 UnsupportedEncodingException 异常,打印异常堆栈信息
.catch UnsupportedEncodingException {:try_80 .. :tryend_BC} :catch_E8
:tryend_BC
# 如果 v4 不为空且不等于空字符串,则执行下面的代码块
if-eqz v4, :F2
:C0
const-string v6, ""
invoke-virtual String->equals(Object)Z, v4, v6
move-result v6
if-nez v6, :F2
# 如果 v4 不为空且不等于空字符串,并且不等于 v2,则执行下面的代码块
:D0
invoke-virtual String->equals(Object)Z, v4, v2
move-result v6
if-eqz v6, :F2
:DC
# 获取 MainActivity$1 类中的 this$0 字段,这是 MainActivity 对象的引用
iget-object v6, p0, MainActivity$1->this$0:MainActivity
# 调用 MainActivity 的 access$1 方法,传递 MainActivity 对象 v6
invoke-static MainActivity->access$1(MainActivity)V, v6
:E6
# 方法结束,返回空
return-void
:catch_E8 # used for: Ljava/io/UnsupportedEncodingException;
# 捕获 UnsupportedEncodingException 异常,打印异常堆栈信息
move-exception v1
invoke-virtual UnsupportedEncodingException->printStackTrace()V, v1
goto :tryend_BC
# 如果条件不成立,则执行下面的代码块
:F2
new-instance v0, AlertDialog$Builder
# 创建 AlertDialog.Builder 对象,并传递 MainActivity 对象的引用
iget-object v6, p0, MainActivity$1->this$0:MainActivity
invoke-direct AlertDialog$Builder-><init>(Context)V, v0, v6
const v6, 0x7F0A0011 # string:dialog_error_tips "密码不对,请继续破解"
# 设置对话框的消息内容为字符串资源 0x7F0A0011
invoke-virtual AlertDialog$Builder->setMessage(I)AlertDialog$Builder, v0, v6
const v6, 0x7F0A0010 # string:dialog_title "提示"
# 设置对话框的标题为字符串资源 0x7F0A0010
invoke-virtual AlertDialog$Builder->setTitle(I)AlertDialog$Builder, v0, v6
const v6, 0x7F0A0013 # string:dialog_ok "确定"
new-instance v7, MainActivity$1$1
# 创建 MainActivity$1$1 匿名内部类,并传递 MainActivity$1 对象的引用
invoke-direct MainActivity$1$1-><init>(MainActivity$1)V, v7, p0
# 设置对话框的确定按钮为字符串资源 0x7F0A0013,并设置点击事件监听器为匿名内部类对象 v7
invoke-virtual AlertDialog$Builder->setPositiveButton(I, DialogInterface$OnClickListener)AlertDialog$Builder, v0, v6, v7
# 显示对话框
invoke-virtual AlertDialog$Builder->show()AlertDialog, v0
goto :E6
.end method
断点进行调试,得出密码表和内置密码
![image-20240709173146427](https://s2.loli.net/2024/07/09/pUZlE57awRsS3Qr.png)
解密代码同上
改变程序
想要考到一百分,一是写对答案,二是修改正确答案
依据输错的错误提示字符,在androidkiller反编译后查找
![image-20240710213028044](https://s2.loli.net/2024/07/10/XKcBTvx5OEeHbfF.png)
继续查找name
![image-20240710213117929](https://s2.loli.net/2024/07/10/kWlBF358eNcfn9v.png)
继续查找id,可以看到错误提示字符串赋给了v6,而:cond_0是个代码块
![image-20240710213244911](https://s2.loli.net/2024/07/10/x48Unb3lJ5WKwHq.png)
上翻寻找跳转到cond_0的逻辑,可以看到三个if跳转语句
![image-20240710213355100](https://s2.loli.net/2024/07/10/UZwyx4sFaijcP6I.png)
直接删除跳转语句,使得程序直接执行.line 55 的成功注册逻辑,修改后保存,
![image-20240710212819612](https://s2.loli.net/2024/07/10/EV4fKsnYFCxbQ58.png)
经过androidkill编译后,重新运行,不论输入什么都出现破解成功。
![image-20240710212857990](https://s2.loli.net/2024/07/10/zK9jST5pM8Drtqw.png)
今日总结
最近一方面因为工期任务紧,一方面家里的琐事比较多,故懈怠了好些天。今天重拾了smali语言顺便了解了android动态调试的过程(用真机调试感觉没有PC端逆向的olldbg那样好使)。基础知识感觉没学到太多,只能说,熟悉了一些工具的使用。另外感觉自己的smali语言还得多熟悉熟悉。