软件安全和恶意代码综合题总结

一、 虚实地址转换(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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43790779/article/details/118312962