⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄

📁 兼容内核漫谈 适合想将Windows上的程序移植到其它平台上的朋友研究查看
💻
📖 第 1 页 / 共 4 页
字号:
                  "\txor %ecx,%ecx\n"
                  "\txor %edx,%edx\n"
                  "\tret\n")[/code]
    注意这里的‘\t’就是作为分隔符的TAB,所以例如“\tpushl %eax\n”就是“pushl %eax”。初始堆栈的位置和内容都是内核为其准备好的,堆栈的内容就是argc,argv[]、envp等等。至于argv[]的内容,则相当于命令行“wine-preloader wine-kthread notepad.exe”。
    我们暂且不去深究这些汇编代码,但是显然这里主要的操作是对wld_start()的调用,所以我们直接看wld_start()。不过有兴趣的读者不妨自己抠一下这里对于堆栈的处理。

[code]/*
*  wld_start
*
*  Repeat the actions the kernel would do when loading a dynamically linked .so
*  Load the binary and then its ELF interpreter.
*  Note, we assume that the binary is a dynamically linked ELF shared object.
*/
void* wld_start( void **stack )
{
    int i, *pargc;
    char **argv, **p;
    char *interp, *reserve = NULL;
    ElfW(auxv_t) new_av[12], delete_av[3], *av;
    struct wld_link_map main_binary_map, ld_so_map;
    struct wine_preload_info **wine_main_preload_info;

    pargc = *stack;
    argv = (char **)pargc + 1;
    if (*pargc < 2) fatal_error( "Usage: %s wine_binary [args]\n", argv[0] );

    /* skip over the parameters */
    p = argv + *pargc + 1;

    /* skip over the environment */
    while (*p)
    {
        static const char res[] = "WINEPRELOADRESERVE=";
        if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
        p++;
    }

    av = (ElfW(auxv_t)*) (p+1);
    page_size = get_auxiliary( av, AT_PAGESZ, 4096 );
    page_mask = page_size - 1;

    preloader_start = (char *)_start - ((unsigned int)_start & page_mask);
    preloader_end = (char *)((unsigned int)(_end + page_mask) & ~page_mask);

#ifdef DUMP_AUX_INFO
    wld_printf( "stack = %x\n", *stack );
    for( i = 0; i < *pargc; i++ ) wld_printf("argv[%x] = %s\n", i, argv[i]);
    dump_auxiliary( av );
#endif

    /* reserve memory that Wine needs */
    if (reserve) preload_reserve( reserve );
    for (i = 0; preload_info[i].size; i++)
        wld_mmap( preload_info[i].addr, preload_info[i].size,
         PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,  -1, 0 );

    /* load the main binary */
    map_so_lib( argv[1], &main_binary_map);

    /* load the ELF interpreter */
    interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
    map_so_lib( interp, &ld_so_map);

    /* store pointer to the preload info into the appropriate main binary variable */
wine_main_preload_info = find_symbol( main_binary_map.l_phdr,
main_binary_map.l_phnum,
                                          "wine_main_preload_info" );
    if (wine_main_preload_info) *wine_main_preload_info = preload_info;
    else wld_printf( "wine_main_preload_info not found\n" );

#define SET_NEW_AV(n,type,val) new_av[n].a_type = (type); new_av[n].a_un.a_val = (val);
    SET_NEW_AV( 0, AT_PHDR, (unsigned long)main_binary_map.l_phdr );
    SET_NEW_AV( 1, AT_PHENT, sizeof(ElfW(Phdr)) );
    SET_NEW_AV( 2, AT_PHNUM, main_binary_map.l_phnum );
    SET_NEW_AV( 3, AT_PAGESZ, page_size );
    SET_NEW_AV( 4, AT_BASE, ld_so_map.l_addr );
    SET_NEW_AV( 5, AT_FLAGS, get_auxiliary( av, AT_FLAGS, 0 ) );
    SET_NEW_AV( 6, AT_ENTRY, main_binary_map.l_entry );
    SET_NEW_AV( 7, AT_UID, get_auxiliary( av, AT_UID, wld_getuid() ) );
    SET_NEW_AV( 8, AT_EUID, get_auxiliary( av, AT_EUID, wld_geteuid() ) );
    SET_NEW_AV( 9, AT_GID, get_auxiliary( av, AT_GID, wld_getgid() ) );
    SET_NEW_AV(10, AT_EGID, get_auxiliary( av, AT_EGID, wld_getegid() ) );
    SET_NEW_AV(11, AT_NULL, 0 );
#undef SET_NEW_AV

    i = 0;
    /* delete sysinfo values if addresses conflict */
    if (is_in_preload_range( av, AT_SYSINFO )) delete_av[i++].a_type = AT_SYSINFO;
if (is_in_preload_range( av, AT_SYSINFO_EHDR ))
delete_av[i++].a_type = AT_SYSINFO_EHDR;
    delete_av[i].a_type = AT_NULL;

    /* get rid of first argument */
    pargc[1] = pargc[0] - 1;
    *stack = pargc + 1;

    set_auxiliary_values( av, new_av, delete_av, stack );

#ifdef DUMP_AUX_INFO
    wld_printf("new stack = %x\n", *stack);
    wld_printf("jumping to %x\n", ld_so_map.l_entry);
#endif

    return (void *)ld_so_map.l_entry;
}[/code]
    简单地说一下这段程序的作用,就是:
    (1)保留本进程用户空间(虚拟地址)的某些区间,将来用于PE映像的装入。
    (2)然后通过map_so_lib()装入wine-kthread的映像,但是要避开保留的那些区间。
    (3)再通过map_so_lib()装入启动ELF映像所需的辅助工具ld-linux.so.2。
    (4)设置一些辅助变量(程序中的new_av[ ]),主要是把有关wine-kthread映像的信息(如程序头数组的位置、数组的大小等等)传递给解释器。
    (5)返回ld-linux.so.2的程序入口地址。

    这里,眼下要装入的是wine-kthread的映像,但是最终要装入的却是Windows应用软件的PE格式映像。PE格式可执行程序映像的装入地址是固定的,所以不能让wine-kthread映像占了它的位置。后者本身的装入位置是固定的,并且不与前者冲突,但是其启动和实际运行却需要动态分配空间,这就可能发生冲突。因此,需要先把PE映像需要用到的地方先加以保留,把它占住,然后才能装入wine-kthread的映像。完成了装入以后,最终就转入到被装入映像wine-kthread中的main()。那么需要为PE映像保留那些区间呢?数组preload_info[]对此作出了规定:

[code]static struct wine_preload_info preload_info[] =
{
    { (void *)0x00000000, 0x00110000 },  /* DOS area */
    { (void *)0x80000000, 0x01000000 },  /* shared heap */
{ (void *)0x00110000, 0x1fef0000 },  /* PE exe range (may be set with
WINEPRELOADRESERVE),
defaults to 512mb */
    { 0, 0 }                             /* end of list */
};[/code]
    首先,从虚拟地址0开始的1MB加64KB的区间是为DOS及其软件保留的。虽然Windows软件已经不是DOS软件,但这是从DOS发展过来的,仍有可能要用到这块空间,所以仍需保留。再往上,从虚拟地址0x00110000开始,大小为0x1fef0000的区间是为PE格式映像本身保留的。实际上这两块空间是连续的,合在一起占了从0到0x20000000、即512MB的空间。除此之外,从地址0x80000000、即2GB边界开始,大小为16MB的空间是为PE映像保留的heap空间、即动态分配的虚拟地址空间(传统上Windows对于32位地址空间的划分是2G+2G,即用户空间和系统空间各2GB,现在也可以像Linux那样划分成3G+1G)。这些都是Windows软件、即PE格式映像的约定,既要装入执行这样的映像,就必须遵守。而保留这些区间的手段,则就是通过上面代码中调用的wld_mmap(),实际上就是系统调用mmap()。注意这样保留的只是当前进程的虚拟地址空间资源,而并不立即就消耗物理存储空间。
另一方面,wine-preloader本身所占用的虚拟地址空间,则是从0x78000000开始的一块不大的空间,反正不会超过0x02000000、即32MB。这样,从0x20000000到0x78000000,大约1.4GB的地址空间仍是空闲的,足够容纳wine-kthread的映像及有关的.so映像。
    为最终要装入的PE映像保留好地址空间以后,就通过map_so_lib()装入wine-kthread的映像。如前所述,此时的argv[1]就是“wine-kthread”,所以装入的就是它的映像。函数map_so_lib()的代码这里就不看了,从函数名看,这个函数是用来装入.so模块、即共享库(动态连接库)的映像的,但实际上也可以用来装入别的ELF映像,所以这里wine-kthread的映像也由map_so_lib()装入。ELF格式的目标映像在装入时需要受到一些协助,以完成与动态连接库、即.so模块的连接。这些协助要由一个工具软件来提供,称为“解释器(interpreter)”。不过这样的解释器并不是作为独立的进程运行,而是本身就以(无需动态连接的)共享库的形式装入目标映像的进程空间中运行;只是这里的动态连接很简单,只要由解释器软件提供一个总的入口即可。解释器模块与创建目标ELF映像时所使用的编译/连接工具是配套的,模块的文件名就写在目标ELF映像的头部数据结构中。所以,一旦装入了目标映像,就可以知道应该配套使用什么解释器,例如“/lib/ld-linux.so.2” (读者不妨这样试一下:“strings wine-kthread | grep ld-linux”,就可以看到wine-kthread的配套解释器就是/lib/ld-linux.so.2)。这里的main_binary_map.l_interp就是该文件名字符串在映像中的位移。所以,上面第二次调用map_so_lib()的目的就是装入ELF映像的解释器。
    此外,这里还通过find_symbol()在已装入的wine-kthread映像中寻找一个名为wine_main_preload_info的变量,找到后就把结构数组preload_info[]的起始地址填写到这个变量中,这是因为wine-kthread也需要知道为PE映像和DLL所保留的空间。
    最后,wld_start()返回ld_so_map.l_entry,这就是ELF映像解释器的入口地址。我们知道,函数的返回值是通过寄存器%eax传递的。回到前面_start()的汇编代码,可以看到它把%eax的内容压入堆栈,然后执行了一条ret指令,这就跳转到了解释器的入口地址。解释器的细节已经不在本文的范围之内,概而言之就是它会装入目标映像运行所需的共享库(例如linc.so),并完成目标映像与共享库的动态连接,最后跳转到目标映像的入口。然后,目标映像在完成了自身的初始化以后,就会调用目其main()。这当然就是wine-kthread的main(),也就是loader/main.c中的main()了。此外,wld_start()在返回之前对堆栈进行了调整,其效果是在原来的argv[]中跳过了argv[0],使原来的argv[1]变成了进入main()时的argv[0]。于是,对于main()来说,这就相当于命令行“wine-kthread notepad.exe”。

[code]int main( int argc, char *argv[] )
{
    char error[1024];
    int i;

    if (wine_main_preload_info)
    {
        for (i = 0; wine_main_preload_info[i].size; i++)
            wine_mmap_add_reserved_area( wine_main_preload_info[i].addr,
                                         wine_main_preload_info[i].size );
    }

    wine_init( argc, argv, error, sizeof(error) );
    fprintf( stderr, "wine: failed to initialize: %s\n", error );

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -