逆向工程核心原理
《逆向工程核心原理》
¶0x00-调试Helloworld程序
选择Release模式生成的可执行文件能使程序代码更简洁,方便调试-之前我都是debug模式的,难怪有一堆初始化操作
OD指令-Ctrl+F2,重新开始调试
Ctrl+F9,一直在函数代码内部运行,知道遇到retn,跳出函数
:可以给地址添加标签
Ctrl+E,编辑数据
空格,编写汇编代码
¶0x01-小端序标记法
采用小端序时,地址高位存储数据的高位,地址低位存储数据的低位,我们知道一个地址最多存储一个字节的单元,也就是两个十六进制,那么0x12345678在内存存储中时则为78 56 34 12
¶0x02-IA-32寄存器基本讲解
其中FS寄存器比较重要,用于计算SEH、TEB、PEB等地址
¶0x03-栈
栈的特征
函数调用入栈顺序
¶0x04-栈帧
通过EBP-栈帧指针寄存器访问局部变量、参数、函数返回地址
函数开始时要先把已有值保存到栈中
指出相关内存属于哪个段
Test相当于&命令,改变ZF的值
¶0x05-Process Explorer
安装Peocess Explorer、sysinternals
¶0x06-函数调用约定
¶0x07-鸡汤
¶0x08-PE文件格式
相当于复习了
¶为什么要有导入表
1、是因为不同版本的dll函数存放的地址也不同,为了能正常调用函数,编译器准备了存放函数实际地址的位置
2、重定位,多个dll无法同时装载到1000000h
因为存在按序号导入也有按名称导入,所以获取函数起始地址的时候有两种方式
即通过函数名称导出:先去函数名称表依次比较,找到相同时,得到索引index,去导出序号表根据index取出里面的值作为新的索引index_new,再去函数地址表找到函数地址
¶0x09-运行时压缩
¶0x0A-调试UPX压缩的notepad程序
¶0x0B-基址重定位表
¶0x0C-从可执行文件中删除重定位表
¶0x0D-Upack PE文件头详细分析
¶重叠文件头
因为DOS头尾部有一堆垃圾数据,所以将其修改并去除垃圾数据并修改e_lfanew,可以实现文件头重叠
¶IMAGE_FILE_HEADER.SizeofOptionalHeader
通过修改可选PE头的大小,可以向文件头插入解码代码。
¶IMAGE_OPTINAL_HEADER.NumberOfRvaAndSizes
¶IMAGE_SECTION_HEADER
也就是说Upack先将notepad.exe压缩至第二个节区,运行时将第二个节区的代码解压至第一个节区
¶RVA to RAW
¶导入表
¶导入地址表
¶0x0E-Upack调试-查找OEP
¶0x0F-内嵌补丁
¶程序分析
先对附件进行分析
弹出的对话框要求解压其本身
对地址4010F5的第一次异或
再次对4010F5进行异或操作,解密
可以看到这里对地址的值进行校验,每次取出四个字节相加,所以当我们修改了内容时需要对校验部分修改
所以解码完的部分存在我们要找的字符串
对401090地址的值进行异或
¶内嵌补丁练习
因为我们要打补丁的字符串位于B区,而B区进行了双重加密,而且对其内容进行了校验,所以采用内嵌补丁
因为节区要对齐,所以可能存在空白节区,那我们就可以在节区末尾添加我们的洞穴代码
然后让程序先跳转到我们这个地址,再在这段代码最后一句,加上跳回原本程序的代码
但是要注意一点,就是我们插入的是已经解完密的jmp,而实际在文件中,这里的jmp是被加密的,所以要先对我们的修改进行加密,也就是xor 7
¶0x10-Windows消息钩取
windows消息机制
https://blog.csdn.net/alzzw/article/details/108217879
我们设置的钩子能在应用程序之前获取到OS相应信息
SetwindowsHookEx()
HHOOK SetWindowsHookExA(
[in] int idHook,// 要安装的挂钩过程的类型
[in] HOOKPROC lpfn,// 指向挂钩过程的指针
[in] HINSTANCE hmod,// 所指向的挂钩过程的 DLL 的句柄
[in] DWORD dwThreadId
);
main.cpp
1 |
|
KeyHook.dll
1 | //KeyHook.cpp |
调用导出函数HookStart()时,SetWindowsHookEx()函数就会把KeyboardProc()添加到键盘钩链
¶0x11-恶意键盘记录器
https://blog.csdn.net/tiandao2009/article/details/79839182 dllmain函数的不同情形
¶0x12-DLL卸载
¶获取目标进程的句柄
1 | hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID); |
该语句使用进程ID来获取目标进程的进程句柄,下面用获得的进程句柄调用CreateRemoteThread()
¶获取FreeLibrary()地址
1 | hModule=GetModule(L"kernel32.dll); |
若要使目标进程自己调用FreeLibrary(),需要先获得FreeLibrary()的地址。然而上述代码获得的不是加载到目标进程中的FreeLibrary()的地址,而是EjectDll.exe进程中FreeLibrary()的地址,但是FreeLibrary在所有进程中地址是相同的
¶在目标进程中运行线程
1 | hThread=CreateRemoteThread(hProcess,NULL,0,pThreadProc,me.modBaseAddr,0,NULL); |
pThreadProc参数是FreeLibrary()的地址,me.modBaseAddr参数是要卸载的DLL的加载地址。将线程函数指定为FreeLibrary()函数,并且把DLL加载地址传递给线程参数,这样就在目标进程中成功调用了FreeLibrary函数
CreateRemoteThread()原意是在外部线程调用执行线程函数,不过这里的线程函数换成了FreeLibrary()
¶注意事项
¶0x13-通过修改PE加载DLL
也就是修改导入表来使得PE文件运行时直接加载dll
晚点看
¶0x14-代码注入
代码注入是一种向目标进程插入独立运行代码并使之运行的技术,他一般调用CreateRemoteThread()以远程线程形式运行插入的代码,所以也被称为线程注入
也就是说DLL是将整个DLL注入进程中,而代码注入只是注入必要的代码,所以同时也要报要操作的数据进行注入
¶0x15-汇编代码注入
接下来将插入的asm的机器码进行复制,并修改格式
得到
1 | char shellcode[] = { |
¶0x16-API钩取
因为在用户模式下要访问系统资源时,我们没有办法直接访问到,所以为了运行实际的应用程序代码,需要加载许多系统库,也就是DLL
实际就是对API调用时进行钩取,获得控制权