-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
需要修补WINE以使用不同的ld-linux.so #11
Comments
wine第一次启动会wine_exec_wine_binary函数调用execv 运行 wine-preloader,并将 wine path、exe path传递进去,类似于 execv("wine-preloader", {"wine-preloader","wine","TIM.exe"})。 而在wine-preloader中不会执行wine,而是使用: 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 ); 使用map_so_lib加载主程序段与解释器ld-linux,如果找不到ld-linux则会报错。 The first time the wine starts, the wine_exec_wine_binary function calls execv to run wine-preloader, and passes the wine path and exe path, similar to execv("wine-preloader", {"wine-preloader","wine","TIM.exe "}). Instead of executing wine in the wine-preloader, use: 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 ); Use the map_so_lib function to load the main program segment and the interpreter ld-linux. If ld-linux is not found, an error will be reported. |
ld.so是由preloader中的map_so_lib函数手动加载的,所以只需要修改一下preloader对环境变量的支持即可。 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 ); 修改为: map_so_lib( argv[1], &main_binary_map );
/* load the ELF interpreter */
interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
char fullpath[256] = {};
wld_memset(fullpath, 0, sizeof(fullpath));
if (rootpath != NULL)
wld_strcat(fullpath, rootpath);
wld_strcat(fullpath, interp);
map_so_lib( fullpath, &ld_so_map ); 具体参考 Commit AppRun修改为: #!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="$HERE/usr/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib/i386-linux-gnu":$LD_LIBRARY_PATH
#Sound Library
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/pulseaudio":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/alsa-lib":$LD_LIBRARY_PATH
#LD Root Path
export LINUXFILEROOT="$HERE"
"$HERE/lib/ld-linux.so.2" "$HERE/bin/wine" "$@" | cat 我已经测试成功,之后会打包文件。 Ld.so is manually loaded by the map_so_lib function in the preloader, so you only need to modify the preloader support for environment variables. 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 ); change into: Map_so_lib( argv[1], &main_binary_map );
/* load the ELF interpreter */
Interp = (char *) main_binary_map.l_addr + main_binary_map.l_interp;
Char fullpath[256] = {};
Wld_memset(fullpath, 0, sizeof(fullpath));
If (rootpath != NULL)
Wld_strcat(fullpath, rootpath);
Wld_strcat(fullpath, interp);
Map_so_lib( fullpath, &ld_so_map ); Specific reference Commit AppRun is modified to: #!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
Export LD_LIBRARY_PATH="$HERE/usr/lib":$LD_LIBRARY_PATH
Export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu":$LD_LIBRARY_PATH
Export LD_LIBRARY_PATH="$HERE/lib":$LD_LIBRARY_PATH
Export LD_LIBRARY_PATH="$HERE/lib/i386-linux-gnu":$LD_LIBRARY_PATH
#Sound Library
Export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/pulseaudio":$LD_LIBRARY_PATH
Export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/alsa-lib":$LD_LIBRARY_PATH
#LD Root Path
Export LINUXFILEROOT="$HERE"
"$HERE/lib/ld-linux.so.2" "$HERE/bin/wine" "$@" | cat I have tested it successfully and will package the file later. |
这看起来很好。 您想向葡萄酒开发人员发送补丁吗?他们有邮寄名单。也许他们想把它融入官方葡萄酒中。(如果你不想做,我可以代表你去做。我能用英语写作。我会给你全部的信用。) 也许 This looks excellent. Do you plan to send a patch to the WINE developers mailing list so that it can be discussed by the WINE developers and perhaps be integrated there? (If you don't want to do it, I can do it on your behalf and giving your full credit.) Maybe it is more common to use |
wine可能通过wine_exec_wine_binary启动其他程序,而wine_exec_wine_binary调用preloader_exec,然后使用wine-preloader或者系统调用execv启动进程。 static void preloader_exec( char **argv, int use_preloader )
{
if (use_preloader)
{
static const char preloader[] = "wine-preloader";
static const char preloader64[] = "wine64-preloader";
char *p, *full_name;
char **last_arg = argv, **new_argv;
if (!(p = strrchr( argv[0], '/' ))) p = argv[0];
else p++;
full_name = xmalloc( p - argv[0] + sizeof(preloader64) );
memcpy( full_name, argv[0], p - argv[0] );
if (strendswith( p, "64" ))
memcpy( full_name + (p - argv[0]), preloader64, sizeof(preloader64) );
else
memcpy( full_name + (p - argv[0]), preloader, sizeof(preloader) );
/* make a copy of argv */
while (*last_arg) last_arg++;
new_argv = xmalloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = full_name;
execv( full_name, new_argv );
free( new_argv );
free( full_name );
}
execv( argv[0], argv );
} 我再次修改了wine-preloader.c,只需要一点点的改变: while (*p)
{
static const char res[] = "WINEPRELOADRESERVE=", loader[] = "WINELDLIBRARY=";
if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
if (!wld_strncmp( *p, loader, sizeof(loader)-1 )) interp = *p + sizeof(loader) - 1;
p++;
}
//...
/* load the ELF interpreter */
if (interp == NULL)
interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
map_so_lib( interp, &ld_so_map ); 使用环境变量(WINELDLIBRARY)传递ld.so的路径。 #include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
/*
* gcc -shared -fPIC -ldl
* for i386: gcc -shared -fPIC -m32 -ldl
*
* hook wine execv syscall. use special ld.so
* */
typedef int(*EXECV)(const char*, char**);
static inline int strendswith( const char* str, const char* end )
{
size_t len = strlen( str );
size_t tail = strlen( end );
return len >= tail && !strcmp( str + len - tail, end );
}
int execv(char *path, char ** argv)
{
static void *handle = NULL;
static EXECV old_execv = NULL;
char **last_arg = argv;
if( !handle )
{
handle = dlopen("libc.so.6", RTLD_LAZY);
old_execv = (EXECV)dlsym(handle, "execv");
}
char * wineloader = getenv("WINELDLIBRARY");
if (wineloader == NULL || strendswith(path, "wine-preloader"))
{
return old_execv(path, argv);
}
while (*last_arg) last_arg++;
char ** new_argv = (char **) malloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = wineloader;
int res = old_execv(wineloader, new_argv);
free( new_argv );
return res;
} 依旧使用环境变量(WINELDLIBRARY)传递ld.so的路径。 #!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="$HERE/usr/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib/i386-linux-gnu":$LD_LIBRARY_PATH
#Sound Library
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/pulseaudio":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/alsa-lib":$LD_LIBRARY_PATH
#LD
export WINELDLIBRARY="$HERE/lib/ld-linux.so.2"
LD_PRELOAD="$HERE/bin/libhookexecv.so" "$WINELDLIBRARY" "$HERE/bin/wine" "$@" | cat 增加了libhookexecv.so文件,修改了wine-preloader文件,可以指定ld.so。 Wine may start other programs with wine_exec_wine_binary, and wine_exec_wine_binary calls preloader_exec, then use wine-preloader or system call execv to start the process. Static void preloader_exec( char **argv, int use_preloader )
{
If (use_preloader)
{
Static const char preloader[] = "wine-preloader";
Static const char preloader64[] = "wine64-preloader";
Char *p, *full_name;
Char **last_arg = argv, **new_argv;
If (!(p = strrchr( argv[0], '/' ))) p = argv[0];
Else p++;
Full_name = xmalloc( p - argv[0] + sizeof(preloader64) );
Memcpy( full_name, argv[0], p - argv[0] );
If (strendswith( p, "64" ))
Memcpy( full_name + (p - argv[0]), preloader64, sizeof(preloader64) );
Else
Memcpy( full_name + (p - argv[0]), preloader, sizeof(preloader) );
/* make a copy of argv */
While (*last_arg) last_arg++;
New_argv = xmalloc( (last_arg - argv + 2) * sizeof(*argv) );
Memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
New_argv[0] = full_name;
Execv( full_name, new_argv );
Free( new_argv );
Free( full_name );
}
Execv( argv[0], argv );
} I modified wine-preloader.c again, with only a few changes: While (*p)
{
Static const char res[] = "WINEPRELOADRESERVE=", loader[] = "WINELDLIBRARY=";
If (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
If (!wld_strncmp( *p, loader, sizeof(loader)-1 )) interp = *p + sizeof(loader) - 1;
p++;
}
//...
/* load the ELF interpreter */
If (interp == NULL)
Interp = (char *) main_binary_map.l_addr + main_binary_map.l_interp;
Map_so_lib( interp, &ld_so_map ); Use the environment variable (WINELDLIBRARY) to pass the path to ld.so. #include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
/*
* gcc -shared -fPIC -ldl
* for i386: gcc -shared -fPIC -m32 -ldl
*
* hook wine execv syscall. use special ld.so
* */
typedef int(*EXECV)(const char*, char**);
static inline int strendswith( const char* str, const char* end )
{
size_t len = strlen( str );
size_t tail = strlen( end );
return len >= tail && !strcmp( str + len - tail, end );
}
int execv(char *path, char ** argv)
{
static void *handle = NULL;
static EXECV old_execv = NULL;
char **last_arg = argv;
if( !handle )
{
handle = dlopen("libc.so.6", RTLD_LAZY);
old_execv = (EXECV)dlsym(handle, "execv");
}
char * wineloader = getenv("WINELDLIBRARY");
if (wineloader == NULL || strendswith(path, "wine-preloader"))
{
return old_execv(path, argv);
}
while (*last_arg) last_arg++;
char ** new_argv = (char **) malloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = wineloader;
int res = old_execv(wineloader, new_argv);
free( new_argv );
return res;
} The path to ld.so is still passed using the environment variable (WINELDLIBRARY). #!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="$HERE/usr/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib/i386-linux-gnu":$LD_LIBRARY_PATH
#Sound Library
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/pulseaudio":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/alsa-lib":$LD_LIBRARY_PATH
#LD
export WINELDLIBRARY="$HERE/lib/ld-linux.so.2"
LD_PRELOAD="$HERE/bin/libhookexecv.so" "$WINELDLIBRARY" "$HERE/bin/wine" "$@" | cat Added libhookexecv.so file, modified wine-preloader file, you can specify ld.so. |
hook syscall可能显示错误,我不知道原因是什么,ld.so和libhookexecv.so都是32位程序,但是不影响程序的运行。
如果不想hook syscall,可以直接修改preloader_exec函数,在里面获取WINELDLIBRARY,然后使用指定的ld.so static void preloader_exec( char **argv, int use_preloader )
{
if (use_preloader)
{
static const char preloader[] = "wine-preloader";
static const char preloader64[] = "wine64-preloader";
char *p, *full_name;
char **last_arg = argv, **new_argv;
if (!(p = strrchr( argv[0], '/' ))) p = argv[0];
else p++;
full_name = xmalloc( p - argv[0] + sizeof(preloader64) );
memcpy( full_name, argv[0], p - argv[0] );
if (strendswith( p, "64" ))
memcpy( full_name + (p - argv[0]), preloader64, sizeof(preloader64) );
else
memcpy( full_name + (p - argv[0]), preloader, sizeof(preloader) );
/* make a copy of argv */
while (*last_arg) last_arg++;
new_argv = xmalloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = full_name;
execv( full_name, new_argv );
free( new_argv );
free( full_name );
}
else
{
char * wineloader = getenv("WINELDLIBRARY");
if (wineloader == NULL)
execv( argv[0], argv );
char **last_arg = argv;
while (*last_arg) last_arg++;
char ** new_argv = (char **) malloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = wineloader;
execv(wineloader, new_argv);
free( new_argv );
}
} Hook syscall may display an error, I don't know what the reason is, ld.so and libhookexecv.so are 32-bit programs, but does not affect the operation of the program.
If you don't want to hook syscall, you can directly modify the preloader_exec function, get WINELDLIBRARY in it, and then use the specified ld.so static void preloader_exec( char **argv, int use_preloader )
{
if (use_preloader)
{
static const char preloader[] = "wine-preloader";
static const char preloader64[] = "wine64-preloader";
char *p, *full_name;
char **last_arg = argv, **new_argv;
if (!(p = strrchr( argv[0], '/' ))) p = argv[0];
else p++;
full_name = xmalloc( p - argv[0] + sizeof(preloader64) );
memcpy( full_name, argv[0], p - argv[0] );
if (strendswith( p, "64" ))
memcpy( full_name + (p - argv[0]), preloader64, sizeof(preloader64) );
else
memcpy( full_name + (p - argv[0]), preloader, sizeof(preloader) );
/* make a copy of argv */
while (*last_arg) last_arg++;
new_argv = xmalloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = full_name;
execv( full_name, new_argv );
free( new_argv );
free( full_name );
}
else
{
char * wineloader = getenv("WINELDLIBRARY");
if (wineloader == NULL)
execv( argv[0], argv );
char **last_arg = argv;
while (*last_arg) last_arg++;
char ** new_argv = (char **) malloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
new_argv[0] = wineloader;
execv(wineloader, new_argv);
free( new_argv );
}
} |
你可以测试这两种修改的程序(testing releases) You can test these two modified programs(testing releases) |
非常感谢您抽出时间来实现这一点。我对你的解决方案印象非常深刻。 这两个版本对我来说都很好。你更喜欢哪一个? 你想把你的补丁发送到 https://www.winehq.org/mailman/listinfo/wine-devel 吗?如果你喜欢,我也可以代表你去做。 Thank you very much for taking the time to implement this. I am very impressed by your solution. Both versions work very well for me. Which one do you prefer? Do you want to send your patch to https://www.winehq.org/mailman/listinfo/wine-devel? If you like I can also do it on your behalf. |
我觉得两种方法都可以,因为我英文比较差,所以就拜托您了。您可以都向他们提供这两种方法,让他们自己选择,如果他们觉得没有必要修改,也没什么关系。 I think both methods are OK, because my English is poor, so I need your help. You can provide them with these two methods, let them choose, if they feel that there is no need to modify, it does not matter. |
让我们把一个补丁发送到 WINE 的 下面的提交是否遗漏了什么?你能把它更新一下吗? Let's send a patch to WINE for the Is something missing in the following commit? Can you please update it? |
非常同意,我刚刚整理了一遍代码,重新做了一次提交,谢谢你的帮助 -- Hackerl/wine@4f7f784 Very much agree, I just sorted out the code and made a new commit. Thank you for your help -- Hackerl/wine@4f7f784 |
你有没有用某种方式修改过 Did you modify |
你为什么这样问,我没修改过,是不是因为hook execv报错。
Why are you asking this, I have not modified it.
|
The original
Then run like this:
However I get this error:
|
因为你没修改wine-preloader,wine-preloader不使用任何库函数,使用中断系统调用加载ld.so. 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 ); static inline int wld_open( const char *name, int flags )
{
int ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (5 /* SYS_open */), "r" (name), "c" (flags) );
return SYSCALL_RET(ret);
}
static void map_so_lib( const char *name, struct wld_link_map *l)
{
int fd;
unsigned char buf[0x800];
ElfW(Ehdr) *header = (ElfW(Ehdr)*)buf;
ElfW(Phdr) *phdr, *ph;
/* Scan the program header table, collecting its load commands. */
struct loadcmd
{
ElfW(Addr) mapstart, mapend, dataend, allocend;
off_t mapoff;
int prot;
} loadcmds[16], *c;
size_t nloadcmds = 0, maplength;
fd = wld_open( name, O_RDONLY );
if (fd == -1) fatal_error("%s: could not open\n", name );
//....
} 补丁 while (*p)
{
static const char res[] = "WINEPRELOADRESERVE=", loader[] = "WINELDLIBRARY=";
if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
if (!wld_strncmp( *p, loader, sizeof(loader)-1 )) interp = *p + sizeof(loader) - 1;
p++;
}
//...
/* load the ELF interpreter */
if (interp == NULL)
interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
map_so_lib( interp, &ld_so_map ); 已修改文件: wine-preloader-patched.zip Because you didn't modify the wine-preloader, the wine-preloader doesn't use any library functions, and uses the interrupt system call to load ld.so. 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 ); static inline int wld_open( const char *name, int flags )
{
int ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (5 /* SYS_open */), "r" (name), "c" (flags) );
return SYSCALL_RET(ret);
}
static void map_so_lib( const char *name, struct wld_link_map *l)
{
int fd;
unsigned char buf[0x800];
ElfW(Ehdr) *header = (ElfW(Ehdr)*)buf;
ElfW(Phdr) *phdr, *ph;
/* Scan the program header table, collecting its load commands. */
struct loadcmd
{
ElfW(Addr) mapstart, mapend, dataend, allocend;
off_t mapoff;
int prot;
} loadcmds[16], *c;
size_t nloadcmds = 0, maplength;
fd = wld_open( name, O_RDONLY );
if (fd == -1) fatal_error("%s: could not open\n", name );
//....
} patch preload.c while (*p)
{
static const char res[] = "WINEPRELOADRESERVE=", loader[] = "WINELDLIBRARY=";
if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
if (!wld_strncmp( *p, loader, sizeof(loader)-1 )) interp = *p + sizeof(loader) - 1;
p++;
}
//...
/* load the ELF interpreter */
if (interp == NULL)
interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
map_so_lib( interp, &ld_so_map ); Patched file: wine-preloader-patched.zip |
也可以使用 Can that be done using |
能否给出一个完整的脚本来创建 Can you please give a whole script how to create |
wine-preload未来应该不会再有很大的变更,所以你可以使用我编译好的二进制文件替换原始文件.LD_PRELOAD是由ld.so预先加载的,用于hook一些库函数,例如open函数(in libc.so).但是wine-preload使用 int 0x80进行系统调用,或许只能考虑ptrace进行hook syscall. Wine-preload should not change much in the future, so you can replace the original file with my compiled binary. LD_PRELOAD is preloaded by ld.so and is used to hook some library functions, such as the open function (in Libc.so). But wine-preload uses int 0x80 for system calls, perhaps only consider ptrace for hook syscall. |
# Get Wine
wget https://www.playonlinux.com/wine/binaries/linux-x86/PlayOnLinux-wine-3.5-linux-x86.pol
tar xfvj PlayOnLinux-wine-*-linux-x86.pol wineversion/
cd wineversion/*/
# Get suitable old ld-linux.so and the stuff that comes with it
wget -c http://ftp.us.debian.org/debian/pool/main/g/glibc/libc6_2.24-11+deb9u3_i386.deb
dpkg -x libc6_2.24-11+deb9u3_i386.deb .
# Add a dependency library, such as freetype font library
# .....
# Get libhookexecv.so
wget -c https://github.com/probonopd/libhookexecv/releases/download/continuous/libhookexecv.so -O lib/libhookexecv.so
# Get patched wine-preload
wget -c https://github.com/Hackerl/Wine_Appimage/releases/download/testing/wine-preloader-patched.zip
unzip wine-preloader-patched.zip
mv wine-preloader bin/
# Clean
rm wine-preloader-patched.zip
rm libc6_2.24-11+deb9u3_i386.deb
cat > AppRun <<\EOF
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="$HERE/usr/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib/i386-linux-gnu":$LD_LIBRARY_PATH
#Sound Library
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/pulseaudio":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/alsa-lib":$LD_LIBRARY_PATH
#LD
export WINELDLIBRARY="$HERE/lib/ld-linux.so.2"
LD_PRELOAD="$HERE/lib/libhookexecv.so" "$WINELDLIBRARY" "$HERE/bin/wine" "$@" | cat
EOF
chmod +x AppRun
# Run
./AppRun explorer.exe wine-preload will not change much in the future, so it can be directly replaced with a patch file.Now wine can run, but it will report an error because there is no dependent library installed.You can use "apt download" to download the listed dependencies and then extract them using "dpkg -x".
|
I found a way to not modify any files. Because the "wine-preloader" uses "int 0x80" for system calls, ptrace is used for hook syscall. #include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <stdio.h>
#include <syscall.h>
#include <fcntl.h>
#include <string.h>
/*
* sudo apt-get -y install gcc-multilib
* gcc -static preloaderhook.c -o wine-preloader_hook
* for i386: gcc -m32 -static preloaderhook.c -o wine-preloader_hook
* Put the file in the /bin directory, in the same directory as the wine-preloader.
* hook int 0x80 open syscall. use special ld.so
* */
#define LONGSIZE sizeof(long)
#define TARGET_PATH "/lib/ld-linux.so.2"
#define HasZeroByte(v) ~((((v & 0x7F7F7F7F) + 0x7F7F7F7F) | v) | 0x7F7F7F7F)
int main(int argc, char ** argv)
{
printf("======= Ptrace Hook =======\n");
if (argc < 2)
return 0;
int LD_fd = -1;
char * wineloader = (char *) getenv("WINELDLIBRARY");
if (wineloader != NULL)
LD_fd = open(wineloader, O_RDONLY);
pid_t child = fork();
if(child == 0)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execv(*(argv + 1), argv + 1);
}
else
{
while(1)
{
int status = 0;
wait(&status);
if(WIFEXITED(status))
break;
long orig_eax = ptrace(PTRACE_PEEKUSER,
child, 4 * ORIG_EAX,
NULL);
static int insyscall = 0;
static int hasfind = 0;
if (orig_eax == SYS_open)
{
if(insyscall == 0)
{
/* Syscall entry */
insyscall = 1;
//Get Path Ptr
long ebx = ptrace(PTRACE_PEEKUSER,
child, 4 * EBX, NULL);
char Path[256];
memset(Path, 0, 256);
//Read Path String
for (int i = 0; i < sizeof(Path)/LONGSIZE; i ++)
{
union
{
long val;
char chars[LONGSIZE];
} data;
data.val = ptrace(PTRACE_PEEKDATA, child, ebx + i * 4, NULL);
memcpy(Path + i * 4, data.chars, LONGSIZE);
if (HasZeroByte(data.val))
break;
}
if (strcmp(Path, TARGET_PATH) == 0)
hasfind = 1;
}
else
{
/* Syscall exit */
insyscall = 0;
long eax = ptrace(PTRACE_PEEKUSER,
child, 4 * EAX, NULL);
//Modify Open Syscall Ret
if (hasfind && LD_fd != -1)
{
ptrace(PTRACE_POKEUSER, child, 4 * EAX, LD_fd);
ptrace(PTRACE_DETACH, child, NULL, NULL);
break;
}
}
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
}
}
close(LD_fd);
return 0;
}
} Then modify libhookexecv.so and jump to wine-preloader_hook when executing wine-preloader. #include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
/* Author: https://github.com/Hackerl/
* https://github.com/Hackerl/Wine_Appimage/issues/11#issuecomment-445724165
* sudo apt-get -y install gcc-multilib
* gcc -shared -fPIC -ldl libhookexecv.c -o libhookexecv.so
* for i386: gcc -shared -fPIC -m32 -ldl libhookexecv.c -o libhookexecv.so
*
* hook wine execv syscall. use special ld.so
* */
typedef int(*EXECV)(const char*, char**);
static inline int strendswith( const char* str, const char* end )
{
size_t len = strlen( str );
size_t tail = strlen( end );
return len >= tail && !strcmp( str + len - tail, end );
}
int execv(char *path, char ** argv)
{
static void *handle = NULL;
static EXECV old_execv = NULL;
char **last_arg = argv;
if( !handle )
{
handle = dlopen("libc.so.6", RTLD_LAZY);
old_execv = (EXECV)dlsym(handle, "execv");
}
char * wineloader = getenv("WINELDLIBRARY");
if (wineloader == NULL)
{
return old_execv(path, argv);
}
while (*last_arg) last_arg++;
char ** new_argv = (char **) malloc( (last_arg - argv + 2) * sizeof(*argv) );
memcpy( new_argv + 1, argv, (last_arg - argv + 1) * sizeof(*argv) );
char * pathname = NULL;
char hookpath[256];
memset(hookpath, 0, 256);
if (strendswith(path, "wine-preloader"))
{
strcat(hookpath, path);
strcat(hookpath, "_hook");
wineloader = hookpath;
}
new_argv[0] = wineloader;
int res = old_execv(wineloader, new_argv);
free( new_argv );
return res;
} test file https://github.com/Hackerl/Wine_Appimage/releases/tag/testing |
|
上一个版本的程序,如果ld.so存在,会导致打开多余的文件句柄,所以"Open Syscall"不应该被执行。在系统调用发生时,将调用号修改为-1,调用必然会失败,然后再填入正确的fd. The previous version of the program, if ld.so exists, will cause the extra file handle to be opened, so "Open Syscall" should not be executed. When the system call occurs, the call number is changed to -1, the call will fail, and then fill in the correct fd. #include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <stdio.h>
#include <syscall.h>
#include <fcntl.h>
#include <string.h>
/*
* sudo apt-get -y install gcc-multilib
* only for i386: gcc -m32 -static preloaderhook.c -o wine-preloader_hook
* Put the file in the /bin directory, in the same directory as the wine-preloader.
* hook int 0x80 open syscall. use special ld.so
* */
#define LONGSIZE sizeof(long)
#define TARGET_PATH "/lib/ld-linux.so.2"
#define HasZeroByte(v) ~((((v & 0x7F7F7F7F) + 0x7F7F7F7F) | v) | 0x7F7F7F7F)
#define HOOK_OPEN_LD_SYSCALL -1
int main(int argc, char ** argv)
{
printf("======= Ptrace Hook =======\n");
if (argc < 2)
return 0;
char * wineloader = (char *) getenv("WINELDLIBRARY");
if (wineloader == NULL)
{
printf("WINELDLIBRARY Not Found\n");
return 0;
}
int LD_fd = open(wineloader, O_RDONLY);
if (LD_fd == -1)
{
printf("WINELDLIBRARY Open Failed\n");
return 0;
}
pid_t child = fork();
if(child == 0)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execv(*(argv + 1), argv + 1);
}
else
{
while(1)
{
int status = 0;
wait(&status);
if(WIFEXITED(status))
break;
long orig_eax = ptrace(PTRACE_PEEKUSER,
child, 4 * ORIG_EAX,
NULL);
static int insyscall = 0;
if (orig_eax == HOOK_OPEN_LD_SYSCALL)
{
printf("Modify Open Syscall: %s\n", wineloader);
ptrace(PTRACE_POKEUSER, child, 4 * EAX, LD_fd);
//Detch
ptrace(PTRACE_DETACH, child, NULL, NULL);
break;
}
if (orig_eax == SYS_open)
{
if(insyscall == 0)
{
/* Syscall entry */
insyscall = 1;
//Get Path Ptr
long ebx = ptrace(PTRACE_PEEKUSER,
child, 4 * EBX, NULL);
char Path[256];
memset(Path, 0, 256);
//Read Path String
for (int i = 0; i < sizeof(Path)/LONGSIZE; i ++)
{
union
{
long val;
char chars[LONGSIZE];
} data;
data.val = ptrace(PTRACE_PEEKDATA, child, ebx + i * 4, NULL);
memcpy(Path + i * 4, data.chars, LONGSIZE);
if (HasZeroByte(data.val))
break;
}
if (strcmp(Path, TARGET_PATH) == 0)
{
printf("Found Open Syscall: %s\n", TARGET_PATH);
//Modify Syscall -1. So Will Not Call Open Syscall.
ptrace(PTRACE_POKEUSER, child, 4 * ORIG_EAX, HOOK_OPEN_LD_SYSCALL);
}
}
else
{
/* Syscall exit */
insyscall = 0;
}
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
}
}
close(LD_fd);
return 0;
} |
我想知道我们是否可以使用
I am wondering whether we could use |
我觉得unionfs-fuse很优雅地解决了问题,使用"hook"反而会很麻烦.如果只是针对少数的系统调用,"hook"方式会显得很精致高效.但如果想实现应用程序的读写重定向,将需要"hook"所有读写相关系统调用.工作量十分巨大,而且应用运行会很低效. I think unionfs-fuse solves the problem elegantly. It is very troublesome to use "hook". If it is only for a few system calls, the "hook" method will be very delicate and efficient. But if you want to implement read and write redirection of the application. , will need to "hook" all read and write related system calls. The workload is very large, and the application will be very inefficient. |
我在 https://github.com/probonopd/libhookexecv/releases 上进行构建,但是似乎仍然有一些问题可能与unionfs-fuse有关。你能看一下吗? I am making builds at https://github.com/probonopd/libhookexecv/releases but I seem to still have some issues possibly related to unionfs-fuse. Would you have a look? This is what I have used to make it: https://github.com/probonopd/libhookexecv/blob/8d1febe710c3581e3498f940b9e6142e6a7e4d1b/winedeploy.sh |
我测试了你打包的appimage,无法运行的原因是wine虚拟环境(wineprefix文件夹)有问题.我替换成另一个环境,完全可以运行起来. I tested your packaged appimage, the reason it can't run is that there is a problem with the wine virtual environment (wineprefix folder). I replaced it with another environment and it works perfectly. #!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="$HERE/usr/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/lib/i386-linux-gnu":$LD_LIBRARY_PATH
# Sound Library
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/pulseaudio":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH="$HERE/usr/lib/i386-linux-gnu/alsa-lib":$LD_LIBRARY_PATH
# LD
export WINELDLIBRARY="$HERE/lib/ld-linux.so.2"
export WINEDLLOVERRIDES="mscoree,mshtml=" # Do not ask to install Mono or Gecko
export WINEDEBUG=-all # Do not print Wine debug messages
# Load Explorer if no arguments given
EXPLORER=""
if [ -z "$@" ] ; then
EXPLORER="explorer.exe"
fi
# Load bundled WINEPREFIX if existing
MNT_WINEPREFIX="$HOME/.QQ.unionfs" # Use the name of the app
atexit()
{
killall "$WINELDLIBRARY" && sleep 0.1 && rm -r "$MNT_WINEPREFIX"
}
if [ -d "$HERE/wineprefix" ] ; then
RO_WINEPREFIX="$HERE/wineprefix" # WINEPREFIX in the AppDir
TMP_WINEPREFIX_OVERLAY=/tmp/QQ # Use the name of the app
mkdir -p "$MNT_WINEPREFIX" "$TMP_WINEPREFIX_OVERLAY"
"$WINELDLIBRARY" "$HERE/usr/bin/unionfs-fuse" -o use_ino,uid=$UID -ocow "$TMP_WINEPREFIX_OVERLAY"=RW:"$RO_WINEPREFIX"=RO "$MNT_WINEPREFIX" || exit 1
export WINEPREFIX="$MNT_WINEPREFIX"
echo "Using $HERE/wineprefix mounted to $WINEPREFIX"
trap atexit EXIT
fi
# LANG=C is a workaround for: "wine: loadlocale.c:129: _nl_intern_locale_data: Assertion (...) failed"; FIXME
LANG=C LD_PRELOAD="$HERE/lib/libhookexecv.so" "$WINELDLIBRARY" "$HERE/bin/wine" "$@" "$EXPLORER" | cat
atexit函数用来退出时清理,但是"killall $WINELDLIBRARY"会杀死wine的进程,使得一些虚拟的win32进程不会退出。我测试几次后发现大量的残留进程,类似于"C:\Windows....",所以你需要针对性的杀死unionfs-fuse进程。 另外,你将"$HOME/.QQ.unionfs"作为顶层虚拟文件节点,读写节点为"/tmp/QQ"。所以wine写入的新数据都存放在"/tmp/QQ",而"$HOME/.QQ.unionfs"文件夹只是虚拟的。在unionfs-fuse结束之后,"$HOME/.QQ.unionfs"将变为空文件夹。 而系统重启之后,"/tmp/QQ"将被清空,所以用户使用应用时写入的数据将会丢失。 可以参考一些下面的脚本: The atexit function is used to clean up when exiting, but "killall $WINELDLIBRARY" will kill the wine process, making some virtual win32 processes not exit. I tested a few times and found a lot of residual processes, similar to "C:\Windows....", so you need to kill the unionfs-fuse process in a targeted manner. In addition, you will use "$HOME/.QQ.unionfs" as the top-level virtual file node and the read-write node as "/tmp/QQ". So the new data written by wine is stored in "/tmp/QQ", and the "$HOME/.QQ.unionfs" folder is only virtual. After the unionfs-fuse ends, "$HOME/.QQ.unionfs" will become an empty folder. Can refer to some of the following scripts: #!/bin/bash
#......
#......
RO_DATADIR="$HERE/wineprefix"
RW_DATADIR="$HOME/.AppName"
TOP_NODE="/tmp/.AppName.unionfs"
mkdir -p $RW_DATADIR $TOP_NODE
"$WINELDLIBRARY" $HERE/usr/bin/unionfs-fuse -o use_ino,nonempty,uid=$UID -ocow "$RW_DATADIR"=RW:"$RO_DATADIR"=RO "$TOP_NODE" || exit 1
function finish {
echo "Cleaning up"
#need to kill unionfs-fuse
}
trap finish EXIT
export WINEPREFIX="$TOP_NODE"
# LANG=C is a workaround for: "wine: loadlocale.c:129: _nl_intern_locale_data: Assertion (...) failed"; FIXME
LANG=C LD_PRELOAD="$HERE/lib/libhookexecv.so" "$WINELDLIBRARY" "$HERE/bin/wine" "$@" "$EXPLORER" | cat 应用写入的新数据存放在"$HOME/.AppName",所以不会发生数据丢失。 The new data written by the application is stored in "$HOME/.AppName", so no data loss will occur. Finally, I suggest that you package wine as a separate appimage and create a "/usr/bin/wine" soft link to better separate Windows applications and wine. For example, when wine has a big upgrade, just replace wine.appimage, and the application can enjoy a more complete Windows operating system simulation. And the wine is independent, saving hard disk space. If the wine is independent, the problem you are encountering now will be easier to debug. |
|
https://github.com/probonopd/libhookexecv/releases
|
Yes |
为了运行32位Windows应用程序,必须使用32位Windows,这又需要32位
ld-linux.so.2
和glibc
。但是现在大多数64位系统都没有安装32位兼容性库。对于其他程序,通常可以手动加载32位ELF文件,并使用一个私有的“ld-linux.so.2”捆绑版本,如下所示:
但是,对于WINE,这是行不通的。我的猜测是,WINE通过后台的其他机制启动其他的WINE实例,而后者不会使用指定的
"$HERE/lib32/ld-linux.so.2"
和--library-path "$HERE/lib32"
。葡萄酒开发商亚历山大Julliard [葡萄酒开发回答](https://www.winehq.org/pipermail/wine-devel/2017-November/119944.html):
:建设:这需要完成。我们将非常欢迎捐款。
与此同时,我们可以通过在
/ tmp
这个固定位置放置一个符号链接到我们自定义的ld-linux.so.2
来避开这个限制,但这是一个很丑恶的事情。In order to run 32-bit Windows applications, 32-bit Windows must be used, which in turn requires 32-bit
ld-linux.so.2
andglibc
. But most 64-bit systems these days don't have the 32-bit compatibility libraries installed anymore.With other programs it is usually possible to manually load the 32-bit ELF file with a private, bundled version of
ld-linux.so.2
like so:However, with WINE, this does not work. My guess is that WINE launches other WINE instances through other mechanisms in the background, which in turn don't get loaded using the specified
"$HERE/lib32/ld-linux.so.2"
and--library-path "$HERE/lib32"
.WINE developer Alexandre Julliard answered on wine-devel:
🚧 This needs to be done. We would highly welcome contributions.
In the meantime, we may get around this limitation by placing a symlink to our custom
ld-linux.so.2
at a fixed location such as/tmp
, but it is an ugly hack.The text was updated successfully, but these errors were encountered: