一、系统环境
OS: Windows_NT x64 10.0.19045
python:3.8.10
Node.js: 18.17.1
frida :14.2.14
objection:1.11.0
vscode: 1.87.2
device:nexus 5x-7.1.2
二、详细分析
1.首先用jadx查看程序逻辑
这个实例没有加壳,可以很清晰的看到程序代码,直接打开MainActivity就可以看到验证的代码位置
通过上面的代码可以看到用户名和密码相加后的内容传入到VVVVV.VVVV进行校验,根据返回的结果弹出不同的提示内容,下面是VVVVV这个类的完整代码
public class VVVVV {
private VVVVV() {
}
public static boolean VVVV(View.OnClickListener context, String input) {
if (input.length() != 5) {
return false;
}
byte[] v = eeeee(input);
byte[] p = "6f452303f18605510aac694b0f5736beebf110bf".getBytes();
if (v.length != p.length) {
return false;
}
for (int i = 0; i < v.length; i++) {
if (v[i] != p[i]) {
return false;
}
}
return true;
}
private static byte[] eeeee(String input) {
byte[] SALT = {95, 35, 83, 73, 75, 35, 95};
try {
StringBuilder sb = new StringBuilder();
sb.append((char) SALT[0]);
sb.append((char) SALT[1]);
for (int i = 0; i < input.length(); i++) {
sb.append((char) input.getBytes("iso-8859-1")[i]);
sb.append((char) SALT[i + 2]);
}
sb.append((char) SALT[6]);
byte[] bArr = new byte[0];
return sssss(sb.toString()).getBytes("iso-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
private static String ccccc(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 15;
int two_halfs = 0;
while (true) {
if (halfbyte < 0 || halfbyte > 9) {
buf.append((char) ((halfbyte - 10) + 97));
} else {
buf.append((char) (halfbyte + 48));
}
halfbyte = data[i] & 15;
int two_halfs2 = two_halfs + 1;
if (two_halfs >= 1) {
break;
}
two_halfs = two_halfs2;
}
}
return buf.toString();
}
private static String sssss(String text) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] bArr = new byte[40];
md.update(text.getBytes("iso-8859-1"), 0, text.length());
return ccccc(md.digest());
} catch (UnsupportedEncodingException e2) {
e2.printStackTrace();
return null;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
从这个代码可以知道账户名加密码也就是flag的长度是5个数字
if (input.length() != 5) {
return false;
}
从上面的代码可以看出调用逻辑是
VVVV -> eeeee -> sssss -> ccccc
只有VVVV是进行校验的地方,后面3个函数都是对输入的内容进行加密的代码
通过上面的分析发现有两种方式可以获得flag:
1.暴力破解,通过循环的方式调用VVVV获得正确的flag
2.把加密代码抠出来,用android studio创建工程调用加密代码获得正确的flag加密后的内容
目标明确后就用objection来验证函数的调用逻辑是否正确
2.用objection调试看函数内容
打开终端运行附加进程命令
objection -g com.kanxue.pediy1 explore
验证内存中类是否存在
android hooking search classes com.kanxue.pediy1.VVVVV
hook指定方法并打印调用信息
com.kanxue.pediy1 on (google: 7.1.2) [usb] # android hooking watch class_method com.kanxue.pediy1.VVVVV.VVVV --dump-args --dump-backtrace --dump-return
(agent) Attempting to watch class com.kanxue.pediy1.VVVVV and method VVVV.
(agent) Hooking com.kanxue.pediy1.VVVVV.VVVV(android.view.View$OnClickListener, java.lang.String)
(agent) Registering job 483231. Type: watch-method for: com.kanxue.pediy1.VVVVV.VVVV
com.kanxue.pediy1 on (google: 7.1.2) [usb] # (agent) [483231] Called com.kanxue.pediy1.VVVVV.VVVV(android.view.View$OnClickListener, java.lang.String)
(agent) [483231] Backtrace:
com.kanxue.pediy1.VVVVV.VVVV(Native Method)
com.kanxue.pediy1.MainActivity$1.onClick(MainActivity.java:36)
android.view.View.performClick(View.java:5637)
android.view.View$PerformClick.run(View.java:22429)
android.os.Handler.handleCallback(Handler.java:751)
android.os.Handler.dispatchMessage(Handler.java:95)
android.os.Looper.loop(Looper.java:154)
android.app.ActivityThread.main(ActivityThread.java:6121)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
de.robv.android.xposed.XposedBridge.main(XposedBridge.java:107)
(agent) [483231] Arguments com.kanxue.pediy1.VVVVV.VVVV([object Object], ass1112)
(agent) [483231] Return Value: (none)
3.用frida进行hook修改函数执行结果
function main(){
Java.perform(function(){
//先故意写错,通过异常提示来得出函数的参数签名
Java.use("com.kanxue.pediy1.VVVVV").VVVV.implementation = function(x){
var result = this.VVVV(x);
console.log("x,y,result:",x,result);
return result;
}
})
}
setImmediate(main);
通过异常信息得到参数签名
[Nexus 5X::com.kanxue.pediy1]-> Error: expected an unsigned integer
Error: VVVV(): argument types do not match any of:
.overload('android.view.View$OnClickListener', 'java.lang.String')
把正确的参数签名填入代码后,就可以获得正常hook函数
function main(){
Java.perform(function(){
//先故意写错,通过异常提示来得出函数的参数签名
// Java.use("com.kanxue.pediy1.VVVVV").VVVV.implementation = function(x){
// var result = this.VVVV(x);
// console.log("x,y,result:",x,result);
// return result;
// }
Java.use("com.kanxue.pediy1.VVVVV").VVVV.overload('android.view.View$OnClickListener', 'java.lang.String').implementation = function(x,y){
var result = this.VVVV(x,y);
console.log("x,y,result:",x,y,result);
return result;
}
})
}
setImmediate(main);
x,y,result: [object Object] 33311 false
修改代码循环调用VVVV轮询出正确的flag,flag确定是5位数,所以flag的范围一定是在10000-99999之间
Java.use("com.kanxue.pediy1.VVVVV").VVVV.overload('android.view.View$OnClickListener', 'java.lang.String').implementation = function(x,y){
var intv = 10000;
for(var i = 0;i < 90000; i++){
var strv = intv + i;
var result = this.VVVV(x,strv.toString());
if(result == true){
console.log("x,y,result:",x,strv.toString(),result);
}
}
return result;
}
注入js文件注入程序
$frida -U com.kanxue.pediy1 -l test1.js
获取正确flag
[Nexus 5X::com.kanxue.pediy1]-> x,y,result: [object Object] 66888 true
第2种扣代码用android studio写的方式大同小异就不写了