写时复制,读时加载
实现写时复制,读时加载,原理为,申请内存时,只给一段线性地址空间,并不分配物理内存,当cpu读、写该内存时,发生缺页中,或者写错误,中断处理程序根据前面设置的内容,决写分配物理内存,或者是共享内存,如果需要读取文件时,则根据需要读取相应的文件数据。
主要代码如下:
LPPE_FILE_ELEM load_pe_dll(char * lpFileName) {
LPPE_FILE_ELEM lpSysFE, lpUserFE; //文件列表中的元素,保存文件信息
//文件加载内存的默认地址
//PIMAGE_DOS_HEADER pdh; //dos头
PIMAGE_NT_HEADERS pnh; //pe文件头
LPVOID pfile, pKrFile;
PMEMORY_BASIC_INFORMATION pmbi;
BYTE buf[512]; //临时块
//补全文件目录路径
add_patch((char *) buf, lpFileName);
//1、从系统进程中查找是否已经加载
lpSysFE = pe_find_file((char*) buf, get_sys_files_list());
if (lpSysFE) {
//系统中已经加载,进入下一步
pKrFile = (LPVOID) lpSysFE->hModule;
DbgPrint("dll已读入内存 : %s ,%x\n", lpSysFE->strFilePath,
lpSysFE->dwNumberOfShares);
//return lpFE->hModule;
} else {
//将文件加入系统文件列表中
lpSysFE = kr_malloc(sizeof(PE_FILE_ELEM) + strlen((char*) buf) + 1);
strcpy(lpSysFE->strFilePath, (char*) buf);
if (load_file(lpSysFE->strFilePath, 0, 512, buf)) {
//新exe头部的文件地址 指向PE
pnh = (PIMAGE_NT_HEADERS) ((DWORD) buf
+ ((PIMAGE_DOS_HEADER) buf)->e_lfanew);
//系统进程中仅申请空间,读时加载
pmbi = mem_virtual_alloc(&stMbiSys, 0,
pnh->OptionalHeader.SizeOfImage, MEM_COMMIT,
PAGE_READWRITE,
MEM_IMAGE);
pKrFile = (PBYTE) pmbi->BaseAddress;
lpSysFE->hModule = (DWORD) pKrFile;
lpSysFE->dwImgsize = pnh->OptionalHeader.SizeOfImage;
lpSysFE->dwNumberOfShares = 0;
list_push(get_sys_files_list(), (PLIST_ELEM) lpSysFE);
load_pe_text((PIMAGE_DOS_HEADER) buf, (DWORD) lpSysFE->hModule,
lpSysFE->strFilePath);
if (!pKrFile) {
//失败
//DbgPrint("dll已读入内存 : %s ,%x\n");
} else {
//初始化时设置文件共享的次数为0,加入系统加载文件队列。
}
}
} // if (lpFE)
//2、检查用户空间 当前文件是否已经加载
lpUserFE = pe_find_file(lpSysFE->strFilePath, get_current_file_list());
if (lpUserFE) {
//2、已经加载 直接返回
return lpUserFE;
} else {
//第一次加入进程,将pKrfile的物理地址映射到 pfile
pnh = (PIMAGE_NT_HEADERS) ((((PIMAGE_DOS_HEADER) pKrFile)->e_lfanew)
+ (DWORD) pKrFile);
pmbi = mem_virtual_alloc(
MBI_USER_BASE, (LPVOID) pnh->OptionalHeader.ImageBase,
pnh->OptionalHeader.SizeOfImage,
MEM_COMMIT, PAGE_READWRITE, MEM_IMAGE);
pfile = (PBYTE) pmbi->BaseAddress;
DbgPrint("共享 dll: %s,%x,%x,%x,%x\n", lpSysFE->strFilePath, pfile,
pKrFile, pnh->OptionalHeader.ImageBase);
lpUserFE = kr_malloc(
strlen((char*) lpSysFE->strFilePath) + sizeof(PE_FILE_ELEM)
+ 1);
strcpy(lpUserFE->strFilePath, lpSysFE->strFilePath);
lpUserFE->hModule = (DWORD) pfile;
lpUserFE->dwImgsize = lpSysFE->dwImgsize;
lpUserFE->hSysModule = pKrFile;
list_push(get_current_file_list(), (PLIST_ELEM) lpUserFE);
lpSysFE->dwNumberOfShares++;
DoRelocationTable(pfile);
link_import(pfile);
//文件头映射
// mem_map_demand(pKrFile, pfile,
// _ALIGN( pdh->e_lfanew + sizeof(IMAGE_NT_HEADERS32)
// + pnh->FileHeader.NumberOfSections
// * sizeof(IMAGE_SECTION_HEADER), 4096),
// PG_USER_R_P);
}
return lpUserFE;
}
BOOL mem_fail_sys(DWORD code, DWORD addr) {
DWORD temp;
LPPE_FILE_ELEM fe;
PMEMORY_BASIC_INFORMATION pmbi =mem_find_mbi(&stMbiSys,
addr);
//DbgPrint("内核%s错误 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",(code&2?"写":"读"), addr, code,
// pmbi->BaseAddress, pmbi->RegionSize, pmbi->State, pmbi->Type);
if (!pmbi) {
return FALSE;
}
//如果是映射
if (pmbi->State == MEM_COMMIT) {
//分配物理内存,以4K为单位,
if (pmbi->Type == MEM_IMAGE) {
mem_physics(addr, 4096, PGE_SYS_RW_P);
fe = (LPPE_FILE_ELEM) pe_find_from_addr(get_sys_files_list(), addr);
if (fe->dwNumberOfShares != 0) {
//直接分配内存,再读取文件
//200对齐
load_file_4k(fe, addr);
} else {
//正在读文件头
}
} else if (pmbi->Type == MEM_4MB_PAGES) {
// MEM_4MB_PAGES:
//二级目录
// DbgPrint("二级页表%x,%x\n", addr, (DWORD *) MiGetPteAddress(addr));
mem_physics(addr, 4096, PG_USER_RW_P);
//*(DWORD *) MiGetPteAddress(addr) =
// get_freed_physics() + PG_USER_RW_P;
} else {
mem_physics(addr, 4096, PGE_SYS_RW_P);
}
//直接分配内存
} //if (pmbi->State == MEM_COMMIT)
else {
return FALSE;
}
return TRUE;
}
BOOL mem_fail_user(DWORD code, DWORD addr) {
DWORD temp;
LPPE_FILE_ELEM fe;
PMEMORY_BASIC_INFORMATION pmbi =mem_find_mbi(MBI_USER_BASE, addr);
//DbgPrint("用户%s错误 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", (code&2?"写":"读"),addr, code,
// pmbi->BaseAddress, pmbi->RegionSize, pmbi->State, pmbi->Type);
if (!pmbi) {
//找不分配的段。
return FALSE;
}
//状态是不是已经提交
//只读,读写,还共享的读写
//如果是map类,分配内存,如果Image类,则读时加载,写时复制
if (pmbi->State == MEM_COMMIT) {
if (pmbi->Type == MEM_IMAGE) {
fe = (LPPE_FILE_ELEM) pe_find_from_addr(get_current_file_list(),
addr);
if (fe->hSysModule == 0) {
//说明是不是共享文件
//DbgPrint("用户内存错误 0x%x , 0x%x , 0x%x , 0x%x \n", addr, code,
// pmbi->BaseAddress, pmbi->State);
mem_physics(addr, 4096, PG_USER_RW_P);
load_file_4k(fe, addr);
//break;
} else {
//DbgPrint("用户内存错误 %x,%x\n",addr,fe);
//先读取系统内存,如果出错则会中断到mem_fail_sys,加载文件
//如果是读取,则映射,如果是写则分配内存再复制
if (code & 2) {
//重新申请物理内,再复制
//DbgPrint("用户写错误%x\n", addr);
mem_physics(addr, 4096, PG_USER_RW_P);
memcpy((PVOID) (addr & 0xFFFFF000),
(PVOID) ((fe->hSysModule + (addr - fe->hModule))
& 0xFFFFF000), 4096);
} else {
//DbgPrint("用户读错误%x\n", addr);
//说明可能内核没有读取文件,也可能没有映射
//先试读下,没有读取文件会中断后读取,再映射
//mem_fail_sys(0,fe->hSysModule+addr-fe->hModule);
temp = *(PDWORD) (fe->hSysModule + addr - fe->hModule);
//函数自己内部齐,size=1~4096效果应是一样
mem_map_demand(fe->hSysModule + addr - fe->hModule, addr,
4096, PG_USER_R_P);
}
//直接分配内存,再读取文件
//addr-pmbi->BaseAddressg
}
} else if ((pmbi->Type == MEM_PRIVATE)) {//if (pmbi->Type == MEM_IMAGE)
mem_physics(addr, 4096, PG_USER_RW_P);
}
} // if (pmbi->State == MEM_COMMIT)
else {
return FALSE;
}
return TRUE;
}