本书基于Windows操作系统
软件的加密和解密
随着技术的发展,软件加密和解密的研究很有必要。
软件加密对于开发人员来说,可以保护自己的成果,不轻易被人“借鉴”,但随着技术的不断发展,加密技术也要不断地改进;同时,解密手段也在不断升级。所以,加密和解密是相互促进又相互斗争的关系
理论上,没有不能破解的加密技术
所以,作为程序员,研究解密技术可以有助于自己的提升
逆向分析
逆向工程(Reverse Engineering)是指根据已有的产物和结果,通过分析来推导出具体的实现方法
逆向工程:可执行程序–>反编译–>源代码
逆向工程的内容分类:
- 软件使用的限制的去除或软件功能的天际
- 软件源代码的再获得
- 硬件的复现和模拟
逆向分析技术
软件汉化和解密首先要对软件进行分析,对他们的分析可以遵循下面几种方法:
1、通过软件格式使用说明和操作格式分析软件
分析一个软件,需要先学会这个软件。通过阅读软件的使用手册,学习软件操作并了解软件的功能。经验丰富的程序分析师,能够通过软件的使用说明和操作推测出软件的设计思想和编程思路
2、静态分析技术
静态分析指根据反汇编得到的程序清单进行分析,常用方法从提示信息入手进行分析。
原因:现在大多数软件采用人机对话方式设计
人机对话:软件运行时出现需要用户操作的提示信息,并且在执行某段程序之后显示提示信息来反映该段程序的运行状态或者是提示下一步操作
因此,阅读通过静态反汇编得到的程序清单,根据包含提示信息的程序片段,可以知道提示信息前后的程序片段所完成的功能,从而宏观了解软件的编程思维
常用的静态分析工具 IDA
3、动态分析技术
静态分析可以了解各个模块的功能,以及整个软件的编程思路,但是不能了解软件中各个模块的技术细节
对于软件分析来说,静态分析只是第一步,动态跟踪才是分析软件的关键。
动态跟踪主要是指利用OllyDbg或WinDbg等调试工具,一步步跟踪分析
为什么要对软件进行动态分析
- 一般软件要分解成若干个模块来实现,后一个模块的执行依赖于前一个模块处理的结果,这个结果也称为中间结果。如果只进行静态分析,很难获得这个中间结果,只有跟踪前一个模块,才能看到这些结果。另外,触发程序执行的条件有多个分支,如果不动态跟踪,很难的得知程序的执行路径
- 许多软件运行时,最初执行的一段程序需要对后面的各个模块进行一些初始化工作,并不依赖系统的重定位
??这个地方有点不懂
- 加密程序在运行时会采用逐块解密、逐块执行的方法。仅对软件的密码部分进行返回百年,而不对该软件进行动态跟踪分析,是根本不可能进行解密的
如何有效进行动态分析
动态分析主要包括两点:
-
对软件进行粗跟踪 粗跟踪是指在跟踪时要大块大块地跟踪。
在遇到调用指令(CALL)、重复操作指令(REP)、循环操作指令(LOOP)等时一般不要跟踪,而要根据执行结果分析该段程序的功能
粗跟踪的原因:
- 一般软件划分为若干模块,在分析一个软件时,主要分析的是软件中所关心的部分,然而软件最初执行的模块通常不是我们所关心的
这里涉及合理设置断点,需要了解Win32 API函数,根据当时的情况选择合适的断点
- 一般软件划分为若干模块,在分析一个软件时,主要分析的是软件中所关心的部分,然而软件最初执行的模块通常不是我们所关心的
-
对关键部分进行细跟踪 对软件进行一定程度的粗跟踪之后,就能获取软件中我们所关心的模块或程序段了,这样就可以有针对性地对该模块进行具体而详细的跟踪结果。一般,可以要进行对此关键代码分析,并将每次关键的中间结果或指令地址记录下来(养成良好的操作习惯)
Windows操作系统
Windows x86系列CPU采用的是小端序字节存储位置,即低位字节存放在低位地址,高位字节存放在高位地址
Win32 API函数
API(Application Programming Interface,应用程序编程接口)
早期,Windows程序设计只能使用API函数进行编程,这些函数提供应用程序运行所需要的窗口管理、图形设备接口、内存管理等服务功能,这些服务以函数库库的形式组织在一起,形成了Win API
也可以认为API函数是整个Windows框架的基石,下面是Windows操作系统核心,上面是Windows应用程序:

Win16: 16位Windows的API
Win32: 32位Windows的API, 64位Windows API的名称和功能基本没有变化,还是使用Win32的函数名,只是用64位代码实现的
Windows运转的核心是动态链接库(DLL)
早期,Windows的主要部分只需要在3个动态链接库中实现:
- Kernel(KERNEL32.DLL实现):操作系统核心功能服务,包括进程和线程控制、内存管理、文件访问等
- User(USER32.DLL实现):负责处理用户接口,包括键盘和鼠标输入、窗口和菜单管理等
- GDI(GDI32.DLL实现):图形设备接口,允许程序在屏幕和打印机上显示文本和图形
除了上述模块,Windows提供了其他DLL以支持更多的功能:
- 对象安全性
- 注册表操作(ADVAPI32.DLL)
- 通用控件(COMCTL32.DLL)
- 公共对话框(COMDLG32.DLL)
- 用户界面外壳(SHELL32.DLL)
- 网络(NETAPI32.DLL)
Win API是基于C语言的接口,但可以由不同语言编写的程序调用,只要遵循调用规范即可
在NT结构下,Win32 API 接受Unicode和ASCII两种字符集,而其内核只能使用Unicode字符集
ASCII字符集
Unicode字符集
在Win32 API函数字符集中,A表示ANSI(即ASCII),W表示Widechars(即nicode)
ANSI 使用单字节方式
Unicode是宽字节方式,以便处理双字节字符
每个以字节为参数的Win32 函数在操作系统中都有着两种方式的版本
例如:MessageBox函数(此函数用于在USER32.DLL用户模块中创建和显示信息框)
函数原型:
1int MessageBox( 2 HWND hWnd, //父窗口句柄 3 LPCTSTR lpText, //消息框文本地址 4 LPCTSTR lpCaption, //消息框标题地址 5 UINT uType //消息框样式 6);Windows 2000中的MessageBoxA函数的内部结构:
1int MessageBoxA( 2 MessageBoxExA{ //调用MessageBoxExA函数 3 MBToWCSEx() //将MessageBoxA消息框的主体文字转换成Unicode字符串 4 MBT0WCSEx() //将将MessageBoxA消息框标题栏上的文字转换成Unicode字符串 5 MessageBoxExW() //调用MessageBoxExW函数 6 HeapFree() //释放内存 7 8 } 9);上面的结果表明,MessageBoxExA函数其实是一个替换翻译层,用于分配内存,并将ANSI字符串转换成Unicode字符串,系统最终调用Unicode版的MessageBoxExW函数执行。当MessageBoxExW函数返回时,它便释放内存缓存
win32 API程序大量调用系统提供的API函数,而Win32平台上的调试器(OllyDbg等)恰好有针对API函数设置断点的强大功能
WOW64
WOW64(Windows-on-Windows 64-bit)是64位Windows操作系统的子系统,可以使大多数32位应用程序在不进行修改的情况下运行在64位操作系统上
64位Windows(包含64位操作系统应有的系统文件和32位操作系统应用的系统文件):
- System32文件夹–包含原生的64位映像文件
- SysWOW64文件夹–存储32位的系统文件

64位应用程序会加载System32目录下64位的kernel32.dll、user32.dll、ntdll.dll
32位应用程序加载时,WOW64建立32位ntdll.dll所要求的启动环境,将CPU模式切换为32位,并开始执行32位加载器,像运行在32位机器上一样
WOW64会对32位ntdll.dll的调用重定向ntdll.dll(64位),而不是发出原生的32位系统调用指令
WOW64转换到原生的64位模式,捕获与系统调用有关的参数,发出对应的原生64位系统调用。当原生的系统调用返回时,WOW64在返回32位模式之前将所有输出参数从64位转换成32位
WOW64进程只能加载32为的DLL,不能加载原生的64位DLL
类似,原生的64位进程不能加载32为的DLL
Windows 消息机制
Windows是一个消息(Message)驱动式系统,Windows消息提供在应用程序与应用程序之间、应用程序与Windows系统之间进行通信的手段
应用程序想要实现的功能需要由消息触发,通过对消息的响应和处理完成
Windows系统的两种消息队列:
- 系统消息队列
- 应用程序消息队列
计算机的所有输入设备由Windows监控
一个事件从发生到到达处理它的窗口函数的过程:
当一个事件发生时,Windows先将输入的消息放入系统消息队列,再将输入的消息复制到相应的应用程序队列中,应用程序中的消息循环在它的消息队列中检索每个消息并发给相应的窗口函数
注:消息的非抢先性,即事件不论轻重缓急,总是按达到的先后排队,而这会导致一些外部实时事件得不到及时的处理
Windows常用的消息函数:
- SendMessage 函数
调用一个窗口的窗口函数,将一天消息发给那个窗口。除非消息处理完毕,否则该函数不会返回。
函数示例:
返回值由具体的消息决定。如果消息传递成功,则返回True
1LRESULT SendMessage( 2 HWND hWnd, 3 UNIT Msg, 4 WPARAM wParam, 5 LAPRAM lParam 6); - WM_COMMAND消息
- WM_DESTROY消息
- WM_GETTEXT消息
- WM_QUIT消息
- WM_LBUTTONDOWN消息
虚拟内存
虚拟内存(Virtual Memory)不是真正的内存,它通过映射(Map)的方法使用可用虚拟地址(Virtual Address)达到4GB(默认情况下,32位Windows操作系统的地址空间在4GB以内),每个应用程序可以获得2GB的虚拟地址,剩下的2GB留给操作系统自用
WIndows操作系统是一个分时的多任务操作系统,CPU时间在被分成一个个时间片后分配给不同的程序。在一个时间片里,与这个程序的执行无关的内容不会映射到线性地址中。因此,每个程序都有自己的4GB寻址空间,互不干扰。
虚拟内存的实现方法:
- 一个应用程序启动时,操作系统就创建一个进程,并给该进程分配2GB的虚拟地址(不是内存,只是地址)
- 虚拟内存管理器将应用程序的代码映射到那个应用程序的虚拟地址中的某个位置,并把当前需要的代码读入物理地址
虚拟地址与应用程序代码在屋里内存中的位置是没有关系的
- 如果使用DLL,DLL也会被映射到进程的虚拟地址空间中,在需要的时候才会被读入物理内存
- 其他项目(数据、堆栈等)的空间也是从物理内存中分配的,并被映射到虚拟地址空间中
- 应用程序通过使用其他虚拟地址空间中的地址开始执行。然后,虚拟内存内管理器把每次内存访问映射到物理位置
注:
- 应用程序不会直接访问物理地址
- 虚拟内存管理器通过虚拟地址的访问请求来控制所有的物理地址访问
- 每个应用程序都有独立的4GB寻址空间,不同的应用程序的地址空间相互不影响
- DLL总是被映射到其他应用程序的地址空间中,作为其他应用程序的一部分(原因:如果DLL不与其他程序处于同一地址空间,应用程序就无法调用)
使用虚拟内存可以简化内存的管理,弥补物理内存的不足,可以防止多任务环境相爱应用程序之间的冲突】
64位 CPU的最大寻址空间为 2^64 byte,即16TB
实际应用中,64位版本的Win7支持8Gb-192GB内存;64位版本的Win10支持128GB~2IB内存