记一次 APK 逆向动静调试 + so 动态链接库分析

0x00 前言:

好久没有做过安卓逆向了,最近重新系统地学习了安卓逆向技术。找到了一道较为典型的逆向分析题来练手,以锻炼动静态分析和动态链接库分析的基本能力。在这里记录基本的分析流程手法。
在这里插入图片描述


0x01 逆向分析:

一、使用 Genymotion 生成一个安卓虚拟机,把目标 Apk 拖入安装并运行:

Genymotion 是一款出色的跨平台的Android模拟器,具有容易安装和使用、运行速度快、自带 ROOT 的特点, 是Android开发、测试等相关人员的必备工具。

任意输入,返回flag错误。初步判断该软件类似于卡密软件,需要输入正确的卡密才能使用。


二、使用 Jeb 对目标 Apk 进行动静分析:

拖入 jeb 查看 Java 字节码如下:
在这里插入图片描述

tab 键反汇编如下:

在这里插入图片描述
静态分析可知:
主函数主要是获取输入框输入,判断输入字符串长度是否 > 10, 并且字符串收尾由 flag{ 和 } 组成。符合基本条件,就可以进入 check.check() 函数,对 flag{} 包裹的字符串进行检查,验证是否正确。
进入 check 函数:
在这里插入图片描述
可知该check函数采用 AES 对称加密对参数进行加密,然后base64编码后传入 Myjni.encode函数进行二次编码,编码后的结果与 NOYKxeJRlz65XGjgTODxUvJIBdnY8NQZNQgnoK5Mxckh3fhvJjNFWoBM8wVCdfOz 进行比较,比较相同则输入的flag正确。

其中 MyJni 是外部链接库,待会还得逆向分析 MyJni 所在的 .so 外部链接库。
在这里插入图片描述


基本程序逻辑已经理清楚,Ctrl + B 在check 函数中打入断点
在这里插入图片描述
开始 动态调试
在这里插入图片描述
按基本要求输入字符串,断点成功被卡住:
在这里插入图片描述
断点调试直到 0000000E invoke-static Myjni->getkey()String获取AES加密秘钥:Z29qZSUgYKMmYJ5fch9kZL==
在这里插入图片描述

运行到 00000096 invoke-static Base64->encodeToString([B, I)String, p0, v1 生成加密结果:uOkRlVa7zcEd9qTGJdreAw==

在这里插入图片描述
运行 0000009E invoke-static Myjni->encode(String)String, p0 进行二次编码,多次尝试发现,每次编码的结果都不同,而且编码后长度还变短了,有点诡异。以下是三次相同字符串(flag{xxxxxxxxxxxxxx})的二次编码结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


没什么头绪,开始分析 MyJni 所在的 libHello.so, 分析 encode 函数逻辑。导出 libHello.so
在这里插入图片描述
放入 IDA 中进行分析:
在这里插入图片描述
encode 函数的汇编代码如下:

在这里插入图片描述
反汇编伪代码如下:

jstring __fastcall Java_com_example_myapplication_Myjni_encode(JNIEnv *env, jclass jclass, jstring jflag)
{const char *v4; // r12size_t i; // rbxchar v6; // alchar v7; // dlint v8; // ecx__m128 v10; // [rsp+0h] [rbp-78h] BYREF__m128 v11; // [rsp+10h] [rbp-68h]__int128 v12; // [rsp+20h] [rbp-58h]__int128 v13; // [rsp+30h] [rbp-48h]char v14; // [rsp+40h] [rbp-38h]unsigned __int64 v15; // [rsp+50h] [rbp-28h]v15 = __readfsqword(0x28u);v4 = env->functions->GetStringUTFChars(env, jflag, 0LL);env->functions->ReleaseStringUTFChars(env, jflag, v4);if ( *v4 ){for ( i = 0LL; strlen(v4) > i; ++i ){v8 = v4[i];if ( (unsigned int)(v8 - 123) > 0xFFFFFFE5 ){v6 = 97;v7 = -97;}else{if ( (unsigned int)(v8 - 91) < 0xFFFFFFE6 )continue;v6 = 65;v7 = -65;}v4[i] = v6+ v7+ v8+ 17- 26 * ((20165 * ((char)(v7 + v8) + 17) < 0) + ((unsigned int)(20165 * ((char)(v7 + v8) + 17)) >> 19));}}v13 = 0LL;v12 = 0LL;v11 = 0LL;v10 = 0LL;v14 = 0;v10 = _mm_movelh_ps((__m128)*((unsigned __int64 *)v4 + 1), (__m128)*((unsigned __int64 *)v4 + 7));v11 = _mm_movelh_ps((__m128)*((unsigned __int64 *)v4 + 3), (__m128)*((unsigned __int64 *)v4 + 6));v12 = *((_OWORD *)v4 + 2);*(_QWORD *)&v13 = *((_QWORD *)v4 + 2);*((_QWORD *)&v13 + 1) = *(_QWORD *)v4;return env->functions->NewStringUTF(env, &v10);
}

简单分析如下:

在这里插入图片描述

主要分两段处理逻辑:第一段字符串替换加密,第二段字符串重组。


①、替换加密算法分析与逆向:

在这里插入图片描述
转成 python 代码:

after_aes_base64_value = "" # aes加密后base64编码值
ans = "" # 替换后的结果
for i in range(len(after_aes_base64_value )):v8 = ord(after_aes_base64_value[i])if v8 > 96:v6 = 97v7 = -97else:if v8 < 65:ans.append(v8)continuev6 = 65v7 = -65ans.append(v6 + v7 + v8 + 17 - 26 * (20165 * ((v7 + v8 + 17) < 0) + (20165 * (v7 + v8 + 17)) >> 19))

这段代码对字符串中的字母进行了一个复杂的变换,而非字母字符保持不变。

对于字符串中的每个字符,获取字符的ASCII值(v8)。检查 v8 是否大于96(表示是小写字母)。如果v8小于65(不是大写字母或数字),直接将v8添加到ans。确定基值 v6(大写字母为65,小写字母为97)和负基值v7。使用公式计算加密值: v6 + v7 + v8 + 17 - 26 * (20165 * ((v7 + v8 + 17) < 0) + (20165 * (v7 + v8 + 17)) >> 19)

根据以上分析写出逆向解密算法

# 已知加密后的ans值
ans = []
# 逆向函数,计算原始字符的ASCII值
def reverse_char(encoded_char, base):v7 = -basev8_candidates = []for v8 in range(base, base + 26):# 根据加密公式计算对应的原始字符encoded_value = base + v7 + v8 + 17 - 26 * (20165 * ((v7 + v8 + 17) < 0) + (20165 * (v7 + v8 + 17) >> 19))if encoded_value == encoded_char:v8_candidates.append(v8) return v8_candidates# 存储逆向替换字符后的字符串
after_aes_base64_value  = []# 对每个加密后的字符进行逆向计算
for char in ans:if 97 <= char <= 122:  # 小写字母v8_candidates = reverse_char(char, 97)elif 65 <= char <= 90:  # 大写字母v8_candidates = reverse_char(char, 65)else:# 非字母或数字字符直接添加after_aes_base64_value.append(chr(char))continueif v8_candidates:# 选择第一个候选值作为解密结果after_aes_base64_value.append(chr(v8_candidates[0]))

②、字符串重组分析与逆向

在这里插入图片描述

在UTF-8编码下,一个字符 ‘a’ 占1个字节(8位);在UTF-16编码下,‘a’ 占2个字节(16位)。分析上述代码可知,每次合并拿 64位,也就是 64 / 8 = 8 个字符, 8个字符为一组进行重组。重组后的字符串NOYKxeJRlz65XGjgTODxUvJIBdnY8NQZNQgnoK5Mxckh3fhvJjNFWoBM8wVCdfOz 长度为64,刚好可以分为8组,重组规则如下:
在这里插入图片描述
编写逆向重组代码

def reorder_string(s):# 定义序号顺序order = [1, 7, 3, 6, 4, 5, 2, 0]# 每组8个字符groups = [s[i:i+8] for i in range(0, len(s), 8)]reordered_groups = [None] * 8# 根据order将组放置到正确的位置for index, position in enumerate(order):reordered_groups[position] = groups[index]return ''.join(reordered_groups)s = "NOYKxeJRlz65XGjgTODxUvJIBdnY8NQZNQgnoK5Mxckh3fhvJjNFWoBM8wVCdfOz"
result = reorder_string(s)
print(result)
# 8wVCdfOzNOYKxeJRJjNFWoBMTODxUvJINQgnoK5Mxckh3fhvBdnY8NQZlz65XGjg

综上分析,我们即可写出 Myjni.encode 编码对应的解码函数,解码后再 AES 解密,即可得到正确的flag。 刚才动态分析已经获取到了秘钥为:Z29qZSUgYKMmYJ5fch9kZL==,静态分析找到偏移量IV为:ZmxhZ2ZsYWdyZQ==,加密方式是:AES/CBC/PKCS5Padding

故完整解题代码如下:

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64def decode(s):# 逆向重组order = [1, 7, 3, 6, 4, 5, 2, 0]groups = [s[i:i+8] for i in range(0, len(s), 8)]reordered_groups = [None] * 8for index, position in enumerate(order):reordered_groups[position] = groups[index]ascii_array = [ord(char) for char in ''.join(reordered_groups)]# 存储逆向替换字符后的字符串after_aes_base64_value  = []# 逆向替换算法for char in ascii_array:if 97 <= char <= 122:  # 小写字母v8_candidates = reverse_char(char, 97)elif 65 <= char <= 90:  # 大写字母v8_candidates = reverse_char(char, 65)else:# 非字母或数字字符直接添加after_aes_base64_value.append(chr(char))continueif v8_candidates:# 选择第一个候选值作为解密结果after_aes_base64_value.append(chr(v8_candidates[0]))return aes_decrypt(''.join(after_aes_base64_value))# 逆向函数,计算原始字符的ASCII值
def reverse_char(encoded_char, base):v7 = -basev8_candidates = []for v8 in range(base, base + 26):# 根据加密公式计算对应的原始字符encoded_value = base + v7 + v8 + 17 - 26 * (20165 * ((v7 + v8 + 17) < 0) + (20165 * (v7 + v8 + 17) >> 19))if encoded_value == encoded_char:v8_candidates.append(v8) return v8_candidates# aes 解密
def aes_decrypt(encrypted_text):try:# 将密钥和IV进行编码key = 'Z29qZSUgYKMmYJ5fch9kZL=='.encode('utf-8')iv = 'ZmxhZ2ZsYWdyZQ=='.encode('utf-8')encrypted_text_bytes = base64.b64decode(encrypted_text)cipher = AES.new(key, AES.MODE_CBC, iv)decrypted_bytes = cipher.decrypt(encrypted_text_bytes)decrypted_text = unpad(decrypted_bytes, AES.block_size).decode('utf-8')return decrypted_textexcept (ValueError, KeyError) as e:return f"解密失败: {str(e)}"s = "NOYKxeJRlz65XGjgTODxUvJIBdnY8NQZNQgnoK5Mxckh3fhvJjNFWoBM8wVCdfOz"
result = decode(s)
print(f"flag{{{result}}}")

在这里插入图片描述


0x02 总结:

通过 APK 逆向和 so 动态链接库分析,了解应用的内部逻辑和行为。静态分析帮助我们初步理解代码结构和关键点,而动态调试则允许我们在运行时获取更详细的信息。两者结合,可以有效地进行应用的逆向工程和安全分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/861391.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析京东_item_get接口:技术细节与应用场景

在电商领域&#xff0c;实时、准确的数据对于商家、开发者和消费者都至关重要。京东作为中国领先的电商平台&#xff0c;提供了丰富的API接口供开发者使用&#xff0c;其中JD.item_get接口尤为引人注目。本文将对JD.item_get接口进行深入的技术解析&#xff0c;并探讨其应用场景…

在 PHP 中,如何优化大型项目的性能,特别是在处理高并发请求时?

要优化大型项目的性能&#xff0c;特别是在处理高并发请求时&#xff0c;可以考虑以下几个方面&#xff1a; 使用缓存&#xff1a;通过使用缓存来减轻数据库和计算的压力。可以使用各种缓存技术&#xff0c;如 Memcached 或 Redis&#xff0c;将频繁使用的数据存储在缓存中&…

歌尔气压计SPA06-003在无人机的创新应用

随着科技的不断进步&#xff0c;各类智能设备的功能日益强大&#xff0c;其中气压计作为一种能够测量大气压力的传感器&#xff0c;已被广泛应用于多种领域。歌尔气压计以其高精度、低功耗的特点&#xff0c;在无人机和智能手表上的应用尤为突出&#xff0c;为这两个领域的产品…

Python的ctypes库的使用

ctypes 基本数据类型映射表 ctypes 是 Python 的外部函数库。提供了与 C 兼容的数据类型&#xff0c;并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。下面主要介绍如何使用 ctypes 模块对 C 语言编译的动态链接库要求的数据类型进行封装&am…

IPython最简洁方便的Python语法测试工具

前言和需求 相信不少人使用JS时&#xff0c;最常用的快捷键是F12。网页调试&#xff0c;不仅可以调试我们的前端页面&#xff0c;对于多数后端JS程序员来讲&#xff0c;我们有个啥语法不确定了&#xff0c;直接开个网页F12确认一下就行了。甚至写了个方法&#xff0c;不确定对…

springboot + Vue前后端项目(第十九记)

项目实战第十九记 写在前面1. redis安装(windows安装)1.1 获取软件链接地址&#xff1a;1.2 启动redis1.3 测试是否启动成功1.4 通过 Another Redis DeskTop软件可视化查看redis 2. SpringBoot集成redis2.1 引入依赖2.2 注入RedisTemplate2.3 使用redis2.4 redis更新2.5 redis使…

【自然语言处理系列】掌握jieba分词器:从基础到实战,深入文本分析与词云图展示

本文旨在全面介绍jieba分词器的功能与应用&#xff0c;从分词器的基本情况入手&#xff0c;逐步解析全模式与精确模式的不同应用场景。文章进一步指导读者如何通过添加自定义词典优化分词效果&#xff0c;以及如何利用jieba分词器进行关键词抽取和词性标注&#xff0c;为后续的…

美创携手浙江长征职业技术学院,共建智云数据安全大师工作室

6月24日&#xff0c;“美创科技浙江长征职业技术学院智云数据安全大师工作室揭牌暨中国特色学徒制第四期云数据安全和智能运维人才选拔培养启动仪式”在长征职业技术学院隆重举行。 浙江长征职业技术学院计算机与信息技术学院院长梅灿华、计算机与信息技术学院学工办副主任华春…

秋招Java后端开发冲刺——基础篇3

一、Java泛型 Java 1.5中引入的一个新特性&#xff0c;其本质是参数化类型&#xff0c;可以增强代码的可读性以及稳定性。分类 泛型类&#xff1a;在类定义时类名后加&#xff0c;其中T表示泛型泛型接口&#xff1a;与泛型类相似泛型方法&#xff1a;静态泛型方法是没有办法使…

使用 frida hook Android app

Frida&#xff1a; 一种基于动态插装&#xff08;dynamic instrumentation&#xff09;技术的工具包&#xff0c;它主要是为测试人员、开发人员和逆向工程爱好者创建&#xff0c;在目标程序运行时&#xff0c;允许用户将 JavaScript代码注入其中&#xff0c;实现动态修改和调试…

ONLYOFFICE8.1版本桌面编辑器的测评

首先我们先出示一下我们所测评官网的链接&#xff1a; ONLYOFFICE官网链接&#xff1a;ONLYOFFICE - 企业在线办公应用软件 | ONLYOFFICE 我们这款ONLYOFFICE8.1版本有这一下优点 1.解决PDF痛点 ONLYOFFICE在PDF编辑方面支持高亮显示、下划线和删除线、添加批注等功能&#…

【2024最新版】Eclipse安装配置全攻略:图文详解

目录 1. Eclipse介绍1.1 背景1.2 主要特点和功能1.3 版本发布1.4 优势与劣势 2. 下载Eclipse3. 安装Eclipse4. 启动Eclipse 1. Eclipse介绍 Eclipse是一个开源的、基于Java的可扩展开发平台&#xff0c;主要用于Java开发者&#xff0c;但也支持其他语言如C/C、PHP、Python等。…

【PWN · TcachebinAttack | UAF】[2024CISCN · 华中赛区] note

一道简单的tcache劫持 一、题目 二、思路 存在UAF&#xff0c;libc版本2.31&#xff0c;经典菜单题 1.通过unsorted-bin-attack来leak-libc 2.通过uaf打tcache-bin-attack劫持__free_hook实现getshell 三、EXP from pwn import * context(archamd64,log_leveldebug)ioproce…

ansible自动化运维,(2)ansible-playbook

三种常见的数据格式&#xff1a; XML&#xff1a;可扩展标记语言&#xff0c;用于数据交换和配置 JSON&#xff1a;对象标记法&#xff0c;主要用来数据交换或配置&#xff0c;不支持注释 YAML&#xff1a;不是一种标记语言&#xff0c;主要用来配置&#xff0c;大小写敏感&…

Unity太空避障Demo总结

太空避障&#xff1a;主要是实现飞机躲避子弹 面板基类、音乐类、排行榜类、json等等都和上一篇Unity坦克迷宫Demo总结一样&#xff0c;太空避障主要是对四元数的练习和使用 1.选择飞机面板 &#xff08;1&#xff09;通过左右按钮对显示的模型进行切换 &#xff08;2&#xff…

科技未来·无限可能“2024世亚智博会”

随着科技的飞速发展&#xff0c;人类社会正以前所未有的速度迈向一个全新的时代。科学技术作为第一生产力&#xff0c;不仅极大地推动了经济和社会的发展&#xff0c;更在不断地改变着我们的生活方式和思维方式。特别是在人工智能、物联网等前沿科技领域&#xff0c;其创新和应…

【ZYNQ】VDMA 的介绍

AXI VDMA 是 Xilinx 官方提供的高带宽视频 DMA IP&#xff0c;用于实现 AXI4-Stream 视频数据流与 AXI4 接口数据的转换&#xff0c;同时提供帧缓存与帧同步控制功能。本文主要介绍 AXI VDMA 的基本结构与原理&#xff0c;并简要介绍 VDMA 的配置与使用方法。 目录 1 VDMA 简介…

egg代码生成器

今天给大家分享一下egg的代码生成器&#xff0c;这个其实原理很简单&#xff0c;说白了就是用到了nodejs的一个文件io的操作&#xff0c;通过一系列配置参数解析然后生成一个很长的字符串&#xff0c;写入到文件中&#xff0c;最后导出到我们指定的文件夹。 前提概要 为什么我…

web前端——VUE

1.什么是框架&#xff1f; ①概述 框架结构就是基本功能&#xff0c;把很多基础功能已经实现了、封装了。在基础语言之上&#xff0c;对各种基础功能进行封装&#xff0c;方便开发者&#xff0c;提高开发效率 ②前端框架 javaScript是原生的 vue.js&#xff1a; 是一个js框架&…

【Java核心技术16】Java异常处理机制:深入探索与实践

Java异常处理机制&#xff1a;深入探索与实践 所有文章均为原创验证&#xff0c;您随手的 关注、点赞、收藏 是我创作最大的动力。 在软件开发中&#xff0c;异常处理是一个不可或缺的部分&#xff0c;它允许程序在遇到错误时能够优雅地处理&#xff0c;而不是直接崩溃。在Java…