CUCKOO沙箱源码分析 中篇
2020-06-15 16:58:02 Author: bbs.pediy.com(查看原文) 阅读量:448 收藏

前言

这是讲述cuckoo源码分析的第二篇文章, 主要讲述host端的分析模块.

回顾

上一篇文章,主要将cuckoo执行分析模块前的准备工作介绍了一番, 准备工作如下:

设置工作目录(CWD) --> 工作目录存放包括样本信息, 分析结果, yara规则, 配置文件等在内的重要内容.
设置cuckoo服务端 --> 设置一个tcp 服务端, 但也可以作为http服务端来使用. 后面会提到通过http服务,将主要分析代码和样本传入虚拟机中.
加载配置 --> 如: 虚拟机的类型, ip, 端口, 数据库, yara规则
检查虚拟机的相关信息(如: 软件类型, 虚拟镜像名称, 快照)

分析模块之AnalysisManger

AnalysisManager类

AnalysisManager 继承于threading.Thread类, 上篇结尾执行了start函数, 那么在AnalysisManager类中就应该查看对应的run函数.

构造函数

# 构造函数
def __init__(self, task_id, error_queue):
    """@param task: task object containing the details for the analysis."""
    threading.Thread.__init__(self)

    self.errors = error_queue
    self.cfg = Config()
    self.storage = ""
    self.binary = ""
    self.storage_binary = ""
    self.machine = None
    self.db = Database() # 需要获取样本的信息,而样本的信息存储在数据库中, 自然要连接数据库了.
    # 这里说明一下: 上传样本可以通过web界面或submit命令

    self.task = self.db.view_task(task_id) # 查看任务的信息
    # timeout --> 指明分析时间 platform --> 系统类型Windows, Linux, Android
    # started_on --> 开始时间 completed_on --> 结束时间(分析结束之后进行填写)
    # status --> 指明分析状态, 如pending, running, completed, reported, recovered, failed等
    self.guest_manager = None
    self.route = None
    self.interface = None
    self.rt_table = None
    self.unrouted_network = False
    self.stopped_aux = False
    self.rs_port = config("cuckoo:resultserver:port") # 读取配置文件中的服务端ip和端口, 我这里设置的是192.168.56.1和2042

run函数

# 主要代码
self.launch_analysis() # 重要, 展开介绍

if self.cfg.cuckoo.process_results:
            self.store_task_info() # 存储任务结果, 存储在task.json文件中.
            self.db.set_status(self.task.id, TASK_COMPLETED) # 设置task对应的状态为completed
            self.process_results()  # 处理虚拟机运行样本的结果, 这个是流程的最后一步, 放在最后面介绍(第二个坑)

# 在CWD/storage/analyses文件夹下创建lastest符号链接, 指向最新的分析结果文件夹
latest = cwd("storage", "analyses", "latest")
latest_symlink_lock.acquire()
if os.path.lexists(latest):
    os.remove(latest)
    os.symlink(self.storage, latest)

launch_analysis函数

# 初始化分析, 包括:
# 1. 创建文件夹, 用于存放分析结果和样本文件 
# 2. 将target指向的文件存放到storage/binaries下
self.init():

# 获取虚拟机资源
self.acquire_machine()

# 添加host的ip与任务id的映射
ResultServer().add_task(self.task, self.machine)

# 初始化client端, 参数为(虚拟机名称, ip, 平台, task.id)
# self.guest_manaer函数会调用self.guest_manager.start_analysis函数(详解见下文)开始分析.
self.guest_manager = GuestManager(
    self.machine.name, self.machine.ip,
    self.machine.platform, self.task.id, self
)

# 其他辅助性配置加载
# mitm.py --> https代理配置, mitmdump是mitmproxy的命令行配置
# reboot.py --> 重启, 不知道有啥用.
# replay.py --> 流量重放
# service.py --> 部署诱使样本攻击的服务, 例如: 部署蜜罐环境.
# sniffer.py --> 流量抓取, 用于流量分析
RunAuxiliary(self.task, self.machine, self.guest_manager)
self.aux.start()

# 将当前配置存入options中, options是一个字典类型
options = self.build_options()

# 远程桌面. 这个需要在cuckoo.conf中进行设置, 还需要安装其他软件, 比较麻烦.
machinery.enable_remote_control(self.machine.label)

# 设置guests表中的信息, 
guest_log = self.db.guest_start(self.task.id,
                                        self.machine.name,
                                        self.machine.label,
                                        machinery.__class__.__name__)

# id = 2行是 self.db.guest_start执行后的添加的, 设置client的初始信息.
+----+---------+----------------+----------------+------------+---------------------+---------------------+---------+
| id | status  | name           | label          | manager    | started_on          | shutdown_on         | task_id |
+----+---------+----------------+----------------+------------+---------------------+---------------------+---------+
|  1 | stopped | cuckoo_win_x64 | cuckoo_win_x64 | VirtualBox | 2020-06-14 14:40:26 | 2020-06-14 14:40:32 |       1 |
|  2 | init    | cuckoo_win_x64 | cuckoo_win_x64 | VirtualBox | 2020-06-14 15:31:05 | NULL                |       4 | --> 新增的行
+----+---------+----------------+----------------+------------+---------------------+---------------------+---------+

# 开启虚拟机分, 如: machinery/virtualbox.py 中 VirtualBox.start
# machinery对象阿赋值可以在cuckoo源码分析的上篇中找到, 我本地使用的是VirtualBox类的对象, 还可以是VMware, Qemu的
machinery.start(self.machine.label, self.task)

# 路由配置 vpn, tor, 
self.route_network()

if "noagent" not in self.machine.options:
    self.guest_manage(options) # 重要函数,这个函数的主要是调用self.guest_manager.start_analysis.
else:
    self.wait_finish()


# finally 部分做一些清理工作:
# 如: self.aux.stop() --> 辅助模块的清理工作
# 如:  dump_path = os.path.join(self.storage, "memory.dmp")
#      machinery.dump_memory(self.machine.label, dump_path) --> 内存镜像的dump, 估计只有内存取证的时候需要, 平常不怎么需要.
# 如: machinery.stop --> 停止虚拟机
# 如: ResultServer().del_task(self.task, self.machine) --> 删除ip与任务id的映射.

分析模块之GuestManager

start_analysis

参数: options --> 分析的配置文件, monitor --> 'lastest'字符串.

# client 端也开启了http server, 获取agent(配置的时候,需要在虚拟机中放置agent.py)的信息.
r = self.get("/", do_raise=False)

# 老版的cuckoo
if r.status_code == 501:
        self.is_old = True
        self.aux.callback("legacy_agent")
        self.old.start_analysis(options, monitor)
        return

# 获取agent的version, features
status = r.json()
version = status.get("version")
features = status.get("features", [])

# 获取环境变量
# {"message": "Environment variables", "environ": {"TMP": "C:\\Users\\bill\\AppData\\Local\\Temp", "COMPUTERNAME": "BILL-PC", "USERDOMAIN": "bill-PC", ....}}
self.query_environ()

# 通过http协议,上传分析模块, 可以抓包来进行验证.
self.upload_analyzer(monitor) # 重点展开介绍

# 将options中的内容传入client中, 写入到self.analyzer_path的analysis.conf中.
self.add_config(options)

# 将mitm, reboot, replay, service, sniffer等, 这些额外的分析或功能初始化, 与任务对接.
self.aux.callback("prepare_guest")

# 如果分析的内容为文件, 传输文件(样本)
if options["category"] == "file" or options["category"] == "archive":
        data = {
            "filepath": os.path.join(
                self.determine_temp_path(), options["file_name"]
            ),
        }
        # target中存放的是样本的路径
        files = {
            "file": ("sample.bin", open(options["target"], "rb")),
        }
        self.post("/store", files=files, data=data)


if "execpy" in features:
        data = {
            "filepath": "%s/analyzer.py" % self.analyzer_path,
            "async": "yes",
            "cwd": self.analyzer_path,
        }
        # 执行execpy命令 --> 在系统中执行python analyzer.py

        self.post("/execpy", data=data)
    else:
        # Execute the analyzer that we just uploaded.
        data = {
            "command": "C:\\Python27\\pythonw.exe %s\\analyzer.py" % self.analyzer_path,
            "async": "yes",
            "cwd": self.analyzer_path,
        }
        # 执行execute命令, execute(command)
        self.post("/execute", data=data)

upload_analyzer

根据操作系统类型(Windows, Linux), 上传分析模块.

def upload_analyzer(self, monitor):

    # 根据平台, 将对应分析模块进行压缩.
    # 分析模块的文件位于cuckoo/cuckoo/data/analyzer/(android, darwin, linux, windows)
    # 上篇文章中讲到了,init_yara函数compile yara规则为dumpmem.yarac, analyzer_zipfile也会将
    # dumpmem.yarac写入到压缩文件流中.
    zip_data = analyzer_zipfile(self.platform, monitor)

    log.debug(
        "Uploading analyzer to guest (id=%s, ip=%s, monitor=%s, size=%d)",
        self.vmid, self.ipaddr, monitor, len(zip_data)
    )
    # 填充self.analyzer_path内容, 这个路径存放分析模块的内容, 一般是tmp目录
    self.determine_analyzer_path()
    data = {
        "dirpath": self.analyzer_path,
    }
    # 发送extract命令, 令client提取其中的文件
    self.post("/extract", files={"zipfile": zip_data}, data=data)

待续

由于analyzer.py是运行在guest端的, 与客户端联系更紧密, 故放到下篇文章中介绍. 下篇文章将介绍cuckoo在客户端的操作.包括如下内容:

  • 如何将结果传输给host.
  • Client如何运行样本
  • 如何获取样本的行为.(如: 注册表操作, 文件操作, 网络行为)
  • 等其他问题(可能分为两篇:Python方面的讲解和驱动方面的讲解)

[培训]《安卓高级研修班(网课)》9月班招生(限员35人)!一年后遇见不一样的自己!


文章来源: https://bbs.pediy.com/thread-260087.htm
如有侵权请联系:admin#unsafe.sh