一、 虚实地址转换(32位)
1. 原理
X86系统上的Windows使用二级页表来把虚拟地址转译为物理地址。默认页面大小为4KB,占12位。
2. 准备工作
首先做一下准备工作,参考WinDbg 双机调试(调试机为Windows10系统,被调试机为Windows7系统),此外,还需将PAE关闭:
查看是否关闭成功:
3. 实例
(1)在notepad中写入“abtgu’s blog”,在windbg中查找该字符串。
(2)虚拟地址为:0x0021de30,将其转换成2进制形式。
(3)按照图2-8划分虚拟地址。
(4)cr3中的页目录基地址+页目录索引*4=页表的基地址。
(5)页表的基地址为0x3aa90000,页表基地址+页表索引*4=字符串所在物理地址基地址。
(6)字符串物理地址=基地址+字节索引
4. 总结
给出虚拟地址为0x0021de30,cr3中的值为0x3a802000,求其对应的物理地址。
(1)将0x0021de30转换成2进制形式:0000 0000 0010 0001 1101 1110 0011 0000。
(2)按照图2-8划分虚拟地址:0000000000 1000011101 111000110000
页目录索引 页表索引 字节索引 0 0x21d 0xe30 (3)计算页表基地址=[(cr3+页目录索引*4)]&0xfffff000。
页表基地址=[0x3a802000+0*4]&0xfffff000=0x3aa90000
(4)计算物理基地址=[页表基地址+页表索引*4]&0xfffff000。
物理基地址=[0x3aa90000+0x21d*4]&0xfffff000=0x00ca3000;
(5)计算物理地址=物理基地址+字节索引=0x00ca3000+0xe30=0x00ca3e30。
二、PE结构
结合逆向分析实战那本书P67-P84。
1. DOS头(IMAGE_DOS_HEADER)
这一部分只需知道前2个字节是“MZ=4d 5a”,偏移3CH处开始的4个字节是PE头的偏移位置。
2. PE头(IMAGE_NT_HEADERS)
2.1 PE标识(PE..=50 45 00 00)
PE头的前4个字节是“PE..=50 45 00 00”。
2.2 文件头(IMAGE_FILE_HEADER)
文件头大小是14H=20个字节;
偏移2H开始的2个字节是文件中节的个数;
偏移10H开始的2个字节是可选头的大小
2.3 可选头(IMAGE_OPTIONAL_HEADER)
偏移10H处的4个字节是入口地址(AddressOfEntryPoint);
在32位系统中,偏移1CH处的4个字节是可执行文件默认装入的内存地址(ImageBase);
在32位系统中,偏移60H处的128个字节是数据目录数组(DataDirectory,一个数据目录项8个字节,共16个,前4个字节代表该节的虚拟地址,后4个字节是该节的大小)。
数据目录中的第一项是导出表,第二项是导入表。
导入表的位置=可执行文件的起始位置+VirtualAddress。偏移0CH位置是DLL名字的指针。
三、调用栈
ESP:栈指针寄存器,存放的是当前栈的栈顶指针;
EBP:基址指针寄存器,存放的是当前栈的栈底指针;
EIP:指令指针寄存器,存放的是下一条等待执行的指令地址;
cr3:页目录基址寄存器,保存页目录表的物理地址。
1. 函数调用过程
(1)参数入栈:由右向左入栈;
(2)返回地址入栈;
(3)跳转到调用函数入口处;
(4)栈帧调整:
push ebp
mov ebp,esp
sub esp,xxx
(5)执行函数内部操作;
(6)将函数返回值保存到eax中;
(7)调整栈帧:
add esp,xxx
pop ebp
(8)返回父函数:retn
2. 实例
(1)编写程序,配置release环境。
(2)WinDbg打开release下生成的exe文件,添加符号路径。
(3)设置断点,并运行。
(4)修改helloWorld函数的返回地址。
(5)修改helloHook函数的返回地址。
(6)继续运行函数,得到结果。
四、DLL建立与DLL插入
1. DLL建立
(1)编写dll程序testDll,因为需要导出dll的函数供其他程序调用,所以需要使用关键字__declspec(dllexport)
(2)编写引用dll的程序useDll,因为再该程序中需要导入dll中的函数,所以需要使用关键字__declspec(dllimport)。
(3)将第一步生成的dll文件放到useDll的Debug目录下,运行程序。
2. 远程线程插入DLL
实验环境:32位Windows7操作系统
(1)dll代码
#include <windows.h>
#include <tchar.h>
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
if (fdwReason == DLL_PROCESS_ATTACH) {
MessageBox(NULL,"Injection",TEXT("Inject"),MB_OK);
}
return(TRUE);
}
(2)编写插入dll程序代码
基本步骤:
(1)使用VirtualAllocEx函数,分配远程进程的地址空间中的内存。
(2)使用WriteProcessMemory函数,将DLL的路径名拷贝到第一个步骤中已经分配的内存中。
(3)使用GetProcAddress函数,获取LoadLibraryA或LoadLibraryW函数的实地址(在Kernel32.dll中)。
(4)使用CreateRemoteThread函数,在远程进程中创建一个线程,它调用正确的LoadLibrary函数,为它传递第一个步骤中分配的内存的地址。
这时, DLL已经被插入远程进程的地址空间中,同时 DLL的DllMain函数接收到一个
DLL_PROCESS_ATTACH通知,并且能够执行需要的代码。当 DllMain函数返回时,远程线程从它对LoadLibrary的调用返回到BaseThreadStart函数。然后BaseThreadStart调用ExitThread,使远程线程终止运行。现在远程进程拥有第一个步骤中分配的内存块,而DLL则仍然保留在它的地址空间中。若要将它删除,需要在远程线程退出后执行下面的步骤:
(5)使用VirtualFreeEx函数,释放第一个步骤中分配的内存。
(6)使用GetProcAddress函数,获得FreeLibrary函数的实地址(在Kernel32.dll中)。
(7)使用CreateRemoteThread函数,在远程进程中创建一个线程,它调用FreeLibrary函数,传递远程DLL的HINSTANCE。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <malloc.h>
int main()
{
HANDLE hProcess = NULL, hThread = NULL;
//所要插入dll的程序的进程id
DWORD dwProcessId = 2444;
PSTR pszLibFileRemote = NULL;
//dll所处绝对地址
char szLibFile[]="C:\\Injlib\\Debug\\TipInject.dll";
// Get a handle for the target process.
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | // Required by Alpha
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx
PROCESS_VM_WRITE, // For WriteProcessMemory
FALSE, dwProcessId);
int cch = 1 + strlen(szLibFile);
int cb = cch * sizeof(char);
// Allocate space in the remote process for the pathname
pszLibFileRemote = (PSTR)
VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
// Copy the DLL's pathname to the remote process' address space
WriteProcessMemory(hProcess, pszLibFileRemote, szLibFile, cb, NULL);
// Get the real address of LoadLibraryA in Kernel32.dll
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
// Create a remote thread that calls LoadLibraryA(DLLPathname)
hThread = CreateRemoteThread(hProcess, NULL, 0,
pfnThreadRtn, pszLibFileRemote, 0, NULL);
// Wait for the remote thread to terminate
WaitForSingleObject(hThread, INFINITE);
return 0;
}