【angr源码分析】0. Project

Project类

__init__方法

第一步:加载程序,从指定的路径打开程序,调用CLE.Loader加载程序

1
2
3
4
5
6
7
8
def __init__(......):
......
......
else:
# use angr's loader, provided by cle
l.info("Loading binary %s", thing)
self.filename = thing
self.loader = cle.Loader(self.filename, **load_options)

第二步:判断系统的架构,根据用户的输入获得架构,或者直接获得加载的架构

1
2
3
4
5
6
7
8
if isinstance(arch, str):
self.arch = archinfo.arch_from_id(arch) # may raise ArchError, let the user see this
elif isinstance(arch, archinfo.Arch):
self.arch = arch
elif arch is None:
self.arch = self.loader.main_object.arch
else:
raise ValueError("Invalid arch specification.")

第三步:获得默认信息,初始化一些属性

1
2
3
4
5
......
self.entry = self.loader.main_object.entry
self.storage = defaultdict(list)
self.store_function = store_function or self._store
self.load_function = load_function or self._load

第四步:建立project的插件集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#	1.Engines。获得loader,arch的预设值,或者默认值
# 没看懂,不知道engines是什么
# 似乎也设置了factory
engines = EngineHub(self)
...
self.engines = engines
self.factory = AngrObjectFactory(self)

# 2.Anylyses
self.analyses = AnalysesHub(self)
...

# 3.等等
self.surveyors = Surveyors(self)
...

其中,详细信息在:EngineHubFactory

第五步:判断操作系统(解决输入输出问题,SimOS)

如果没有设置默认的simos,那么调用os_mapping方法。SimOS.md

1
2
3
4
if isinstance(simos, type) and issubclass(simos, SimOS):
self.simos = simos(self) #pylint:disable=invalid-name
elif simos is None:
self.simos = os_mapping[self.loader.main_object.os](self)

第六步:根据库函数的需要,将其注册为SimProcedures(感觉代码和描述不相符)

1
2
for obj in self.loader.initial_load_objects:
self._register_object(obj)

详细信息:

  1. _register_object

第七步:运行 OS-specific配置(???)

self.simos.configure_project()



_register_object

在CLE.loader目录下,Loader对象的__init__方法中,出现了:

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

_internal_load方法的描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def _internal_load(self, *args): 
'''
加载任意数量的文件或者lib库。如果不能加载任意一个,就报错。将会返回一个成功加载的对象的list。
'''
object = []
for main_spec in args:
... ...
main_obj = self._load_object_isolated(main_spec) #[^1]
object.append(main_object)
... ...
... ...
for obj in objects:
self._register_object(obj)#[^2]
self._map_object(obj)#[^3]
self._relocate_object(obj)#[^4]
...

详细信息:

  1. _load_object_isolated
  2. [_register_object (loader)](#_register_object – loader)
  3. _map_object
  4. _relocate_object

_load_object_isolated

Loader类的一个方法。描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def _load_object_isolated(self, spec):
'''
Given a partial specification of a dependency, this will return the loaded object as a backend instance.
It will not touch any loader-global data.(没看懂)
'''
#1. 验证文件backend(PE,ELF等等)
full_spec = spec
if 输入的spec是字符串(表示文件名):
#单纯地 获取完整路径,没有别的操作
full_spec = self._search_load_path(spec)

#2. 收集options 信息
options = ... ...
#3. 验证 backend (判断是静态编译还是动态编译???没看懂)
#(选择二进制的文件类型???)
backend_spec = options.pop('backend', None)
backend_cls = self._backend_resolver(backend_spec)#[^1]
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)
#4. 装载
return backend_cls(full_spec, is_main_bin=self.main_object is None, loader=self, **options)#[^2]

#返回值是,一个类的对象(ELF类等)

详细信息:

  1. _backend_resolver
  2. backends_cls

返回:_register_object

_register_object – loader

Loader类的一个方法。描述如下:

1
2
3
4
5
6
7
8
9
10
11
 def _register_object(self, obj):
"""
Insert this object's clerical information into the loader
将对象的clerical information(????)插入到 loader中(???)
"""
self.requested_names.update(obj.deps) #deps是ELF类的一个列表属性,可能表示obj的依赖文件名
for ident in self._possible_idents(obj):#没看懂
self._satisfied_deps[ident] = obj

if obj.provides is not None:
self.shared_objects[obj.provides] = obj

返回:_register_object

_map_object

Loader类的一个方法。描述如下:

1
2
3
4
5
6
def _map_object(self, obj):
"""
This will integrate the object into the global address space, but will not perform relocations.
将对象集合到全局地址空间中,但是并不会处理重定位项(加载文件到内存)
"""
#以ELF文件为例,obj一般是backends.elf文件夹中 ELF类的对象

返回:_register_object

_relocate_object

补充_map_object,处理obj的重定位项,保证依赖关系先被重定位

返回:_register_object

_backend_resolver

在之前的#3中,验证backend时,调用了:

backend_cls = self._backend_resolver(backend_spec)

描述如下:

1
2
3
4
5
6
7
8
9
10
#静态解析:
def _backend_resolver(backend, default=None):
if isinstance(backend, type) and issubclass(backend, Backend):
return backend
elif backend in ALL_BACKENDS:
return ALL_BACKENDS[backend]
elif backend is None:
return default
else:
raise CLEError('Invalid backend: %s' % backend)

ALL_BACKEND是一个字典,在cle.backends.init文件中定义。

ALL_BACKENDS = dict()

保存的是各种文件的backends(目前把backends理解为文件类型,即ELF,PE等)
在cle.elf文件夹下,elf.py文件中,调用了:

register_backend('elf', ELF)

即: ALL_BACKENDS['elf'] = ELF

ELF是一个类,用于静态地加载ELF文件。同理,在cle.pe.pe文件中,也有:

register_backend('pe', PE)

PE同样是一个类,用于加载PE文件。这样就可以解释静态backend_resolver中的

1
2
elif backend in ALL_BACKENDS:
return ALL_BACKENDS[backend]

就是根据文件的类型,返回正确的,用于解析文件的类。backend就是接收返回值的对象。

返回:_load_object_isolated

backends_cls

#4的装载是:backend_cls(full_spec, is_main_bin=self.main_object is None, loader=self, **options)

_backend_resolver中说过,backend_cls根据文件的类型不同,代表ELF类,或者PE类等。

返回:_load_object_isolated