软件逆向工程初识

网安组新人任务Week0笔记

软件逆向工程简介

  • 软件逆向工程(Software Reverse Engineering)又称软件反向工程,是指从可运行的程序系统出发,运用解密、反汇编、系统分析、程序理解等多种计算机技术,对软件的结构、流程、算法、代码等进行逆向拆解和分析,推导出软件产品的源代码、设计原理、结构、算法、处理过程、运行方法及相关文档等。通常,人们把对软件进行反向分析的整个过程统称为软件逆向工程,把在这个过程中所采用的技术都统称为软件逆向工程技术

软件逆向工程的基本流程

  • 研究保护方法、去除保护功能
  • 反汇编目标软件,跟踪、分析代码功能
  • 生成目标软件的设计思想架构算法等文档
  • 向目标软件的可执行程序注入代码,开发出更完善的应用软件

逆向工程的分类

按工程大小分类

代码级

  • 代码级逆向工程需要尝试从机器码中提取软件的代码概念和算法
  • 需要对CPU如何工作,操作系统如何工作以及软件开发过程等方面进行了解
  • 将会使用到的工具:
    • IDA Pro —— 静态反编译软件
    • SoftIce —— 系统调试工具
    • Ollydbg —— 动态追踪工具(将IDA与SoftICE结合起来的思想)

系统级

  • 系统级逆向过程涉及使用运行工具来获取有关软件的信息,检查程序,检查可执行文件以及跟踪程序的输入和输出
  • 需要对操作系统进行了解
  • 将会使用到的工具:
    • SysInternals Suite —— 微软系统工具套装
    • Tripwire —— unix下文件系统完整性检查工具
    • Wireshark —— 网络封包分析软件

按分析方法分类

静态分析

  • 在不执行计算机程序的条件下,对源代码进行分析,找出代码缺陷

动态分析

  • 计算机必须真正运行被测试的程序,通过输入测试用例,对其运行情况即输入输出的对应关系进行分析,以达到检测的目的。

按平台分类

  • Android逆向
  • IOS逆向
  • Windows逆向
  • Unix逆向
  • ……

逆向工程知识点

程序的编译、链接、装载及运行过程

编译型语言与解释型语言

  • 编译型语言:程序在执行前编译成机器语言文件,运行时不需要重新翻译,直接供机器运行
  • 解释型语言:程序在用编程语言编写后,不需要编译,以文本方式存储原始代码,在运行时,通过对应的解释器解释成机器码后再运行
  • Java首先将源代码编译成.class类型文件,程序运行时JVM从class文件中读一行解释执行一行,使用Java是混合型语言

编译:将高级语言指令转换为汇编代码

  • 预处理:正式编译前,根据已放置在文件中的预处理指令来修改源文件的内容,包含宏定义指令,条件编译指令,头文件包含指令,特殊符号替换等
  • 编译、优化:编译程序通过词法分析和语法分析,将其翻译成等价的中间代码表示或汇编代码
  • 目标代码生成:将上面生成的汇编代码译成目标机器指令的过程,目标文件中所存放着与源程序等效的目标的机器语言代码

链接:将有关的目标文件彼此相连接生成可加载、可执行的目标文件

  • 静态链接:链接器将函数的代码从其所在地(目标文件或静态链接库中)拷贝到最终的可执行程序中,整个过程在程序生成时完成
  • 动态链接:在编译链接时只提供符号表和其他少量信息用于保证所有符号引用都有定义,保证编译顺利通过,程序执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间,根据可执行程序中记录的信息找到相应的函数地址并调用执行
  • 经过编译链接后,程序生成,windows程序则都已PE文件形式存储

PE文件(Portable Executable,可移植可执行文件)

  • 常见的EXE、DLL、OCX、SYS、COM都是PE文件
  • PE文件以段的形式存储代码和相关资源数据,其中数据段和代码段是必不可少的两个段
  • 在应用程序中最常出现的段有以下6种:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1、执行代码段,.text命名;

    2、数据段,.data、.rdata 命名;

    3、资源段,.rsrc命名;

    4、导出表,.edata命名;

    5、导入表,.idata命名;

    6、调试信息段,.debug命名。

程序的运行

  • 系统将硬盘上的程序装载到内存里然后在内存里运行程序,因为内存速度快
  • 当程序需要的空间大于内存容量时,硬盘中的部分空间(虚拟内存)将会被用来存储内存中暂时不用的数据

逆向脱壳

加壳

  • 加壳是利用特殊的算法,对EXE、DLL文件里的资源进行压缩、加密,这个压缩之后的文件,可以独立运行,解压过程完全隐蔽,都在内存中完成。
  • 壳的类型通常分为压缩壳和加密壳两类
    • 压缩壳的特点是减小软件体积大小,加密保护不是重点
    • 加密壳种类比较多,不同的壳侧重点不同,一些壳单纯保护程序,另一些壳提供额外的功能
  • Android APK加固方法
    • 对源APK整体做一个加固,放到指定位置,运行的时候再解密动态加载
    • 对so进行加固,在so加载内存的时候进行解密释放

OER(Original Entry Point 程序入口点)

  • 软件加壳一般隐藏了程序的OER或者使用了假的OER
  • 在运行完程序自脱壳模块后,程序会停留在程序加壳之前的OEP位置,此时即可找到真正的OEP位置
  • 寻找加壳程序的正确OEP,是手动脱壳时的第一要务

IAT(Import Address Table 导入地址表)

  • 导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL中。当PE文件被装入内存的时候,Windows装载器才将DLL装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态链接),这操作就需要导入表完成
  • 导入地址就指示函数实际地址。 多数加壳软件在运行时会重建导入地址表
  • 获取加壳程序正确的导入地址表也是手动脱壳操作中的一个关键问题

一些脱壳方法(过于偏向工程)

  • 单步跟踪法:通过Ollydbg的单步调试功能完整走过自脱壳过程定位到OEP
  • ESP定律法:合理利用程序中堆栈平衡 —— 在程序自解密或者自解压过程中,不少壳会先将当前寄存器内容压栈,如使用pushad,在解压结束后,会将之前的寄存器值出栈,如使用popad。因此在寄存器出栈时,往往程序代码被自动恢复,此时硬件断点触发。然后在程序当前位置,只需要少许单步跟踪,就很容易到达正确的OEP位置
  • 内存镜像法(二次断点法):在加壳程序被加载时,通过Ollydbg的ALT+M快捷键,进入到程序虚拟内存区段。然后通过加两次内存一次性断点,到达程序正确OEP的位置 —— 对于程序资源段和代码段下断点,一般程序自解压或者自解密时,会首先访问资源段获取所需资源,然后在自动脱壳完成后,转回程序代码段。这时候下内存一次性断点,程序就会停在OEP处。
  • 一步到达OEP:根据所脱壳的特征,寻找其距离OEP最近的一处汇编指令,然后下int3断点,在程序走到OEP的时候dump程序
  • 最后一次异常法:程序在自解压或自解密过程中,可能会触发无数次的异常。如果能定位到最后一次程序异常的位置,可能就会很接近自动脱壳完成位置
  • 模拟跟踪法:使用Ollydbg下条件断点,SFX相当于是一个自解压段,在自解压段结束时(eip的值转到代码段时),已经距离OEP很近,但是这种跟踪方法会比较耗时。
  • “SFX”法:利用Ollydbg自带的OEP寻找功能,可以选择直接让程序停在OD找到的OEP处,此时自解压已经完成,可以直接dump程序。

代码注入

代码注入是一种用于处理非法数据的计算机臭虫应用,代码注入可被攻击者用来导入代码到某特定的计算机程序,以改变程序的运行进程或目的

  • SQL注入:乘SQL语法之利,注入可读取或者修改数据库、或者扭曲原始查询意义的命令
  • “PHP注入”、”ASP注入”、以及其他类似技术术语是创造来泛指其他种种允许攻击者直接对服务器脚本引擎提供代码的代码注入攻击,比如在”PHP注入”实例里,服务端脚本引擎是PHP

软件逆向