提到逆向工程可能大多数人第一印象就是非道德层面的软件破解,其实不然,逆向工程又称为逆向技术,是一种产品设计技术再现过程,即对一项目产品进行逆向分析及研究,从而演绎并得出该产品的处理流程、组织结构、功能特性及技术规格等设计要素,以制作出功能相近,但又不完全一样的产品。
逆向技术不仅仅是用在商业方面,在军事领域尤为重要,例如越南战争期间我国通过获取4万多块美军战机碎片逆向获得300多项新技术。从这个案例可以看出来,逆向技术不仅仅包含软件逆向,也包含硬件逆向,材料逆向等等。所以下面就软件逆向来介绍其相关知识。
软件逆向技术作用有很多,大多数人应该都使用过逆向出来的盗版软件。实际上这种做法在道德和版权层面来说肯定不被作者所接受,而且会带来较大危害性,会影响到软件开发者的利益,不利于软件市场的健康发展等等。从使用者角度来说其实也有不利影响,例如一些破解软件里可能夹带有病毒或者安全漏洞,被不法人士利用则会造成使用者的数据泄漏或者经济损失等。
对于这种非道德层面的逆向产品我们要知道其危害,但是也要知道逆向技术的正向应用,下面介绍几点:
恶意软件的作者几乎不会提供源代码,除非是基于脚本的病毒。由于缺乏源代码,要准确地了解恶意软件的运行机制,动态分析和静态分析是分析恶意软件的两种主要技术手段。动态分析是指在严格控制的环境(沙盒)中执行恶意软件并使用系统检测实用工具记录其所有行为。相反,静态分则试图通过浏览程序代码来理解程序的行为。此时,要查看的就是对恶意软件进行逆向之后得到的代码清单,通常是汇编代码。在了解到恶意软件的行为之后就可以制定对应有效的防御措施。
安全审核过程划先简单分成3个步骤:发现漏洞、分析漏洞、开发破解程序。无论是否拥有源代码,都可以采用这些步骤来进行安全审核。在没有源代码只有二进制文件情况下,就只能选择逆向方式进行分析。这个过程的第一个步骤,是发现程序中潜在的可供利用的条件。一旦发现漏洞,通常需要对其进行深入分析,以确定该漏洞是否可被利用,如果可利用,可在什么情况下利用。以帮助研究人员了解漏洞的触发条件、影响范围和控制流程等,从而制定更有效的修复措施。
由于编译器(或汇编器)的用途是生成机器语言,不同的编译器对源码翻译出的机器语言可能有差异,这些差异可能不影响功能,但是对性能可能有着巨大差异。所以在这方面可以使用逆向技术来查看编译器翻译出来的机器语言是否符合预期,从而使编译器开发人员能够对编译器进行优化,以达到代码优化验证目的。因此优秀的反汇编工具通常需要验证编译器是否符合设计规范。分析人员还可以从中寻找优化编译器输出的机会,从安全角度来看,还可查知编译器本身是否容易被攻破,以至于可以在生成的代码中插人后门等等。
逆向工程可能会被误认为是对知识产权的严重侵害,但是在实际应用上,反而可能会保护知识产权所有者。例如在软件或者硬件领域,如果怀疑某公司侵犯知识产权,可以用逆向技术逆向对方的产品寻找其侵权证据。
需要注意的是,逆向技术的应用需要遵守相关法律法规和知识产权法律法规的规定,不能用于侵犯他人的合法权益。
任何领域的逆向都不是一项简单的工作,软件逆向技术的应用也需要具备一定的专业知识和技能,需要经过专门的学习和实践才能掌握。这里列举一些对于入门而言所需要的基本知识:
计算机体系结构是指根据属性和功能不同而划分的计算机理论组成部分及计算机基本工作原理、理论的总称。其中计算机理论组成部分并不单与某一个实际硬件相挂钩,如存储部分就包括寄存器、内存、硬盘等。在学习逆向时首选得弄懂什么是CPU,什么是寄存器,什么是内存?(其他如输入设备,输出设备都很重要,这里不做展开介绍)
上述提到CPU解释计算机指令的作用,这里的指令一般是由01二进制数据组成,使用助记符一 一对应就形成了汇编指令。汇编指令在不同体系架构有所不同,所以不同计算机体系关系到软件设计体系,例如X86,ARM, MIPS体系架构的寄存器操作,对执行过程操作可能存在一定的差异。例如x86架构采用复杂指令集(CISC)设计,意味着x86处理器具有更多的内建指令和更复杂的硬件,从而能实现更高的计算性。MIPS采用精简指令集(RISC)设计的架构,设计理念强调高吞吐量和高指令并行度。
如下图指令块分别是上述三种不同架构使用的指令集,如果我们对x86指令集比较熟悉,应该能一眼看出哪段是x86。
下面介绍这三种架构的几个常见的指令区别:
指令类型 / 架构 |
x86 |
ARM |
MIPS |
赋值,寻址 |
mov |
LDR,MOV |
move |
算术运算符 |
add |
ADD / ADC |
add / addi |
跳转 |
jz / jnz |
BRA |
j / jri |
比较 |
cmp |
CMP |
bgtz |
函数调用 |
call |
BL |
jal |
堆栈操作 |
push, pop |
push, pop |
sd,sw |
x86的几个通用寄存器(部分),如下图:
根据上图得到的信息:
通用寄存器的专门用途不是一成不变的,编译器在编译程序的时候会根据很多因素来决定作用,例如编译器、编译条件、操作系统等做出相应的改变。
另外还有部分标志位:
举个例子说明标志位怎么用的。
cmp [esi], 0
jnz 0x152686
上述cmp类似于减法指令,只是不保存结果,判断esi所指内存的值是否等于0, 做差之后标志位ZF就会有结果(等于0就是ZF=1),然后jnz根据ZF表示是否为0决定EIP是否跳转到指定位置。
软件逆向出来的代码清单大多以汇编语言为主要呈现形式,也有逆向工具可生成C/C++等语言伪代码。所以入门至少对汇编语言有一定的了解,对高级语言C/C++/Java语言也需要有一定的了解,至少了解基本的判断循环的操作指令。
如下图的C语言编译为汇编语言后,出现很多指令。在C语言粗略看只有加运算符和函数调用,没有其他过多的操作,而到汇编语言后指令变的繁多且出现多种指令。
汇编语言源代码主要采用两种语法: AT&T 语法和 Intel 语法。这二者的语法在变量、常量、寄存器访问、段和指令大小重写、间接寻址和偏移量等方面都存在巨大的差异。AT&T汇编语法以%作为所有寄存器名称的前缀,以$作为立即操作数的前缀。它这样对操作数排序:源操作数位于左边,目的操作数位于右边。使用AT&T语法,EAX寄存器加4的指令为:add $0x4, %eax。GNU汇编器(Gas)和许多其他GNU工具(如gcc和gdb)都使用AT&T语法。
Intel语法与AT&T语法不同,它不需要前缀,它的操作数排序方式与 AT&T语法操作数恰恰相反:源操作数位于右边,目的操作数位于左边。使用 Intel 语法,上述加法的指令为:add eax, 0x4。后面介绍的内容也都是Intel语法。
结合上图的C语言来简单看看这段汇编的执行流程和语法,这里给汇编指令加上地址,如下图:
假设初始状态rbp为0,栈(rsp寄存器)初始地址为0xd2c8,下面是每行指令执行结果和栈变化情况。
rip值 |
相关寄存器状态 |
栈数据 |
说明 |
400513 |
rbp : 0 rsp : 0xd2c8 |
0xd2c8 : 0x11111111 |
push rbp 将rbp值压入栈,栈指针移动8字节(根据cpu和架构有关) |
400514 |
rbp : 0 rsp : 0xd2c0 |
0xd2c0 : 0 0xd2c8 : 0x11111111 |
mov rbp,rsp 记录栈地址到rbp,一般rbp是栈底地址,rsp是栈顶 |
400517 |
rbp : 0xd2c0 rsp : 0xd2c0 |
0xd2c0 : 0 0xd2c8 : 0x11111111 |
mov edi,0x16 将数值放到第一入参寄存器 |
40051c |
rbp : 0xd2c0 rsp : 0xd2c0 edi : 0x16 |
0xd2c0 : 0 0xd2c8 : 0x11111111 |
call 4004fc 调用函数,控制rip指向指定位置,然后将下一条指定存入到栈中 |
400cfc |
rbp : 0xd2c0 rsp : 0xd2b8 edi : 0x16 |
0xd2b8 : 0x400521 0xd2c0 : 0 0xd2c8 : 0x11111111 |
push rbp 将上个函数的栈底存到栈中,保存上层函数现场 |
400cfd |
rbp : 0xd2c0 rsp : 0xd2b0 edi : 0x16 |
0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 0xd2c0 : 0 |
mov rbp,rsp 获取本函数的栈底地址 |
400500 |
rbp : 0xd2b0 rsp : 0xd2b0 edi : 0x16 |
0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 0xd2c0 : 0 |
sub rsp,0x8 移动栈顶指针,类似于开辟一块空间 |
400504 |
rbp : 0xd2b0 rsp : 0xd2a8 edi : 0x16 |
0xd2a8 : 0x???????? 0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 0xd2c0 : 0 |
mov DWORD PTR [rbp-0x4],edi 栈底往上四个字节存入第一个传参的数值 |
400507 |
rbp : 0xd2b0 rsp : 0xd2a8 edi : 0x16 |
0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 |
mov eax,DWORD PTR [rbp-0x4] 再将数值从栈中取出,赋值给eax, |
40050a/c |
rbp : 0xd2b0 rsp : 0xd2a8 edi : 0x16 eax : 0x16 |
0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 |
mov edi,eax call 4004ed 将eax赋值给edi,edi寄存器默认是函数入参的第一个入参,后面将要函数跳转到4004ed,保存下一条指令到栈 |
40050ed |
rbp : 0xd2b0 rsp : 0xd2a0 edi : 0x16 eax : 0x16 |
0xd2a0 : 0x400511 0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 |
push rbp 保存前一个函数的栈底,栈顶指针向上移动8字节 |
40050ee |
rbp : 0xd2b0 rsp : 0xd298 edi : 0x16 eax : 0x16 |
0xd298 : 0xd2b0 0xd2a0 : 0x400511 0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 |
mov rbp,rsp 设置新函数的栈底 |
4004f1 |
rbp : 0xd298 rsp : 0xd298 edi : 0x16 eax : 0x16 |
0xd298 : 0xd2b0 0xd2a0 : 0x400511 0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 |
mov DWORD PTR [rbp-0x4],edi 将入参存到栈 |
400f4/7 |
rbp : 0xd298 rsp : 0xd298 edi : 0x16 eax : 0x16 |
0xd290 : 0x16???????? 0xd298 : 0xd2b0 0xd2a0 : 0x400511 0xd2a8 : 0x16???????? |
mov eax,DWORD PTR [rbp-0x4] add eax,0xb 将栈值取出存到eax,eax计算后作为返回值返回 |
4004fa |
rbp : 0xd298 rsp : 0xd298 edi : 0x16 eax : 0x21 |
0xd290 : 0x16???????? 0xd298 : 0xd2b0 0xd2a0 : 0x400511 0xd2a8 : 0x16???????? |
pop rbp 即将返回上一层函数,还原上层函数的栈底位置 |
4004fb |
rbp : 0xd2b0 rsp : 0xd2a0 edi : 0x16 eax : 0x21 |
0xd298 : 0xd2b0 0xd2a0 : 0x400511 0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 |
ret 跳转到当前rsp所指的位置,栈顶还原 |
400511 |
rbp : 0xd2b0 rsp : 0xd2a8 edi : 0x16 eax : 0x21 |
0xd2a0 : 0x400511 0xd2a8 : 0x16???????? 0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 |
leave 将函数栈底所指数据存入rsp,再将rsp所指数据弹出到rbp,这是rsp在此基础上会再偏移8字节,等价于: mov rsp, rbp pop rbp |
400512 |
rbp : 0xd2c0 rsp : 0xd2b8 edi : 0x16 eax : 0x21 |
0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 0xd2c0 : 0 |
ret 将栈顶地址弹出到rip,即将跳回到上一层函数 |
400521 |
rbp : 0xd2c0 rsp : 0xd2c0 edi : 0x16 eax : 0x21 |
0xd2b0 : 0xd2c0 0xd2b8 : 0x400521 0xd2c0 : 0 0xd2c8 : 0x11111111 |
pop rbp 即将返回上一层函数,还原上层函数的栈底位置 |
400522 |
rbp : 0 rsp : 0xd2c8 edi : 0x16 eax : 0x21 |
0xd2c0 : 0 0xd2c8 : 0x11111111 |
ret 将栈顶地址弹出到rip,即将跳回到上一层函数 |
经过上面的逐步分析流程,假设有过程(函数)A调用过程B,过程B执行完后返回到A,可以总结出函数调用包含下面一个或多个机制,
函数调用过程时,栈的情况如下:
本地变量 |
正在执行的函数B |
保存的寄存器状态 |
|
返回地址 |
调用函数A的帧 |
入参n |
|
... |
|
入参1 |
|
... |
较早的帧 |
这里详细的过程原理介绍可参考《深入理解计算机系统(第三版)》的第3.7章节,这里不做深入说明。另外这里整明白之后,那么栈溢出攻击原理就会很容易明白。
对于数据结构和算法需要理解,毕竟有“程序 = 数据结构 + 算法”这句古老的公式,这两着是不可分割的关系。想要对逆向出来的程序有一定的理解能力,这方面的知识不可或缺。
在逆向出来的程序中,数据结构呈现不如高级语言那么形象。高级语言结构体里有什么变量成员能够通过变量名一眼看出,而到汇编之后,名称信息全被抛弃,只有寄存器和数值表示结构。看下图实例:
如下结构体和赋值过程是由上图中的汇编反推导出来。可以发现,汇编中表示数据结构是由"基址 + 偏移"的方式表达:
转成伪代码,再做对比确认,发现和反推出来的数据结构仍然有差异,但实际仍然是同一个意思,执行结果相同。
通过上述的案例,知道数据结构在汇编中表达晦涩难懂,没有高级语言的清晰。但是只要知道,像数组,结构体这类连续内存,在汇编中都是以"内存首地址 + 偏移量"存取值,对于基本入门来说则已经不是问题。
在做开发方面的工作其调试(debug)是非常重要的,一般从发现问题到解决问题由下面的几个步骤组成:
上述过程如果应用到逆向工程中,那么“重现故障”就不再是最重要一步,而确定需要逆向的部分模块或功能点(定位和分析)才是最重要的部分。借助调试器分析问题是其手段之一,下面是调试器中一些基本操作介绍:
调试除了上述基本操作外,不同调试器可能还提供更多其他有利于调试的窗口或工具,例如寄存器监控,汇编源码同步,堆栈显示等。
逆向工具种类有很多,有针对安卓的逆向工具,有针对x86的逆向工具,有动态调试分析工具,也有静态分析工具。这里以x86平台的常用调试器为主介绍几款常用工具的优缺点:
名称 |
简介 |
优点 |
缺点 |
下载位置 |
Ollydbg |
windows平台上具有可视化界面的32位汇编分析调试器 |
|
|
http://www.ollydbg.de/viewer.htm |
x64dbg |
是一款开源、免费、功能强大的动态反汇编调试器,能够在Windows平台上进行应用程序的反汇编、调试和分析工作 |
|
|
x64dbg |
GDB |
UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具,也具有逆向功能。 |
|
|
https://www.sourceware.org/gdb/download/ |
Windbg |
微软的调试器集合,可以调试其window内核调试等 |
|
|
Install WinDbg - Windows drivers | Microsoft Learn |
IDA Pro |
是专业的交互式反汇编器,支持多平台运行,支持数十种CPU指令集。 |
|
|
IDA Free |
下面可以给出几个调试器窗口的观感:
GDB是linux的字符形式呈现调试窗口,可以显示源代码窗口,汇编窗口和寄存器窗口,如下:
ollydbg界面比较精致小巧,最关键就是字小信息显示行多,但是界面仍然是上个时代的风格:
IDA Pro界面清爽,审美在线,信息窗口也有非常多种可用,如下图:
有了以上的基础知识,差不多就可以进入逆向的基本学习了,如果需要进阶更深一层,可能还要了解操作系统原理,网络安全技术和软件分析技术等等。
从上章节介绍逆向调试工具有多种,这里以IDA Pro为例介绍其大概使用,进行逆向的入门学习。
交互式反汇编器专业版(Interactive Disassembler Professional)人们常称其为IDA Pro,或简称为IDA。是最强的一个静态反编译软件,是一款交互式的,可编程的,可扩展的,多处理器的,交叉Windows或Linux、MacOS平台主机来分析程序,即跨平台能力。 被公认为最好的花钱可以买到的逆向工程利器。IDA Pro已经成为事实上的分析敌意代码的标准并让其自身迅速成为攻击研究领域的重要工具。它支持数十种CPU指令集,其中包括Intel x86,x64,MIPS,PowerPC, ARM, c8051等等。
打开软件后会出现如下对话框,可以直接打开列表中历史已经加载过的程序,
或者也可以点击New按钮,在菜单栏File > Open打开需要分析的exe可执行文件,打开后会出现如下窗口,IDA pro会自动分析该程序适合的加载器,当然也可以自己选择其他的加载方式,需要拥有推翻IDA决定的能力。
当程序加载完之后,在状态栏的左下角位置会有idle标识,或者输出窗口也有完成标识。然后即可开始自己的分析操作。
这里介绍入门常用的操作方式和窗口的信息展示。加载完之后,界面的初始状态如下图,各序号窗口如下说明:
1 为程序段的分布图
2 为函数窗口
3 为反汇编代码窗口
4 为信息输出窗口
在反汇编代码窗口按空格键,当前函数段会变为图形化展示:
在反汇编代码窗口按下F5,会呈现C的伪代码,如下,伪代码的命名仅供参考标识用时,不一定准确。
在反汇编窗口中,有很多信息是IDA pro协助加上,如下图:
在反汇编窗口呈现的信息都是IDApro给出自动生成的信息和注释,我们也可以对这些信息进行修改和更加。
修改函数名(标号1)和模块名(标号3)将鼠标放到对应的字符上,按N键进行修改。如果想恢复默认名,只需要将原本设置的名字删除即可。
增加修改注释(标号4)是将鼠标移到指定行,然后按分号键进行编辑注释:
修改汇编指令(标号5)稍微的复杂一些,选中待修改的汇编指令行,然后菜单栏点击Edit > Patch program > Assemble出现对话框进行修改:
修改完之后,如果需要使其生效,则菜单栏点击Edit > Patch program > Apply patches to input file,这里最好将原程序备份一份,以防修改出错。
需要注意的是,IDA不提供ctrl+Z撤回操作,如果编辑出错并且无法还原的情况下,只能关闭软件不保存数据库。
其他常用的窗口默认没有被打开的,可自行通过View > Open subviews > Quick view(Ctrl + 1快捷键)打开窗口,选择需要显示的窗口,如下图:
用于查看汇编指令对应的十六进制数据,如下图高亮处指令mov esi, 2Dh对应的十六进制数据就是BE 2D 00 00 00,BE为指令,2D 00 00 00为数据0x0000002D,通过十六进制也可直接修改数据。
通过在对应的十六进制数据上右击鼠标,选择Edit对数据编辑数值。
这里将2D 00 00 00改为2D 00 01 00,可以发现,对应指令传输的数据发生了变化,往寄存器赋值的立即数变为0x1002Dh。
至于上述立即数为什么不是0x2D000100,这里牵扯到大小端问题:
小端模式:数据的高字节,存放在高地址中。计算机读取数据的方向,是从高地址开始读取的;
大端模式:数据的高字节,存放在低地址中。计算机读取数据的方向,是从低地址开始读取的;
字符串窗口显示的是可执行程序中所有可见的字符串,可以通过搜索栏找到自己需要查找的字符串,这个是定位分析点的一个好方式。
IDA不仅是很棒的静态分析工具,也具有很强的动态调试功能,可以调试本地程序也可以调试远程程序。进入到调试模式这里介绍2种方式。
从静态分析界面进入到调试模式,点击工具栏中的绿色三角形按钮,注意后面的调试器选择Local Windows debugger。
进入调试界面后默认布局如下,有很多窗口在静态分析模式已经出现,其中需要关注通用寄存器状态窗口和堆栈窗口。
如下,单步调试时会实时显示寄存器中的数值,点击蓝色回车箭头可以跳转到对应的指示位置。
右击寄存器名称,出现弹窗可以修改寄存器变量,复位,增减值等操作。以及右边的标志位也都可以随时修改。
是直接新建动态调试工程,在菜单栏点击Debugger > Run 后能看到下面选项:
可以远程跨平台调试程序。例如远程调试Linux的程序,设置步骤如下
只需要将上述红字部分填写完毕即可进行远端调试。
使用上述方式进入到调试模式之后,就可以单步调试程序了。单步调试按钮图标如下图前2个,IDA还提供了运行到函数返回位置(第3个按钮),运行到光标所在为止(第4个按钮)。
上述是IDA的使用介绍,如果还想再深入IDA使用可参考《IDA-Pro权威指南(第2版)》。
这里以逆向010Editor作为实例仅供个人学习,勿要将其扩展到商业行为构成侵权。
010Editor是一款十六进制编辑器,有30天的免费试用期的,当超过30天之后,软件就不能再打开,需要注册码才行,如下所示:
随便输入一串字符串然后点击验证证书,发现出现弹窗提示出错。
从上面的出错信息可以找一些特征词,例如“Invalid name or license...”这段字符串就是破解的关键词入口。下面打开IDA Pro开始操刀。
首先打开字符串窗口,View > Open subviews > Strings点击顺序打开窗口,然后在窗口中搜索上述加粗的特征关键词。
点击该字符串,会跳转到对应的反汇编代码上,然后点击交叉引用DATA XREF: sub_5DC320+61F↑,会跳转到该字符串调用的地方,如下图:
然后按F5将该反汇编代码转为C伪代码,可以看出来一个名为v22的变量值不等于147就会报该信息。
继续往上跟踪v22变量,该过程中发现有证书激活成功的字符串,是由v21变量控制,v21等于219就会激活成功,v21变量由v28或者v19赋值,而v19或者v28都是sub_407FFE函数的返回值。
到这里可以停下来思考下,那是不是把v21改为219,把v22改为147就能注册成功呢?可以在第223行对应的反汇编代码处添加断点,进行验证。
添加完断点之后的程序,开始进入调试模式,按照4.1章节的操作顺序操作,发现程序在断点处停止,回到C伪代码模式,可以看到v21变量存在ebx寄存器中,v22变量存在eax寄存器中,可以在通用寄存器窗口对寄存器的值进行修改,也就是对变量的值进行修改。
将eax(v22)改为147(0x93h),ebx(v21)改为219(0xDBh),然后再次运行,发现注册成功,并且进入到软件主界面。
上面已经验证没有问题,紧接着就是对可执行文件的修改,否则下次打开还是需要证书验证。
在上面已经发现了重要函数sub_407FFE的返回值是关键,所以可以在该函数内部,将返回值全部改为所需要的0xDB即可,而v22的值可以暂时不用管,因为通过C伪代码发现,只要验证成功了,就不会走到v22值的判断逻辑。
下面进入到sub_407FFE内部查看如何修改:
sub_407FFE中调用sub_E34090函数,继续跟进。
如果要返回219发现需要sub_B119DD返回45才行。那么如果想直接返回219,可以在上面if(this[11])判断的地方进行修改,使总是能进入到判断,然后返回219即可。
按照上图中方法修改,按tab找到对应的汇编位置,使用Edit > Patch program > Assemble修改代码,修改完之后再使用Edit > Patch program > Apply patches to input file(参考3.2章节修改),保存被修改的可执行文件。下面是修改后的结果:
修改之后再次运行,会发现程序打开之后会直接进入到主界面,不会再弹出注册窗口,并且已经显示注册的用户名信息。
至此已经逆向修改成功,本次是最简单的入门逆向实战,通过修改判断值以达到目的,总结步骤可有下几步:
实际上所有的软件都可以被破解,只是难度不再像上面实战的那么容易,所谓的反逆向技术不是说绝对的无法逆向,而是使用一些技术让逆向变的困难,例如加壳技术就是其中一种。
加壳原理是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,之后再把控制权交还给原始代码,如下图对比加壳和没有加壳对比情况,使用特定的算法和技术将应用程序进行包装和保护,以防止逆向工程和恶意攻击者窃取敏感信息、篡改应用程序等手段。
另外要知道加壳不仅仅是反逆向,实际上也有很多其他的用途,例如降低程序的大小,因为对主程序进行了压缩,所以实际程序会比加壳之前的程序要小。如果到非法攻击者手里,加壳技术可能就是用来隐藏病毒的一种方式,逃避杀毒软件查杀。
几种常见的壳类型:
道高一尺,魔高一丈。逆向分析和反逆向技术都在不断的发展,但终究没有绝对不能逆向的软件,只是逆向难度在不停增加, 只要逆向带来的收益比付出的成本大,那么再难的反逆向也都是浮云。