加密与解密
0x1 基础
0x01 大端小端
大端:12345678h存入后还是12345678,高位字节存入低地址,依序
小端:12345678h存入后变为78563412,低位字节存入低地址,反序
0x02 win api函数
应用程序编程接口,相当于是一个合成了各个功能的函数库,提供应用程序运行所需要的窗口管理、内存管理等功能
windows应用程序->API->系统服务->硬件层
常用函数:
hmemcpy{目的数据地址,源数据地址,数据大小}:拷贝数据,当做断点
GetWindowsText{句柄,缓冲区地址,复制的最大字符数}:获得文本控件内容
GetDlgItem{句柄,控件标识}:获取指定对话框的句柄
GetDlgItemText{句柄,控件标识,缓冲区指针,缓冲区长度}:获取对话框文本,成功就返回文本长度
messageBox{父窗口句柄,消息框文本,消息框标题,消息框样式}:创建信息框
0x03 句柄
相当于一个唯一的标识,通常为32位,让windows能正确引用对应的对象。当一个进程被初始化的,系统会分配句柄表,句柄值就是索引
0x04 windows消息机制
消息是给应用程序与应用程序之间、应用程序与windows系统间通信的
应用程序实现功能靠消息触发,靠消息响应和处理来完成
流程:事件发生->windows把输入的消息放入系统消息队列->拷贝到相应的应用程序队列->循环检索发给对应的应用程序窗口
常用函数:
- sendMessage{目的窗口句柄,消息标识,消息的WPARAM,消息的LPARAM}
0x05 保护模式
虚拟内存:简化内存管理,防止多程序间的冲突
应用程序不会直接访问物理内存,应用程序启动时,操作系统会新建一个进程,分配给它2GB的虚拟地址,虚拟内存管理器将应用程序的代码映射到虚拟地址中,并把当前所需要的代码读取到物理地址中。
dll也会映射到虚拟地址,但是都是要和对应的使用的应用程序一起,没有自己私有的虚拟地址。
不同应用程序的虚拟地址是隔离的。
0x2 动态调试
0x01 OD的使用:
L:log
E:executable
M:memory
T:threads
W:windows
Hhandles
C:CPU,默认的主窗口,绝大部分操作,包括了反汇编,信息,数据,寄存器和堆栈
P:patches
K:call stack
B:breakpoints
R:reference
S:source
反汇编窗口:
地址:单击地址可以跳转,再次双击可以返回
机器码:设置或取消无条件断点,F2
汇编代码:可以直接修改
注释:分号(;)可以加注释
信息窗口:
显示寄存器值、API函数、跳转提示等
数据面板:
十六进制显示,ctrl+G可以快速跳转到对应地址
寄存器面板:
单击鼠标右键可以切换显示寄存器的方式
堆栈面板:
显示ESP指向地址的内容,API函数和子程序等
0x3 静态分析
peid用来查壳,ida用来静态分析,做逆向题的时候经常用到
0x01 ida的常用功能
F5 反汇编
0x02 IDC脚本的使用实践
比如用来解密对代码段进行加密的程序
smc(self modifying code)技术,在可执行文件中保存着加密后的数据。只有在程序运行的时候,程序某处的代码就会被调用来解密
书本例子实践:
打开书本里的例子exe
是一个pe32的文件,用ida7.0(32)版本的ida
打开后看到程序入口:
看到了call 401080和401060的操作,点击过去看看
反汇编一下:
看起来像是对401060做了一系列操作
401060的函数如下,无法看出功能
理解401080,是从401060的首位开始,之后的每一位数据和1做异或,运行到401074为止
编写一个idc脚本:
1 | #include <idc.idc> |
运行:
运行结束:401060函数的数据就被解密出来了
还是无法看出来是什么功能
按u,全部转换成数据:
再按c,ida重新分析,即可得到最终代码
附:IDC常用函数:
读取和修改数据的函数:
- long Byte(long addr),从虚拟地址addr处读取一个字节值
- long Word(long addr),从虚拟地址addr处读取一个字(2个字节)值
- long Dword(long addr),从虚拟地址addr处读取一个双字(4个字节)值
- long PatchByte(long addr,long val),设置虚拟地址addr处的一个字节值
- long PatchWord(long addr,long val),设置虚拟地址addr处的一个字值
- long PatchDword(long addr,long val),设置虚拟地址addr处读取一个双字值
- void idLoaded(long addr),如果addr包含有效数据,则返回1,否则返回0
用户交互函数:
- void Message(string fromat,……),在输出窗口打印一条格式化消息,这个函数类似于C语言的printf函数,并接受printf风格的格式化字符串
- void printf(……),在输出窗口中打印每个参数的字符串表示形式
- void Warning(string fromat,……),在对话框中显示一条格式化消息
- string AskStr(string default,string prompt),显示一个输入框,要求用户输入一个字符串值。如果操作成功,则返回用户的字符串;如果对话框被取消,则返回0
- string AskFile(long doSave,string mask,String prompt),显示一个文件选择对话框,以简化选择文件的任务。你可以创建新文件保存数据(doSave=1),或选择现有的文件读取数据(doSave=0)。你可以根据mask(如*.或.idc)过滤显示的文件列表。如果操作成功,则返回选定文件的名称;如果对话框被取消,则返回0
- long AskYN(long deafult,string prompt),用一个答案为“是”或“否”的问题提示用户,如初一个默认的答案(1为是,0为否,-1为取消)。返回值是一个表示选定答案的整数。
- long ScreenEA(),返回当前光标所在位置的虚拟地址
- bool Jump(long addr),跳转到反汇编窗口的指定地址
0x03 hiew使用
F5 输入地址后跳转
F3 修改文件内容,直接输入要修改的数字即可
F9 更新,修改完内容后可以更新一下
F10 更新完后文件内容就被修改了
0x4 逆向分析技术
启动函数:
编译器会生成的一个代码,用来完成初始化进程,然后才会执行winmain函数。各编译器有各自的启动函数。启动函数在分析的时候可以略过
函数:
组成:函数名,入口参数,返回值,函数功能
CALL指令:会保存返回信息(它之后的指令的地址),遇到RET的时候就返回。CALL后跟着的就是被调用的函数的首地址
参数传递:
堆栈方式:先进后出。
流程:1、压入函数结束时应返回的地址和需要的一些参数
2、函数使用EBP指针+偏移量对堆栈中的参数进行寻址,然后取出进行操作
3、函数使用RET返回,CPU把EIP设为之前堆栈里存入的应返回的地址,继续执行
一般用EBP存取堆栈,也会用ESP堆栈指针来寻址,结合栈溢出学习看
寄存器方式:一部分参数使用寄存器,用完的话剩下的用堆栈
通过全局变量进行隐含参数的传递
返回值:
一般放在EAX中返回,如果结果超过了EAX的容量,其高32位就会放到EDX中
数据结构:
局部变量:
在堆栈中进行分配,函数执行完就释放。或者直接存在寄存器中。
全局变量:
作用于整个程序,放在全局变量的内存区,通常位于一个固定的位置
数组:
在内存中按顺序连续存放在一起,一般用基址+偏移量来访问
控制语句:
CMP:
JZ:
指令修改:
替换一个字节:90 nop