一次jeb动态调试记录
实例:Brokendex.apk
修复apk文件
classes.dex文件头损坏
- apk文件修改扩展名为zip,解压,可以打开该文件夹,找到根目录下的classes.dex
- 使用010 Editor打开classes.dex,修改头部,先覆盖1个字节的0(菜单栏编辑),然后再复制正常的classes.dex的头部放进去。
- 再把修改好的classes.dex替换源文件,重新压缩在修改扩展名成apk
运行jeb_wincon.bat启动jeb
adb连接——jeb调试必需(替代选项为Android sdk)
-
配置:adb连接
下载adb工具包,将adb.exe的路径添加到PATH
cmd命令行输入 adb version查看版本(命令行该命令正常)
打开雷电模拟器,命令行输入adb devices 可以看到雷电模拟器在列表里
注:雷电模拟器adb自动连接,有的模拟器需要adb connetcion连接好模拟器以后才能adb连接上模拟器。
-
debugger连接
确认有设备:“命令行输入adb devices 可以看到雷电模拟器在列表里” 后如果jeb上连接列表是空的,点击按钮:Refresh Machines List。
如果点击attach按钮没有成功连接,在模拟器中打开对应的apk后,refresh再次尝试。(Process非空)
Machines/Devices:选中要调试设备,即设备名称。
Processes:选中要调试的应用程序对应的进程
jeb调试
1.快捷键 Tab:将定位到的smali转换为伪代码。具体操作就是在工程目录中,单击选中“MainActivity”文件按“Tab”即可。不灵的话就右键菜单栏Decomplie
2.快捷键Ctrl+B:在Smali中鼠标单击选中行后添加断点,再按一次取消断点。
工程目录:
Manifest:配置文件
Bytecode:字节码
Certificate:证书
Resources:资源文件
动态调试
普通调试:
- 打断点后菜单栏Debugger start,连接:跳出的连接窗口如前面所示。
连接成功后,调试界面增加的窗口:
VM/Threads 是线程窗口。
VM/Breakpoints 是查看断点窗口。
VM/Locals是寄存器窗口,使用它查看对应寄存器的值,但是上排的工具还是灰色无法使用。
窗口暂时都是空的,工具是灰的无法使用。
- 在雷电模拟器内点击button,下断点的位置变成深蓝色,表示成功断下:
工具也可以使用了:
debug模式
debug模式调试和普通模式调试区别:
在程序启动过程中,程序入口界面和入口点里的函数执行时机非常早。要调试此类函数,就需要使用debug模式启动。
-
首先打开雷电模拟器,保证需要调试的apk已经安装。在cmd里面输入adb shell am start -D -n 应用程序包名/应用程序入口界面,回车执行该命令
-
模拟器弹出了一个弹窗,等待被调试
- 在onCreate方法打一个断点,设置断点的方法和普通模式相同。点击菜单栏“Debugger”选项下的“Start”按钮开始调试。
127.0.0.1:5555 offline
启动 adb 的时候, adb 通过 “adb fork-server server” 启动 adb deamon
而后deamon 就会去找本地的 5555 端口, 直到 5555+32
如果你有程序监听 5555 端口, 会被 adb 认为是 emulator
解决:
结束adb.exe进程再开启就可以
- 经常debugger连接设备的时候找不到device,特别是进程,后面还是使用了比较稳定的那个版本的adb工具,非常正常,而且第一句命令行输出就帮忙把out of date 的 adb server给kill了。第一次非常顺利就是使用了该adb。
Brokendex.apk实例
反汇编的时候jeb出现问题,而GDA反映良好。静态分析首选GDA。
- 静态分析关键函数:
public void MainActivity.onClick(View v) //method@2967
{
String result = new String(Base64.decode("REVYX042UmM=".getBytes(), 0));
this.plaintext = result.toUpperCase().toCharArray();
this.Encryption();
return;
}
public void MainActivity.Encryption() //method@2965
{
char tmp;
int v0 = 0;
while ((v0 < this.plaintext.length())) {
tmp = this.plaintext[v0];
v2 = this.cipherStr.append(this.maplist.get(Character.valueOf(tmp))).append(" ");
v0 = v0+1;
}
this.ciphertext = this.cipherStr;
return;
}
Java.lang.Character.valueOf()是Java中的内置方法,该方法返回代表指定char值的Character实例。如果不需要新的Character实例,则通常优先于构造方法
Character(char)使用此方法,因为此方法通过缓存频繁请求的值可能会产生明显更好的空间和时间性能。此方法将始终缓存范围从[’\ u0000’到’\ u007F’]在内的值,并且可能会缓存此范围之外的其他值。
- 首先将“REVYX042UmM=” base64解码,并转成大写,变成char数组,即为plaintext。
- 接着使用Encrption函数,获取plaintext的字符,通过maplist获取对应的值并最终返回ciphertext。
调试看到的plaintext:
- 调试Encrytion函数结束后的寄存器:
v0就是plaintext的存储,v2实际上应该是cipherstr的存储但是看不到,在p0里面可以看到所有的内容与整个过程。
在调试的过程中,该寄存器就看到了v3的变化,v0 v1 v2都是执行完函数结束了才出现了,之前一直在找这几个变量,耽误了好多时间,实际上并不影响… 但是当我重新再弄一次的时候,寄存器里面都有了(第一次只有v3 v4)。
<=====事实上是因为在寄存器的开关选项里,一直默认的是显示debug的变量,需要再次点击该button以取消。
得到的ciphertext:
-** * -**- -*--*-- -* -**** *-* -*-*
看起来就是摩斯密码
:ᄶe82a
尝试把plaintext改成Flag,也没得到什么有价值的。
而且看maplist就是单纯的实现字符到摩斯码的转换。
小结
试验了一次动态调试使用。没有找到与flag相关。
参考
https://zhuanlan.zhihu.com/p/302856081
https://www.cnblogs.com/nonamelake/p/14577951.html