【Android逆向】静态分析+frida破解test2.apk
有了上一篇的基础
https://www.cnblogs.com/gradyblog/p/17152108.html
现在尝试静态分析的方式来处理
为什么还要多此一举,因为题眼告诉了我们是五位数字,所以可以爆破,不告诉这个题眼的话,就得分析
1. IDA 打开libroysue.so,查看JNI_OnLoad, 从method_table里找到Sign对应的函数
.data:00080000 ; ===========================================================================
.data:00080000
.data:00080000 ; Segment type: Pure data
.data:00080000 AREA .data, DATA
.data:00080000 ; ORG 0x80000
.data:00080000 ; JNINativeMethod method_table[1]
.data:00080000 1C 55 07 00 21 55 07 00 25 71+_ZL12method_table JNINativeMethod <aSign, aLjavaLangStrin_1, _Z4fuckP7_JNIEnvP7_jclassP8_jstring+1>
.data:00080000 03 00 ; DATA XREF: JNI_OnLoad+EA↑o
.data:00080000 ; JNI_OnLoad+EC↑o
.data:00080000 ; .text:off_376D0↑o
.data:00080000 ; _Unwind_VRS_Interpret+1B2↑o
.data:00080000 ; _Unwind_VRS_Interpret:def_68226↑o
.data:00080000 ; .text:off_68354↑o
.data:00080000 ; fuck(_JNIEnv *,_jclass *,_jstring *) ...
从这里可以看出,底层是这个 fuck函数与之对应,看一下它,整理后
jstring __fastcall fuck(JNIEnv *env, jclass jcls, jstring str_)
{
jstring v4; // [sp+14h] [bp-BCh]
_jmethodID *methodID; // [sp+18h] [bp-B8h]
jstring v6; // [sp+30h] [bp-A0h]
int i; // [sp+38h] [bp-98h]
jbyte *ByteArrayElements; // [sp+3Ch] [bp-94h]
_jbyteArray *array; // [sp+40h] [bp-90h]
jobject j_str_bytes; // [sp+44h] [bp-8Ch]
_jmethodID *v11; // [sp+48h] [bp-88h]
_jclass *v12; // [sp+4Ch] [bp-84h]
_jmethodID *digest_method_id; // [sp+50h] [bp-80h]
_jobject *md_instance; // [sp+54h] [bp-7Ch]
_jclass *Class; // [sp+5Ch] [bp-74h]
_jobject *j_str; // [sp+60h] [bp-70h]
_jfieldID *fieldID; // [sp+68h] [bp-68h]
_jclass *clazz; // [sp+6Ch] [bp-64h]
unsigned __int8 *src; // [sp+74h] [bp-5Ch]
char *v23; // [sp+9Ch] [bp-34h]
_WORD v24[4]; // [sp+A3h] [bp-2Dh] BYREF
if ( !str_ )
return 0;
src = (unsigned __int8 *)_JNIEnv::GetStringUTFChars(env, str_, 0);
clazz = _JNIEnv::FindClass(env, "android/os/Build");
fieldID = _JNIEnv::GetStaticFieldID(env, clazz, "FINGERPRINT", "Ljava/lang/String;");
_JNIEnv::GetStaticObjectField(env, clazz, fieldID);
strcat((char *)src, "REAL");
j_str = (_jobject *)j_o0OoOOOO(env, src);
_android_log_print(4, "roysuejni", "before entering aes => %s", (const char *)src);
Class = _JNIEnv::FindClass(env, "java/security/MessageDigest");
methodID = _JNIEnv::GetStaticMethodID(env, Class, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;");
v4 = j_o0OoOOOO(env, "MD5");
md_instance = _JNIEnv::CallStaticObjectMethod(env, Class, methodID, v4);
digest_method_id = _JNIEnv::GetMethodID(env, Class, "digest", "([B)[B");
v12 = _JNIEnv::FindClass(env, "java/lang/String");
v11 = _JNIEnv::GetMethodID(env, v12, "getBytes", "()[B");
j_str_bytes = _JNIEnv::CallObjectMethod(env, j_str, v11);
array = (_jbyteArray *)_JNIEnv::CallObjectMethod(env, md_instance, digest_method_id, j_str_bytes);
ByteArrayElements = _JNIEnv::GetByteArrayElements(env, array, 0);
for ( i = 0; i <= 15; ++i )
sprintf((char *)&v24[i], "%02x", (unsigned __int8)ByteArrayElements[i]);
v23 = (char *)j_ll11l1l1ll(src);
strcat(v23, (const char *)v24);
v6 = j_o0OoOOOO(env, (const unsigned __int8 *)v23);
_android_log_print(4, "roysuejni", "result is => %s ", v23);
_JNIEnv::ReleaseStringUTFChars(env, str_, src);
free(v23);
return v6;
}
静态分析可知,大概逻辑如下
1. 给输入拼接个 REAL,比如输入 1111 变成 1111REAL
2. 然后执行给MD5,得到MD5的值(32位)
3. 将拼接后的输入由j_ll11l1l1ll处理一下,得到返回值
4. 将返回值和md5进行拼接,返回
2. 点进去看看j_ll11l1l1ll在干什么
unsigned __int8 *__fastcall ll11l1l1ll(const unsigned __int8 *input)
{
unsigned __int8 *v2; // [sp+8h] [bp-30h]
uint8_t *output; // [sp+Ch] [bp-2Ch]
size_t byte_count; // [sp+10h] [bp-28h]
uint8_t *iv; // [sp+18h] [bp-20h]
uint8_t *key; // [sp+1Ch] [bp-1Ch]
char *v8; // [sp+2Ch] [bp-Ch]
key = (uint8_t *)ll11lll1l1();
iv = (uint8_t *)ll11l1l1l1();
v8 = (char *)ll11l1l11l(input);
byte_count = strlen(v8);
output = (uint8_t *)malloc(byte_count);
j_qpppqp(output, (uint8_t *)v8, byte_count, key, iv);
v2 = j_bbddbbdbb(output, byte_count);
free(v8);
free(output);
free(key);
free(iv);
return v2;
}
看到 key iv ,八成就是AES加密了,再看看是哪种模式的加密,点进j_qpppqp看看
void __fastcall qpppqp(uint8_t *output, uint8_t *input, uint32_t length, const uint8_t *key, const uint8_t *iv)
{
__int64 v5; // d17
unsigned __int8 v6; // [sp+Bh] [bp-2Dh]
uint32_t i; // [sp+Ch] [bp-2Ch]
v6 = length & 0xF;
if ( key )
{
Key = key;
KeyExpansion();
}
if ( iv )
Iv = (uint8_t *)iv;
for ( i = 0; i < length; i += 16 )
{
v5 = *((_QWORD *)input + 1);
*(_QWORD *)output = *(_QWORD *)input;
*((_QWORD *)output + 1) = v5;
XorWithIv(output);
state = (state_t *)output;
Cipher();
Iv = output; // 这里加密后的数据变下一次IV,是典型的CBC模式
input += 16;
output += 16;
}
if ( v6 )
{
qmemcpy(output, input, v6);
memset(&output[v6], 0, 16 - v6);
XorWithIv(output);
state = (state_t *)output;
Cipher();
}
}
由代码特征可知: 这里加密后的数据变下一次IV,是典型的CBC模式
那么只要拿到key 和iv 试一下就可确认算法是不是预测正确
frida代码
function print_dump(arg, size) {
console.log(hexdump(arg, {
offset: 0,
length: size,
header: true,
ansi: true
}))
}
function main() {
Java.perform(function () {
var lib_hanlder = Process.findModuleByName("libroysue.so");
console.log("lib_handler: " + lib_hanlder)
if (lib_hanlder) {
var addr_ll11l1l1ll = lib_hanlder.base.add(0x0003C9E4 + 0x1)
Interceptor.attach(addr_ll11l1l1ll,{
onEnter: function(args) {
console.log(" === hook before")
var arg = args[0]
print_dump(arg, 128)
},
onLeave:function(retVal) {
console.log(" === hook after: " + retVal)
print_dump(retVal, 128)
}
})
var addr_qpppqp = lib_hanlder.base.add(0x0003B868 + 0x1)
var output
Interceptor.attach(addr_qpppqp,{
onEnter: function(args) {
output = args[0]
console.log(" === hook qpppqp before: " + output)
console.log(" === hook qpppqp before KEY: ")
var arg = args[3]
print_dump(arg, 128)
console.log(" === hook qpppqp before IV: ")
var arg = args[4]
print_dump(arg, 128)
},
onLeave:function(retVal) {
console.log(" === hook qpppqp after==>: " + output)
print_dump(output, 128)
}
})
}
})
}
setTimeout(main, 3000)
输出日志
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
dbf3a038 31 31 31 31 52 45 41 4c 00 84 f1 db 10 33 f0 db 1111REAL.....3..
dbf3a048 00 f2 58 d9 00 00 00 00 58 a0 f3 db 00 00 00 00 ..X.....X.......
dbf3a058 bc 6e c1 c6 44 00 00 00 48 ac 20 c7 58 ab 20 c7 .n..D...H. .X. .
dbf3a068 00 00 00 00 00 00 96 42 00 ad 20 c7 00 00 00 00 .......B.. .....
dbf3a078 00 00 00 00 00 00 00 00 18 e2 11 d6 00 00 00 00 ................
dbf3a088 64 e2 11 d6 00 00 00 00 d0 f0 9b e4 00 00 00 00 d...............
dbf3a098 00 20 bb c6 00 00 00 00 80 27 f2 db 00 00 00 00 . .......'......
dbf3a0a8 00 00 00 00 00 00 00 00 00 29 f2 db 00 00 00 00 .........)......
=== hook qpppqp before: 0xcd33c9d0
=== hook qpppqp before KEY:
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
e42d0350 67 6f 6f 64 6c 2d 61 65 73 2d 6b 65 79 31 32 34 goodl-aes-key124
e42d0360 00 52 34 cd 88 52 34 cd 67 6f 6f 64 6c 2d 61 65 .R4..R4.goodl-ae
e42d0370 73 2d 69 76 31 32 33 35 00 d8 07 dc 88 d8 07 dc s-iv1235........
e42d0380 31 31 31 31 52 45 41 4c 08 08 08 08 08 08 08 08 1111REAL........
e42d0390 00 00 00 00 c7 c3 00 00 80 03 2d e4 9b 87 77 11 ..........-...w.
e42d03a0 00 00 00 00 bd 04 1b e7 1e 00 00 00 c8 c3 00 00 ................
e42d03b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e42d03c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
=== hook qpppqp before IV:
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
e42d0368 67 6f 6f 64 6c 2d 61 65 73 2d 69 76 31 32 33 35 goodl-aes-iv1235
e42d0378 00 d8 07 dc 88 d8 07 dc 31 31 31 31 52 45 41 4c ........1111REAL
e42d0388 08 08 08 08 08 08 08 08 00 00 00 00 c7 c3 00 00 ................
e42d0398 80 03 2d e4 9b 87 77 11 00 00 00 00 bd 04 1b e7 ..-...w.........
e42d03a8 1e 00 00 00 c8 c3 00 00 00 00 00 00 00 00 00 00 ................
e42d03b8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e42d03c8 00 00 00 00 00 00 00 00 e8 04 2d e4 01 05 1b e7 ..........-.....
e42d03d8 00 10 24 e5 68 e7 60 da 80 28 e1 e5 b0 9f 4f d6 ..$.h.`..(....O.
=== hook qpppqp after==>: 0xcd33c9d0
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
cd33c9d0 72 cb e2 0c 9e 28 ec 25 e0 0f e3 bf 7a 37 c1 8e r....(.%....z7..
cd33c9e0 2c 60 f6 e4 00 00 00 00 13 00 00 00 00 00 00 00 ,`..............
cd33c9f0 44 0b e1 e5 00 00 00 00 40 51 9a e4 00 00 00 00 D.......@Q......
cd33ca00 2c 60 f6 e4 00 00 00 00 13 00 00 00 01 20 00 00 ,`........... ..
cd33ca10 a4 f4 38 c6 80 fc 97 c6 00 b8 2d c6 00 40 f0 db ..8.......-..@..
cd33ca20 a4 f4 2c c6 f0 fb 97 c6 50 ec 33 c6 00 40 f0 db ..,.....P.3..@..
cd33ca30 3e 00 00 00 44 00 00 00 4b 00 00 00 53 00 00 00 >...D...K...S...
cd33ca40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
=== hook after: 0xda60e740
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
da60e740 37 32 63 62 65 32 30 63 39 65 32 38 65 63 32 35 72cbe20c9e28ec25
da60e750 65 30 30 66 65 33 62 66 37 61 33 37 63 31 38 65 e00fe3bf7a37c18e
da60e760 00 66 34 65 33 62 30 65 38 61 33 32 35 63 66 37 .f4e3b0e8a325cf7
da60e770 63 65 66 32 38 39 61 62 31 66 35 36 32 64 66 65 cef289ab1f562dfe
da60e780 00 20 00 00 03 00 00 00 00 00 00 00 00 00 00 00 . ..............
da60e790 00 00 80 3f 00 00 00 00 00 00 00 00 00 00 00 00 ...?............
da60e7a0 00 00 80 3f 00 00 00 00 00 00 00 00 00 00 00 00 ...?............
da60e7b0 00 00 80 3f 10 00 00 00 00 d0 86 e4 68 ad 04 00 ...?........h...
那么可以得出
key: goodl-aes-key124
iv: goodl-aes-iv1235
加密后的数据为: 72cbe20c9e28ec25e00fe3bf7a37c18e4f4e3b0e8a325cf7cef289ab1f562dfe
去掉32位的md5,aes加密部分为72cbe20c9e28ec25e00fe3bf7a37c18e
4. 打开在线AES解密,得出结果确实是1111REAL
5. 取出apk中的校验字符串4143cb60bf8083ac94c57418a9a7ff5a14a63feade6b46d9d0af3182ccbdf7af,去掉后面的32个字符得到4143cb60bf8083ac94c57418a9a7ff5a,解密得到45678REAL
6. 得到flag为45678,手机验证,通过
【Android逆向】静态分析+frida破解test2.apk的更多相关文章
- Android逆向之旅---破解"穿靴子的猫"游戏的收费功能
一.游戏收费分析 游戏收费非常正常的,可是玩游戏最恶心的就是你还没玩就要充值,非常恼火,事实上我不怎么玩游戏,主要是给小孩子们弄,比方如今好多小屁孩们喜欢玩水果忍者这个游戏.可是这个游戏在没有開始玩的 ...
- Android简单应用程序破解——runtime.apk
对于<Debugging Android Application>一文中最后附上的练习,我采用了另一种静态方法绕开原有的逻辑去破解.主要的过程如下: 利用apktool将练习的runtim ...
- Android逆向之旅---静态分析技术来破解Apk
一.前言 从这篇文章开始我们开始我们的破解之路,之前的几篇文章中我们是如何讲解怎么加固我们的Apk,防止被别人破解,那么现在我们要开始破解我们的Apk,针对于之前的加密方式采用相对应的破解技术,And ...
- Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)
Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码) 来源 https://blog.csdn.net/jiangwei0910410003/article/details/51 ...
- Android逆向破解表单注册程序
Android逆向破解表单注册程序 Android开发 ADT: android studio(as) 程序界面如下,注册码为6位随机数字,注册成功时弹出通知注册成功,注册失败时弹出通知注册失败. 布 ...
- Android逆向破解表单登录程序
Android逆向破解表单登录程序 Android开发 ADT: android studio(as) 程序界面如下,登录成功时弹出通知登录成功,登录失败时弹出通知登录失败. 布局代码 <?xm ...
- Android逆向之静态分析
想必打过CTF的小伙伴多多少少都触过Android逆向,所以斗哥将给大家整一期关于Android逆向的静态分析与动态分析.本期先带来Android逆向的静态分析,包括逆向工具使用.文件说明.例题解析等 ...
- Android 逆向实战篇(加密数据包破解)
1. 实战背景由于工作需要,要爬取某款App的数据,App的具体名称此处不便透露,避免他们发现并修改加密逻辑我就得重新破解了. 爬取这款App时发现,抓包抓到的数据是加密过的,如图1所示(原数据较长, ...
- Android逆向分析(2) APK的打包与安装背后的故事
前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...
- Android逆向分析(2) APK的打包与安装
http://blog.zhaiyifan.cn/2016/02/13/android-reverse-2/ 2/18日增加对aidl和java编译的描述. 前言 上一次我们反编译了手Q,并遇到了Ap ...
随机推荐
- [转帖]Jmeter中如何读取MYSQL数据作为请求参数
在项目测试过程中,我经常需要将数据库中的数据作为参数传递到请求中.Jmeter中MYSQL数据库连接操作过程如下: 1.下载/n导入mysql的jdbc驱动包 下载mysql驱动包地址: http:/ ...
- Docker镜像精简方法之二 COPY vs ADD 与镜像层
Docker镜像精简方法之二 COPY vs ADD 与镜像层 摘要 昨天只是讲了一下大体的思路. 但是没有实操. 今天想着修改一下默认的打包镜像的命令,验证一下效果 原始命令 FROM adopto ...
- [转帖]skywalking配置nacos集群模式
版本: name version nacos 1.1.0 skywalking 6.2.0 elasticsearch 6.3.2 es集群管理工具 cerebro-0.8.3 https://git ...
- [转帖]009 Linux 文件大小统计与排序 (du 于 df 和 sort)
https://my.oschina.net/u/3113381/blog/5463932 01 du 与 df 作用与区别? Linux 最有用最常用的统计文件大小命令是什么?无疑就是 du 和 d ...
- Vue.use和install之间的关系
创建一个plugins.js文件 跟main.js同级下,创建一个plugins.js文件 export default { // install是vue给我们提供的.它会自动去执行install. ...
- Git如何拉取指定远程分支
转载来自https://www.jianshu.com/p/856ce249ed78 目的 我们想要获取到代码仓库中分支"a" 中的文件到本地,我了解到有三种方法. 代码仓库 ...
- C++ Qt开发:运用QJSON模块解析数据
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QJs ...
- 14.7 Socket 循环结构体传输
在上述内容中笔者通过一个简单的案例给大家介绍了在套接字编程中如何传递结构体数据,本章将继续延申结构体传输,在某些时候例如我们需要传输一些当前系统的进程列表信息,或者是当前主机中的目录文件,此时就需要使 ...
- MySQL 之基础命令(精简笔记)
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RD ...
- Exception message: CreateSymbolicLink error (1314): ???????????
window下运行任务报错:Exception message: CreateSymbolicLink error (1314): ??????????? 报错信息如下: Diagnostics: E ...