Wine中PE格式文件的加载(三):PE格式文件的加载

前面分析到ntdll中加载了kernel32,然后调用了kernel32的初始化函数__wine_kernel_init。该函数的实现在dlls/kernel32/process.c中。内容较多,就不完整截图了

首先函数开始获取了需要加载的windows应用的路径名,这个就是在开始执行的命令的微信的绝对路径,在wine中一般为“/home/username/.wine/drive_c/Programfile/Tencent/WeChat/WeChat.exe”

这个值在wine开始时将其放入全局变量,使用时还做了一些字符编码格式的处理

__wine_main_argc = argc;

__wine_main_argv = argv;

还有需要注意的是之前创建的PEB,TEB在这个过程中会不断的完善数据,而且需要是使用,比如这里也将路径写入了PEB。

得到了可执行文件的路径,接下来就是加载的具体实现。

 

部分函数代码如下图所示:

LoadLibraryExW在dlls/kernel32/module.c中实现,kernel32是加载了的,因此可以使用该函数。

最终在dlls/ntdll/loader.c中实现,实现函数为LdrLoadDll,这也符合windows以及wine的实现结构,从kernel32到ntdll。

 

LdrLoadDll函数如下图所示:

主要调用了load_dll实现,而load_dll中,根据native还是buitin来分别调用不同的函数load_native_dll、load_buitin_dll。

Load_native_dll函数实现用到了下面两个函数:

先讲NtCreateSection(实现在dlls/ntdll/virtual.c)。在wineserver中,发送了“create_mapping”,在wineserver中创建了一个mapping对象,数据来自file,file是在这之前打开了PE格式文件的fd对应在wine中HANDLE.在mapping创建好后分配了handle将其返回(wine中对象的管理使用handle)

create_mapping函数在server/mapping.c中,函数调用了get_image_params用来获取PE中数据。

下面具体分析get_image_params函数

首先声明一个dos结构体,这是对应pe文件中dos头,这个dos头在使用时是为了兼容dos系统。(该结构体中有一个偏移量数据e_lfanew,为PE头的离文件头部的偏移量。Windows加载器通过它可以跳过DOS Stub部分直接找到PE头)

然后声明了section头。这两个结构体都是在winnt.h中定义的。

还声明一个nt结构体,对应PE头。

其中,两个结构体具体定义分别如下图所示:

然后,通过下面代码分别得到了dos头和pe头。

接下来,执行下面的代码(32位执行上面部分),得到了mapping->base

最后通过上面的代码,加载section头。将pos指向section头开始地址;sec的大小乘个数量得到size;最后用pread函数用lseek控制从pos开始读。

 

上面NtCreateMapping得到了一个mapping,接下来就是将PE文件根据mapping映射到内存中,通过函数NtMapViewOfSection实现。

该函数首先调用了map_image,该函数主要作用是将可执行PE格式文件映射到内存。部分函数代码如下图所示:

在该函数中调用map_view函数,用来创建一个试图并且用mmap保留一块儿相对应的内存区域。

在这里做了判断,是否在预留的内存空间中switch (wine_mmap_is_in_reserved_area( base, size ))然后调用了if ((ptr = wine_anon_mmap( base, size, VIRTUAL_GetUnixProt(vprot),MAP_FIXED )) != base)实际就是做了一个匿名映射。

最后创建结构体file_view,这个结构体对象在真正映射数据时用status =create_view( view_ret, ptr, size, vprot );

map_view部分函数代码如下图所示:

接下来,就是具体的内存映射。调用了几次map_file_into_view函数,先映射PE头,再映射各个section。而映射Section时是根据每一个Section头的sec->VirtualAddress得到数据。

如下图所示:



阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页