2024届龙信杯电子数据取证题解-手机取证部分

文摘   2024-10-22 14:43   北京  
        本文主要是针对比赛题目进行分析和解题思路分享,只进行知识分享,不具一定的实战能力,后台不解答涉及可能侵害他人权利的问题,切勿用于违法犯罪活动。如果有工作方面的解答需求,请后台联系添加微信私聊。
        本次比赛的手机部分涉及的知识范围广,有手机取证基础、APP分析、犯罪组织层级分析等,重点考查了APP逆向分析。积累比赛经验:遇到APP逆一下总没错。

本题所需知识点

ADB

当我们在PC上启动adb进程的时候,adb进程会在本地生成一对秘钥 adbkey 和 adbkey.pub根据弹框提示 The computer’s RSA key fingerprint is:xxx ,可以看出是一对RSA算法的秘钥,其中公钥是来发送给android设备的.

当你执行 adb shell 的时候,adb进程会将PC上的adbkey.pub发送给android设备;这个时候如果android上已经保存了这台PC的adbkey.pub ,则连接成功,如果没有保存则会出现弹框,当你点击允许之后android设备就会保存PC的 adbkey.pub


手机中会存储adb连接的公钥,要知道adb连接过几个设备只需要找到adb存储的公钥有几个就行

adb key存放位置:

  • android设备 : /data/misc/adb/adb_keys // 将PC的adbkey.pub 拷贝并且重命名而来

  • windows: C:\Users\Administrator.android\adbkey

  • Linux: ~/.android/adbkey

每行存储一个key

SIM卡

Android手机的SIM卡使用记录一般保存在data分区的telephony.db文件中,该文件也是SQLite数据库。表“sim_info”会记录使用过的SIM卡的ICCID、手机号等信息。

IOS的SIM卡使用记录一般保存在CellularUsage.db文件中,SIM卡历史记录保存在表“subscriber_info”中。

对盗取敏感信息类APP分析的一些小认识

这类APP将获取到的敏感信息打包成文件通过网络传输,在jadx中分析的时候可用查找关键词:

  • file:涉及到文件的操作,一定会涉及到FileInputStream、FileoutputStream两个函数

  • url、email:涉及网络传输,必须得有一个目的地,可能是url,也有可能是邮箱

在File相关函数中发现变量s0、u0,这两个变量是加密的,实际上能看到很多变量的值是加密的,说明APP作者不想让别人轻易看到这些内容,同时这些加密变量大量出现在FileOutputStream函数中

加解密在java中通常会引入Cipher这个包,我们查找cipher这个包的用例,发现b.b.a下a函数


在打包文件的过程中发现多个该函数的用例,并调用了加密变量

在CyberChef尝试解密这些加密变量,解密成功

组织层级分析

7-10题是对手机加密容器data中的名单数据.xlsx进行层级的分析

观察表的结构就会发现:

  • 被邀请人与邀请人构成指向关系

  • “邀请人”的值存在重复,“邀请人”与“姓名”也存在重复。

  • 每行”姓名“都对应一个非空的”邀请人“值。所以root结点不是简单的无邀请人,而且表中可能不只有一个根结点。

这里考虑到使用有向树(https://baike.baidu.com/item/有向树/6919420)来处理数据

我采用pandas库来读取excel中的数据,networkx库来便捷化实现有向图

# 作者:WelK1n

# 时间:2024/10/14

import pandas as pd
import networkx as nx

# 读取 Excel 数据
file_path = "J:/名单数据.xlsx"  # 请替换为实际的文件路径
data = pd.read_excel(file_path)

# 构建有向图
G = nx.DiGraph()

# 读取xlsx中的关键数据并创建图结点
for index, row in data.iterrows():
   if pd.notna(row['邀请人']):  # 确保邀请人不为空
       name = row['姓名']
       inviter = row['邀请人']
       phone = row['手机号']
       inviter_phone = row['邀请人手机号']

       # 添加节点(确保唯一性)
       G.add_node(phone, label=name)  # 姓名对应手机号
       G.add_node(inviter_phone, label=inviter)  # 邀请人对应邀请人手机号

       # 添加边
       G.add_edge(inviter_phone, phone)  # 从邀请人到被邀请人


# 递归函数来打印总层级关系树,统计层级和节点信息
def print_tree(node, graph, level=0, level_info=None):
   indent = " " * (level * 4)  # 控制缩进
   name = graph.nodes[node]['label']
   children = list(graph.successors(node))
   child_count = len(children)  # 子节点数量

   result = f"{indent}- {name} (子节点数: {child_count})\n"  # 打印当前节点和子节点数量

   # 统计层级信息
   if level not in level_info:
       level_info[level] = {'nodes': [], 'leaf_count': 0, 'children_count': {}}
   level_info[level]['nodes'].append(name)  # 添加当前节点到层级信息
   level_info[level]['children_count'][name] = child_count  # 记录节点的子节点数量

   if len(children) == 0:  # 如果没有子节点,则为叶子节点
       level_info[level]['leaf_count'] += 1  # 增加叶子节点计数

   for neighbor in children:
       result += print_tree(neighbor, graph, level + 1, level_info)  # 递归调用并累加结果

   return result


# 找到所有根节点(没有入边的节点)
root_nodes = [node for node in G.nodes if G.in_degree(node) == 0]

# 保存总层级关系树为“总层级关系树.txt”
with open('总层级关系树.txt', 'w', encoding='utf-8') as f:
   for root in root_nodes:
       f.write(print_tree(root, G, level_info={}))

# 统计层级数据
level_data = {}
for root in root_nodes:
   level_info = {}
   print_tree(root, G, level_info=level_info)  # 获取每个根节点的层级信息
   for level, info in level_info.items():
       if level not in level_data:
           level_data[level] = {'nodes': [], 'leaf_count': 0, 'children_count': {}}
       level_data[level]['nodes'].extend(info['nodes'])  # 添加该层所有节点
       level_data[level]['leaf_count'] += info['leaf_count']  # 统计叶子节点总数
       level_data[level]['children_count'].update(info['children_count'])  # 统计每个节点的子节点数量

# 保存层级数据分析为“层级数据分析.txt”
with open('层级数据分析.txt', 'w', encoding='utf-8') as f:
   for level in sorted(level_data.keys()):
       # 对每个节点的下线(子节点)数量按降序排序
       sorted_children = sorted(level_data[level]['children_count'].items(), key=lambda x: x[1], reverse=True)
       nodes_info = [f"{node} (子节点数: {count})" for node, count in sorted_children]
       nodes = ", ".join(nodes_info)
       leaf_count = level_data[level]['leaf_count']
       f.write(f"层级 {level + 1}: 节点数 = {len(level_data[level]['nodes'])},子节点数量 = {sorted_children}, 叶子节点数量 = {leaf_count}, 节点 = [{nodes}]\n")

# 5. 递归函数,从被邀请人出发向上查找邀请人,并打印层级关系
def search_inviter(node, graph, level=0):
   indent = " " * (level * 4)  # 控制缩进
   name = graph.nodes[node]['label']
   result = f"{indent}- {name}\n"  # 打印当前节点

   # 获取邀请人
   parents = list(graph.predecessors(node))  # 邀请人在有向图中是前驱节点
   if len(parents) > 0:  # 如果有邀请人
       inviter = parents[0]
       result += search_inviter(inviter, graph, level + 1)  # 递归调用

   return result


# 找到所有被邀请人(即存在入边的节点)
invited_nodes = [node for node in G.nodes if G.out_degree(node) > 0]

# 保存被邀请人视角的层级关系为“被邀请人视角的层级关系.txt”
with open('被邀请人视角的层级关系.txt', 'w', encoding='utf-8') as f:
   for invited in invited_nodes:
       f.write(search_inviter(invited, G))

print("ok!")

生成两个txt文件:

  • 总层级关系树.txt:树形记录上下级关系

  • 层级数据分析.txt:针对不同层级,统计具体的数据。

    • 节点数:统计本层级共有多少个节点

    • 子节点数量:统计每层每个节点有多少子节点,就是邀请人有多少个被邀请人/下线,我这里采用了sort排序来降序输出子节点数量,即下线数量最多的人优先靠前。

    • 叶子节点数量:我认为有分析价值,意味着一个人有几个没有下线的下线

    • 节点:输出该层级的所有节点

  • 被邀请人视角的层级关系.txt:从被邀请人视角出发,去看每一个被邀请人的上级,以及上级的上级

题目解析

检材处理

该镜像实际上是tar文件,使用压缩软件打开,可以直接解压出来

1.分析手机检材,请问此手机共通过adb连接过几个设备?[标准格式:3]

2

2.分析手机检材,机主参加考试的时间是什么时候?[标准格式:2024-06-17]

2024-08-23

3.分析手机检材,请问手机的蓝牙Mac地址是多少?[标准格式:12:12:12:12:12:12]

48:87:59:76:21:0f

4.分析手机检材,请问压缩包加密软件共加密过几份文件?[标准格式:3]

6

找到压缩包加密软件FileCompress,apk丢jadx分析

doZipSingleFileWithPassword()函数给压缩包设置密码1!8Da9Re5it2b3a.

X-ways浏览递归,搜索filecompress

用刚刚找到的密码成功解压这些加密压缩包

5.分析手机检材,请问机主的另外一个155的手机号码是多少?[标准格式:15555000555]

15599555555

6.分析手机检材,其手机存在一个加密容器,请问其容器密码是多少。[标准格式:abc123]

7.分析手机检材,接上问,其容器中存在一份成员名单,嫌疑人曾经误触导致表格中的一个成员姓名被错误修改,请确认这个成员的原始正确姓名?[标准格式:张三]

加密容器,文件大小过滤mod512,data就是加密容器

需要用TC模式解密

llzp.jpg放在这里就很可疑,实际上也是隐写了xlsx文件,但我分离出来的存在问题。希望大佬评论区讲解一下

还有一个取巧的办法(也容易弄巧成拙),office类软件都有一个记忆功能,打开office文档后会定位到上一次操作过的位置

这里打开后就是2414行,搜索陆陆的手机号发现实际叫陆俊梅

恢复表后,使用上文中的python脚本做层级分析就可以了

8.分析手机检材,接上题,请确认该成员的对应的最高代理人是谁(不考虑总部)?[标准格式:张三]

去看“被邀请人视角的层级关系.txt”

9.分析手机检材,请确认在该组织中,最高层级的层次是多少?(从总部开始算第一级)[标准格式:10]

12级

10.分析手机检材,请问第二层级(从总部开始算第一级)人员最多的人是多少人?[标准格式:100]

这道题的题目有点歧义,我认为是问第二层级下线人员最多的人是有多少个下线,所以这里答60人

11.分析手机检材,机主共开启了几款APP应用分身?[标准格式:3]

找到“隐空间”的包,里面有个fenshen.db(复现的时候龙信的工具到期了,没法直接贴图出来)

存在两个分身

12.分析手机检材,请问机主现在安装了几款即时通讯软件(微博除外)?[标准格式:]

除微博外两个

13.分析手机检材,请问勒索机主的账号是多少(非微信ID)?[标准格式:AB123CD45]

1836042664454131712

14.分析手机检材,接上问,请问机主通过此应用共删除了多少条聊天记录 ?[标准格式:2]

由上图,1条

15.分析手机检材,请问会盗取手机信息的APP应用包名是什么?[标准格式:com.lx.tt]

com.lxlxlx.luoliao

16.接上题,请问该软件作者预留的座机号码是多少?[标准格式:40088855555]

40085222666

w0和x0单独直接解密是不行的,查看用例后发现是连接在一起,解密后那一串数字应该就是电话号码了

17.接上题,恶意程序偷取数据的收件邮箱地址的gmail邮箱是多少?[标准格式:lx@gmail.com]

1304567895@gmail.com

18.接上题,恶意程序偷取数据的发件邮箱地址是多少?[标准格式:lx@gmail.com]

temp1234@gmail.com

定位package b.b.a.g;查看aVar.a(str, "见附件", arrayList, arrayList2);,该函数是发送邮件函数

加密的变量d解密后

19.接上题,恶意程序偷取数据的发件邮箱密码是多少?[标准格式:abc123]

qwer123456

加密变量e解密后就是密码

20.接上题,恶意程序定义收发件的地址函数是什么?[标准格式:a]

b








金星路406取证人
这是位于大兴区黄奕路金星406的取证爱好者,这是一个致力于学习电子数据取证的团体~~
 最新文章