angr源码分析——cle.Loader类

上篇文章分析angr.Project类时说到,angr工具第一步就是将二进制文件加载到cle.loader类里。(CLE means Loading Everything)。loader类加载所有的对象并导出一个进程内存的抽象。生成该程序已加载和准备运行的地址空间。构造函数参数:cle.loader.Loader(main_binary, auto_load_libs=True, force_lo...
摘要由CSDN通过智能技术生成

上篇文章分析angr.Project类时说到,angr工具第一步就是将二进制文件加载到cle.loader类里。(CLE means Loading Everything)。loader类加载所有的对象并导出一个进程内存的抽象。生成该程序已加载和准备运行的地址空间。

构造函数参数:

cle.loader.Loader(main_binary, auto_load_libs=True, force_load_libs=(), skip_libs=(), main_opts=None, lib_opts=None, custom_ld_path=(), use_system_libs=True, ignore_import_version_numbers=True, case_insensitive=False, rebase_granularity=16777216, except_missing_libs=False, aslr=False, page_size=1, extern_size=32768)

    :param main_binary:         要加载主要二进制文件的路径,或者一个带有二进制文件的对象。
    以下参数是可选的.
    :param auto_load_libs:      是否自动加载已加载对象所依赖的共享库.
    :param force_load_libs:     无论加载对象是否需要,都加载的库列表.
    :param skip_libs:           即使加载对象需要也不会加载的库列表.
    :param main_opts:           一个字典,加载主二进制文件的选项.
    :param lib_opts:            一个字典,将库名称映射到加载它们时要使用的选项字典.
    :param custom_ld_path:      用于搜索共享库的路径列表.
    :param use_system_libs:     是否搜索所请求库的系统加载路径,默认为true.
    :param ignore_import_version_numbers: 文件名中具有不同版本号的库是否被认为是等效的,例如libc.so.6和libc.so.0。
    :param case_insensitive:    如果将其设置为True,则无论底层文件系统的区分大小写如何,文件系统加载都将以不区分大小写的方式进行。
    :param rebase_granularity:  用于重新分配共享对象的对齐方式。
    :param except_missing_libs: 当找不到共享库时抛出异常.
    :param aslr:                在符号地址空间中加载库。 不要使用这个选项(???)。
    :param page_size:           数据映射到内存的粒度。 如果您在非分页环境中工作,请将其设置为1。
一些变量解释:
    :ivar memory:               程序加载,重定位的内存。
    :vartype memory:            类型为cle.memory.Clemory类
    :ivar main_object:          代表了主要的二进制文件对象
    :ivar shared_objects:       一个字典,映射了名名称和它们代表的对象.
    :ivar all_objects:             一个列表,代表了所有被加载的不同对象.
    :ivar requested_names:      一个集合,包含了所有没标记为某个部分的依赖的不同的共享库.
    :ivar initial_load_objects:   一个列表,代表所有初始加载请求结果的所有对象.
当引用字典选项时,它需要一个包含零个或多个以下key的字典:
     - backend(后端):“elf”,“pe”,“mach-o”,“ida”,“blob”:后端使用的加载器
     - custom_arch:用于二进制文件的archinfo.Arch对象
     - custom_base_addr:重新绑定对象的地址

     - custom_entry_point:用于对象的入口点

更多的key在每个后端的基础上定义。

执行流程:

第一步:参数初始化。在构造函数中,主要就是初始化各个参数,这部分在后面有些参数需要格外关注时可以再仔细分析。

第二步:加载对象初始化。构造函数的最后一行代码为

self.initial_load_objects = self._internal_load(main_binary, *force_load_libs)

这里调用了函数_internal_load(),跟进去看对象初始化的逻辑。

这个函数的参数为传入要加载的文件或库。如果由于任何原因无法加载其中的任何一个,则会退出。

它将返回一个成功加载的所有对象的列表,如果其中它们任何被加载过,那么返回的列表会比你提供的列表小。

下面这段函数看一下:

for main_spec in args:
            if self.find_object(main_spec, extra_objects=objects) is not None:
                l.info("Skipping load request %s - already loaded", main_spec)
                continue
            l.info("loading %s...", main_spec)
            main_obj = self._load_object_isolated(main_spec)
            objects.append(main_obj)
            dependencies.extend(main_obj.deps)

            if self.main_object is None:
                self.main_object = main_obj
                self.memory = Clemory(self.main_object.arch, root=True)

main_spec 就是传入的二进制文件或库。循环中,先判断的是这个对象是否加载过,若加载过则不再加载。加载一个文件时调用的函数为 _load_object_isolated()。这个函数给定一个依赖的部分说明,它将会把加载对象作为一个后端实例返回。它不会触及任何加载器全局数据。再跟进这个函数看下。

# STEP 1: identify file
        if isinstance(spec, Backend):
            return spec
        elif hasattr(spec, 'read') and hasattr(spec, 'seek'):
            full_spec = spec
        elif type(spec) in (bytes, unicode):
            full_spec = self._search_load_path(spec) # this is allowed to cheat and do partial static loading
            l.debug("... using full path %s", full_spec)
        else:
            raise CLEError("Bad library specification: %s" % spec)

        # STEP 2: collect options
        if self.main_object is None:
            options = self._main_opts
        else:
            for ident in self._possible_idents(full_spec): # also allowed to cheat
                if ident in self._lib_opts:
                    options = self._lib_opts[ident]
                    break
            else:
                options = {}

        # STEP 3: identify backend
        backend_spec = options.pop('backend', None)
        backend_cls = self._backend_resolver(backend_spec)
        if backend_cls is None:
            backend_cls = self._static_backend(full_spec)
        if backend_cls is None:
            raise CLECompatibilityError("Unable to find a loader backend for %s.  Perhaps try the 'blob' loader?" % spec)

        # STEP 4: LOAD!
        l.debug("... loading with %s", backend_cls)
        return backend_cls(full_spec, is_main_bin=self.main_object is None, loader=self, **options) #它就是main_oject 调用的是backend的init函数
第一步:识别文件。确认这是一个二进制文件,并调用_search_load_path()函数获取一个完整的文件路径。这部分是允许欺骗并做部分静态加载的。
第二步:收集选项options。在这一步,调用了_possible_idents()函数,对刚刚获得full_spec进行识别,它遍历所有可能用于描述给定spec的识别符。

第三步:识别后端。首先从options中获取backend,并初始化一个backend_cls变量,如果该变量为空,则调用_static_backend()函数获取backend_cls实例。

也就是说,真正对二进制文件识别的函数为_static_backend(),再次跟进看其逻辑。

 def _static_backend(self, spec):
        """
        Returns the correct loader for the file at `spec`.
        Returns None if it's a blob or some unknown type.
        TODO: Implement some binwalk-like thing to carve up blobs automatically
        """

        try:
            return self._backend_resolver(self._lib_opts[spec]['backend'])
        except KeyError:
            pass

        with stream_or_path(spec) as stream:
            for rear in ALL_BACKENDS.values():
                if rear.is_default and rear.is_compatible(stream):
                    return rear

        return None

该函数根据spec返回二进制文件的正确加载器。如果是blob或者其他未知类型则返回none。其中代码中允许我们实现能够识别和分割blob类型二进制文件的代码。(这个重点哎!)

识别后端利用的是特征匹配吧,首先加载刚刚遍历提取得到的spec,然后与ALL_BACKENDS的值进行匹配,及rear变量,调用rear.is_compatible()函数。所以我们要去看一下ALL_BACKENDS都是些什么,is_compatible()函数的匹配逻辑。而且我们的目标是要解

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,angr 中支持使用蒙特卡洛树搜索(Monte Carlo Tree Search,MCTS)策略进行路径探索。具体来说,angr 提供了一个名为 MCTSExplorer 的探索技术,可以使用蒙特卡洛树搜索策略来探索程序路径。MCTSExplorer 技术会在每个基本块中执行多次符号执行,并根据执行结果动态调整探索策略,以尽可能地覆盖更多的路径。 以下是使用 MCTSExplorer 探索技术进行路径探索的示例代码: ```python import angr # 创建一个 angr 项目 proj = angr.Project('/path/to/binary') # 初始化一个初始状态 state = proj.factory.entry_state() # 初始化一个 MCTSExplorer 探索技术 mcts_explorer = angr.exploration_techniques.MCTSExplorer( num_samples=10, # 每个基本块执行的符号执行次数 num_rounds=5, # 蒙特卡洛树搜索的迭代次数 heuristic=angr.exploration_techniques.ExplorerHeuristic(), # 探索策略 ) # 开始探索 path_group = proj.factory.path_group(state, immutable=False) path_group.use_technique(mcts_explorer) path_group.run() # 输出探索结果 print("Total branches explored:", path_group.branches_explored) print("Total basic blocks explored:", path_group._stashes['deadended'].size()) ``` 在这个示例代码中,我们创建了一个 angr 项目对象以及一个初始状态对象。然后,我们初始化了一个 MCTSExplorer 探索技术,其中 num_samples 参数用于指定每个基本块执行的符号执行次数,num_rounds 参数用于指定蒙特卡洛树搜索的迭代次数,heuristic 参数用于指定探索策略。接着,我们将 MCTSExplorer 技术添加到路径组对象中,并执行路径探索。最后,我们输出了探索结果。 需要注意的是,使用蒙特卡洛树搜索策略进行路径探索可能会非常耗时,因此需要根据具体情况调整 num_samples 和 num_rounds 参数的值,以平衡探索效率和探索质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值