0%

杀软的无奈-基础工具篇(一)

前言

杀软的无奈是我准备开始写的一个新的专题文章,主要用来分享恶意代码分析过程用到的一些方法和工具,以及shellcode编写,shellcode分析,metasploit中的shellcode编码器的实现,编码器特征码定位,编码shellcode绕过杀软检测,基于unicorn检测shellcode编码器等相关的知识。文章中讲的案例会主要集中在linux平台中的ELF文件,但是由于个人的精力和知识水平有限,文章更新的频率和质量不太敢保证。如果有地方写的不太对,希望大佬们能够积极斧正,给与一些指导和帮助。

这是这个系列的第一篇文章,俗话说万丈高楼平地起,我们第一篇主要说一下我觉得非常实用的工具,在之后的文章中我的脚本会基于这些工具开发,并不会再介绍这些基础工具的使用。

相关工具的简介

  • Capstone, 全能的反编译框架
  • Keystone, 全能的编译框架
  • IDAPython, 给ida神器再插上翅膀
  • unicorn, 基于qemu的模拟执行框架(unicorn官方版本不支持SMC,我patch了一下相关代码https://github.com/wonderkun/unicorn,建议安装这个版本)
  • flare-emu, 基于unicorn的ida插件,能够快速帮你获取你不想读的代码的执行结果。

全能反汇编引擎 Capstone

Capstone是一个非常优秀的反汇编框架,支持多种CPU架构的,而且提供多种语言的api接口,使用起来非常的简单方便,IDA,Radare2,Qemu等著名项目都使用了Capstone Engine。

源码地址:https://github.com/aquynh/capstone.git,官方文档: http://www.capstone-engine.org/lang_python.html

一个简单的例子如下:

1
2
3
4
5
6
7
from capstone import *

CODE = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"

md = Cs(CS_ARCH_X86, CS_MODE_64)
for i in md.disasm(CODE, 0x1000):
print("%d\t0x%x:\t%s\t%s\t%s" %(i.id,i.address, i.mnemonic, i.op_str,i.bytes.hex()))

初始化一个Cs类,需要有两个参数,分别是平台和架构模式

md.disasm 函数需要提供两个参数,第一个参数是需要分析的bytes,第二个参数是基地址。返回一个生成器,遍历就可以得到每条指令的对象 CsInsn,它导出了与此条指令相关的很多属性,详细的解释如下:

全能的编译引擎 Keystone

与Capstone相对应的,那必然是keystone了,keystone与capstone功能恰好恰好相反,是一个全能的支持多种架构的编译框架。源代码地址https://github.com/keystone-engine/keystone,官方文档地址https://www.keystone-engine.org/docs/tutorial.html

1
2
3
4
5
6
7
8
9
CODE = b"INC ecx; DEC edx" # separate assembly instructions by ; or \n

try:
# Initialize engine in X86-32bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
print("%s = %s (number of statements: %u)" %(CODE, encoding, count))
except KsError as e:
print("ERROR: %s" %e)

可以看到,跟Capstone的使用方法非常类似。

IDAPython

ida是逆向分析的神器,但是再加上idapython那就是给神器安装上翅膀,非常好用,关于idapython的api使用说明,可以读一下我的学习记录,里面有比较好的学习资料推荐。

flare-emu

是fireEye开源的一款基于unicorn,并且直接可以再ida导入使用的代码模拟执行工具,这个工具对于我们利用ida分析恶意代码或者shellcode都非常的有用,特别是复杂的加密算法,或者是恶心的自解密代码。
关于这款工具的使用说明可以参考这篇翻译文章https://wonderkun.cc/2020/03/02/%E7%94%A8%E6%A8%A1%E6%8B%9F%E6%89%A7%E8%A1%8C%E5%AE%9E%E7%8E%B0Objective-C%E4%BB%A3%E7%A0%81%E8%87%AA%E5%8A%A8%E5%8C%96%E5%88%86%E6%9E%90/
,或者直接看源代码 https://github.com/fireeye/flare-emu,我当时修改了一个python3的版本用于支持ida7.4,
详情见我的githubhttps://github.com/wonderkun/flare-emu

注意: 在mac平台上,ida默认使用的python并不是是用brew安装的python3,需要手工切换一下,切换方法可以参考https://github.com/wonderkun/flare-emu#intall-on-mac
pip安装的unicorn可能不支持python3,需要自己编译安装一下unicorn。

unicorn

Unicorn 是一款基于qemu模拟器的模拟执行框架,支持Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (include X86_64)等指令集,为多种语言提供编程接口比如C/C++、Python、Java 等语言。Unicorn的DLL 可以被更多的语言调用,比如易语言、Delphi,前途无量。它的设计之初就考虑到线程安全问题,能够同时并发模拟执行代码,极大的提高了实用性。

在后续分析shellcode的过程中,会遇到大量的 self-modify-code,unicorn官方提供的版本是不支持SMC代码的,https://github.com/unicorn-engine/unicorn/issues/820,所以我参照网上的方法patch了一个版本https://github.com/wonderkun/unicorn,建议安装这个版本。就目前来看是够用的,但是官方还没有接受我的pr,具体原因未知。

虚拟内存

Unicorn 采用虚拟内存机制,使得虚拟CPU的内存与真实CPU的内存隔离。Unicorn 使用如下API来操作内存:

  • mem_map
  • mem_read
  • mem_write
    使用uc_mem_map映射内存的时候,address 与 size 都需要与0x1000对齐,也就是0x1000的整数倍,否则会报UC_ERR_ARG 异常。如何动态分配管理内存并实现libc中的malloc功能将在后面的课程中讲解。

    Hook机制

    Unicorn的Hook机制为编程控制虚拟CPU提供了便利。
    Unicorn 支持多种不同类型的Hook。
    大致可以分为(hook_add第一参数,Unicorn常量):
  • 指令执行类
    • UC_HOOK_INTR
    • UC_HOOK_INSN
    • UC_HOOK_CODE
    • UC_HOOK_BLOCK
  • 内存访问类
    • UC_HOOK_MEM_READ
    • UC_HOOK_MEM_WRITE
    • UC_HOOK_MEM_FETCH
    • UC_HOOK_MEM_READ_AFTER
    • UC_HOOK_MEM_PROT
    • UC_HOOK_MEM_FETCH_INVALID
    • UC_HOOK_MEM_INVALID
    • UC_HOOK_MEM_VALID
  • 异常处理类
    • UC_HOOK_MEM_READ_UNMAPPED
    • UC_HOOK_MEM_WRITE_UNMAPPED
    • UC_HOOK_MEM_FETCH_UNMAPPED
      调用hook_add函数可添加一个Hook。Unicorn的Hook是链式的,而不是传统Hook的覆盖式,也就是说,可以同时添加多个同类型的Hook,Unicorn会依次调用每一个handler。hook callback 是有作用范围的(见hook_add begin参数)。
      python包中的hook_add函数原型如下
      1
      2
      def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0):
      pass
  • htype 就是Hook的类型,callback是hook回调用;
  • callback 是Hook的处理handler指针。请注意!不同类型的hook,handler的参数定义也是不同的。
  • user_data 附加参数,所有的handler都有一个user_data参数,由这里传值。
  • begin hook 作用范围起始地址
  • end hook 作用范围结束地址,默认则作用于所有代码。

hookcall

不同类型的hook,对应的callback的参数也是不相同的,这里只给出C语言定义。
Python 编写callback的时候参考C语言即可(看参数)。

UC_HOOK_CODE & UC_HOOK_BLOCK 的callback定义

1
typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, void *user_data);
  • address: 当前执行的指令地址
  • size: 当前指令的长度,如果长度未知,则为0
  • user_data: hook_add 设置的user_data参数

READ, WRITE & FETCH 的 callback 定义

1
2
typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,
uint64_t address, int size, int64_t value, void *user_data);
  • type: 内存操作类型 READ, or WRITE
  • address: 当前指令地址
  • size: 读或写的长度
  • value: 写入的值(type = read时无视)
  • user_data: hook_add 设置的user_data参数

invalid memory access events (UNMAPPED and PROT events) 的 callback 定义

1
2
typedef bool (*uc_cb_eventmem_t)(uc_engine *uc, uc_mem_type type,
uint64_t address, int size, int64_t value, void *user_data);
  • type: 内存操作类型 READ, or WRITE
  • address: 当前指令地址
  • size: 读或写的长度
  • value: 写入的值(type = read时无视)
  • user_data: hook_add 设置的user_data参数
    返回值
    返回真,继续模拟执行
    返回假,停止模拟执行