Hi,
I have observed a DLL loading prolem when injecting into another process.
For example,
In folder C:\DirA, I have A.exe which is linked to B.dll, and B.dll is linked to C.dll. B.dll and C.dll are in same folder as A.exe.
If I run A.exe, B.dll and C.dll will be loaded without problem since they are in same folder.
Support there is a process running C:\DirX\X.exe, and C:\DirX\ is not the current folder and it's not on PATH.
Now I inject B.dll from A.exe into process X.exe, the System will map C:\DirA\B.dll into process X.exe.
Since B.dll is linked to C.dll, the system will try to load C.dll for process X.exe. But because C.dll is not in same folder of process X.exe, wine failed to load C.dll for X.exe. This cause DLL injection to fail.
Windows does not have this problem. So I am trying to find a solution.
I think when processing the imports for C:\DirA\B.dll to load C.dll, it should try C:\DirA\C.dll first, then form the standard load locations.
I think it should call load_library( "C:\DirA\C.dll ", LOAD_WITH_ALTERED_SEARCH_PATH) so C.dll can be loaded into C:\DirX\X.exe process.
Alexandre, can you please comment ?
Regards Hongbo Ni
_________________________________________________________________ Want to help Windows Live Messenger plant more Aussie trees? http://livelife.ninemsn.com.au/article.aspx?id=443698
Hongbo Ni hongbo_ni@hotmail.com writes:
I think it should call load_library( "C:\DirA\C.dll ", LOAD_WITH_ALTERED_SEARCH_PATH) so C.dll can be loaded into C:\DirX\X.exe process.
Alexandre, can you please comment ?
You should write test cases.
Hongbo Ni wrote:
I think when processing the imports for C:\DirA\B.dll to load C.dll, it
should
try C:\DirA\C.dll first, then form the standard load locations.
I think it should call load_library( "C:\DirA\C.dll ",
LOAD_WITH_ALTERED_SEARCH_PATH)
so C.dll can be loaded into C:\DirX\X.exe process.
Not sure about what you mean with injecting here but in normal application operation when loading a DLL explicitedly from a specific directory (which is not the current directory nor the application directory or one of the standard search paths), Windows will not resolve to other DLLs implicitedly loaded by that DLL eventhough they are in the same directory than the referencing DLL.
Rolf Kalbermatter
Rolf Kalbermatter wrote:> > Not sure about what you mean with injecting here but in normal application operation> when loading a DLL explicitedly from a specific directory (which is not the current> directory nor the application directory or one of the standard search paths), Windows
did you mean 'Wine', i know ms-windows has no problem.
will not resolve to other DLLs implicitedly loaded by that DLL eventhough they are in> the same directory than the referencing DLL.
This is exactly the another situation I am going to explain. Thanks.
I have produced a patch to load the imported dll from same directory as the referring DLL when import_dll() fail to load from standard locations (current dir, app dir and PATH)
But I did not know how to write test case for it since there are other DLL involved.
Regards Hongbo Ni _________________________________________________________________ Windows Live Messenger treats you to 30 free emoticons - Bees, cows, tigers and more! http://livelife.ninemsn.com.au/article.aspx?id=567534
Hongbo Ni wrote:
Not sure about what you mean with injecting here but in normal
application operation
when loading a DLL explicitedly from a specific directory (which is not
the current
directory nor the application directory or one of the standard search
paths),
Windows
did you mean 'Wine', i know ms-windows has no problem.
No I was refereing to Windows and in my experience this is true for Windows at least up to XP.
will not resolve to other DLLs implicitedly loaded by that DLL eventhough
they are in
the same directory than the referencing DLL.
This is exactly the another situation I am going to explain. Thanks.
It's been some time that I had to deal with this in an application someone else had developed as I have always made sure to avoid this for a long time.
But from working in higher level applications loading DLLs form a specific absolut path, this loading usually seems to fail if this DLL references other DLLs that are not in the standard Windows search locations (memory, application dir, windows and system dir, and PATH environment variable) eventhough those dependant DLLs reside in the same directory as the DLL that was just attempted to be loaded.
It may be that you have to make sure to not modify the current directory in the process of determining the path to load the parent DLL from. I've seen for instance in those higher level development environments that loading such DLL combos failed on the initial application load only to be prompted to locate the parent DLL in question with a file dialog and then it magically seemed to work. This is because the windows file dialog will automagically modify the current directory for the current process and now the implicit LoadLibrary during the loading of the parent DLL seems to also find the dependant DLLs in that same directory.
And this behaviour was certainly like that up to and including W2K but I do believe even XP.
That is not to say that Wine might not have to do something extra here, for instance to support new Vista behaviour which seems to have complicated the whole DLL loading procedure a bit.
I have produced a patch to load the imported dll from same directory as the referring DLL when import_dll() fail to load from standard locations (current dir, app dir and PATH)
But I did not know how to write test case for it since there are other DLL
involved.
Well it will be a bit cumbersome as you will have to create two extra dummy DLLs A.dll and B.dll where A.dll implicitedly loads B.dll and then load A.dll from the test executable. A.dll and B.dll would have to reside in a different directory (possibly subdirectory) to the actual test executable and of course can't be inside any directory considered a windows or system directory. Also make sure you set the current directory to something else than that directory. And then see what happens when this test is crosscompiled and run under Windows. If the LoadLibrary of A.dll does not fail under Windows then yes Wine will need some extra functionality to replicate that behaviour.
My guess is that it will however fail on most Windows versions except maybe Vista which would still be a reason to try to implement that behaviour in Wine too. However you will likely have to do quite a bit more tests to then figure out the exact conditions under which this behaviour is applied under newer Windows versions.
Rolf Kalbermatter
Rolf Kalbermatter wrote:
My guess is that it will however fail on most Windows versions except maybe Vista which would still be a reason to try to implement that behaviour in Wine too. However you will likely have to do quite a bit more tests to then figure out the exact conditions under which this behaviour is applied under newer Windows versions.
You are right, when loading a DLL from specific folder, the linked DLL in same folder can not be loaded. I have tested on Windows Vista.
Back to DLL injecting, It works since Windows 95. My program has been working for many years, but not on Wine.
Here is how to inject a DLL into another process:
A.exe is linked to A.dll, A.dll is linked to B.dll, they are all in the same folder.
in A.dll I have a function called InjectDll(), it calls SetWindowsHookEx(WH_GETMESSAGE,...) to inject A.dll into Notepad process. Since A.dll is lined to B.dll, B.dll will be loaded on window, but on wine.
//in A.dll LRESULT WINAPI GetMsgProc (int nCode, WPARAM wParam, LPARAM lParam) { if(g_hHookGetMsg==NULL) return 0; return(CallNextHookEx(g_hHookGetMsg, nCode, wParam, lParam)); }
DLLA_API int InjectDll(void) { HWND hwnd = FindWindow(NULL,"Notepad"); if(hwnd){ g_hHookGetMsg = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hinstDll,GetWindowThreadProcessId(hwnd, NULL)); if(g_hHookGetMsg){ PostMessage(hwnd, WM_NULL, 0, 0); } } return fnDllB(); //in DLLB.dll }
Maybe in window SetWindowsHookEx not only map A.dll, but so all DLL linked by A.dll into Hooked Window process.
Also at the time A.exe is injecting A.dll, B.dll is loaded, so the system knows where B.dll is. Now I am wondering how window do that.
To Prove windows do it, I have put a Test suite at http://www.njstar.com/zipfile/HookApp.zip It contains source project and compiled exe in BIN folder.
1. start BIN\App-exe\App.exe first, it's is not linked to DLLA or DLLB.
2. start BIN\HookApp.exe and DLLA and DLLB is linked and loaded.
3. Click BIN\HookApp.exe's menu Inject. it will inject DLLA into BIN\App-exe\App.exe, and DLLB will also got loaded into BIN\App-exe\App.exe on Windows. This will be fail on Wine.
Regards Hongbo
_________________________________________________________________ Are you paid what you're worth? Find out: SEEK Salary Centre http://a.ninemsn.com.au/b.aspx?URL=http%3A%2F%2Fninemsn%2Eseek%2Ecom%2Eau%2F...
Hongbo Ni wrote:
Back to DLL injecting, It works since Windows 95. My program has been working for many years, but not on Wine.
Here is how to inject a DLL into another process: .....
Ok I think I understand. You are using a technique I have once read about in Microsoft System Journal back in about 1994. And this is certainly tricky.
Eventhough I'm anything but an expert in these matters I do think that the code that receives a message for a process and checks if a WH_GETMESSAGE hook is set and in that case maps the according DLL into the process space if that hasn't already happened, should also track down all implicit dependencies of that DLL and map them into the process space too.
It may currently just do a LoadLibrary equivalent and that will of course not find the B.dll as it is not in any of the standard search locations for the injected process.
I do not think that the solution is to "correct" LoadLibrary to actually do a search for linked libraries in the parent library's directory too.
However I fear the proper fix might go deep into Wine server and probably is above most people's head except for Alexandre. What you can do is trying to get an actual test integrated to the Wine tests that does what you do in your test that you wrote and mark that test as todo_wine. And then hope that Alexandre can find a way to implement a fix without to much trouble.
Of course if you can find the way to implement that correctly a second patch after the test has been added as todo_wine would certainly be welcome.
Rolf Kalbermatter
On Fri, Aug 1, 2008 at 6:25 PM, Rolf Kalbermatter r.kalbermatter@hccnet.nl wrote:
Ok I think I understand. You are using a technique I have once read about in Microsoft System Journal back in about 1994. And this is certainly tricky.
Its also documented in Inside Windows by Jeffery Richter if you happen to have a copy of that laying around.
On Sat, Aug 2, 2008 at 2:10 AM, Steven Edwards winehacker@gmail.com wrote:
Its also documented in Inside Windows by Jeffery Richter if you happen to have a copy of that laying around.
Sorry make that Advanced Windows. I gave my copy to a friend so I don't recall much about the chapter, except there is example code that could be used for testing that came with the cd in the book.
Rolf Kalbermatter wrote:
I do not think that the solution is to "correct" LoadLibrary to actually do a search for linked libraries in the parent library's directory too.
However I fear the proper fix might go deep into Wine server and probably is above most people's head except for Alexandre. What you can do is trying to get an actual test integrated to the Wine tests that does what you do in your test that you wrote and mark that test as todo_wine. And then hope that Alexandre can find a way to implement a fix without to much trouble.
Of course if you can find the way to implement that correctly a second patch after the test has been added as todo_wine would certainly be welcome.
What I have descripted is only one way of hook to another process, there are other ways.
Good news, It turns out the fix could be a simple patch as this
@ -322,7 +322,7 @@ void *get_hook_proc( void *proc, const WCHAR *module ) { TRACE( "loading %s\n", debugstr_w(module) ); /* FIXME: the library will never be freed */ - if (!(mod = LoadLibraryW(module))) return NULL; + if (!(mod = LoadLibraryExW(module, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))) return NULL; } return (char *)mod + (ULONG_PTR)proc; }
When my debug, I noticed 'module' comes in full path. so LoadLibraryW will load the module, but when it process imports, it searchs the application dir, current dir and PATH. So DLL in the same dir can not be loaded.
By using LOAD_WITH_ALTERED_SEARCH_PATH, LoadLibraryExW will put in path of 'module' on the load_path, so fix-imports can load linked DLLs by searching the dir of injected dll first, then the current and PATH.
In process HOOK, the target proc has no idea of the injected DLL comes from. The linked DLLs are most likely NOT in the same dir as current hooked application(s). More likely in the same dir as injected DLL .
What do I have to do to get this patch accepted ?
Hongbo Ni
_________________________________________________________________ Are you paid what you're worth? Find out: SEEK Salary Centre http://a.ninemsn.com.au/b.aspx?URL=http%3A%2F%2Fninemsn%2Eseek%2Ecom%2Eau%2F...
What do I have to do to get this patch accepted ?
Write a regression test that shows the problem, marked with todo_wine. --Juan