Add your own alternative version
Stats
210.4K views 11.9K downloads 161 bookmarked
Posted
17 Aug 2007
Licenced
|
Comments and Discussions
|
|
So I opted to use this code, because it was easier for me to understand:
HMODULE h = LoadLibrary(dllName);
if (!h)
{
wchar_t msgBuf[MAX_PATH +1];
swprintf_s(msgBuf, L"Could not load the dll : %s", dllName);
wprintf(L"Could not load the dll : %s\n", dllName);
wprintf(L"LoadLibrary failed (%d).\n", GetLastError());
MessageBox(0, msgBuf, L"Error", MB_ICONERROR);
ExitProcess(0);
}
FARPROC p = GetProcAddress(h, "Initialize");
if (!p)
{
MessageBox(0, L"Could not load the function: Initialize", L"Error", MB_ICONERROR);
ExitProcess(0);
}
__asm call p
ExitThread(0)
VirtualProtectEx(hProc, codeCaveAddr, workspaceIndx, PAGE_EXECUTE_READWRITE, &oldProtect)
WriteProcessMemory(hProc, codeCaveAddr, workspace, workspaceIndx, &bytesRet)
VirtualProtectEx(hProc, codeCaveAddr, workspaceIndx, oldProtect, &oldProtect)
FlushInstructionCache(hProc, codeCaveAddr, workspaceIndx)
HeapFree(GetProcessHeap(), 0, workspace)
hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)((void*)codecaveExecAddr), 0, 0, NULL)
WaitForSingleObject(hThread, INFINITE)
VirtualFreeEx(hProc, codeCaveAddr, 0, MEM_RELEASE)
But the problem I encounter is that when debugging we get the messagebox saying we injected correctly, but when we call ExitThread(0) the application stops, we never call VirtualProtectEx, we never write process memory, we never flush the instruction cache, and we never create a remote thread.
Thus we never drop back to wmain, we never resume the process, and we never free the handles.
This is... not ideal, but I can't for the life of my figure out why we exit at this point - I'm currently using a stub DLL that's almost a verbatim copy of the example one provided with a few additional exports for later use.
EDIT: if I change the code to start it without being paused, comment out resumethread and call it as follows in the snippet below, with ExitThread(0); commented out since it just terminates the entire process
WaitForInputIdle(pi.hProcess, INFINITE);
inject(pi.hProcess, hooksPath, L"Initialize");
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
In this case it works, but WaitForSingleObject() returns immediately implying one of two cases:
1) the process silently crashes
2) there is a bug in waitforsingleobject (or I suppose in waitforinputidle)
However if we opt to not close the handles we're sitting there at the process using 40mib or so (normally it uses a bit over a gib) and then it eventually crashes on the injector exiting, this tells me that this injection completely breaks the game.
Thus... delayed injection? Wait for some predictable event before injecting? Any ideas?
modified 31-Jul-17 15:59pm.
|
|
|
|
|
I am new, so this might be a silly question: Does the injection work automatically as soon as the target process run? Or does another process needs to run to inject the dll into the target process? I would like to achieve the former.
|
|
|
|
|
The injection doesn't have a global effect which means you should do same job for different process even they run same execution file.
|
|
|
|
|
A month or two ago, I had this example working for Internet Explorer 9 but recently it stopped. It could be due to Visual Studio 2010 got upgraded with SP1, it could be VC++ distributable got updated etc. It could be Internet Explorer updated from 9 to 10 and rolling it back to 9 did not clean all the things up.
The point being: I know the sample in this article works!! I just can't get it working again, not even when trimming it down to the core.
The simplest possible scenario I could come up with was changing the sample application to use notepad in stead of pinball.exe (pinball is no longer on win7 computers I think).
So, I changed the lines
char* exeString = "\"C:\\Program Files\\Windows NT\\Pinball\\PINBALL.EXE\" -quick";
char* workingDir = "C:\\Program Files\\Windows NT\\Pinball";
to this:
char* exeString = "\"C:\\Windows\\notepad.exe\"";
char* workingDir = "C:\\Windows";
Finally I changed the location of the Inejctor.dll file to the correct location on my computer:
_snprintf(dllPath, MAX_PATH, "C:\\Users\\anoppa\\Desktop\\codeProjectSample\\InjectDLL.dll");
Visual Studio 2010 had a strange problem with the project file, it asked for msvcr100d.dll (notice the 'd'). It turned out that the project was compiled default with /MDd (Multi-threaded Debug DLL) and must be compiled with /MTd (Multi-threaded Debug). At least according to this blog http://www.rhyous.com/2010/09/16/avoiding-the-msvcr100-dll-or-msvcr100d-dll/[^]
When I run this, Notepad starts, but there is no messagebox reporting the injection worked. This indicates the InjectDLL was not loaded. I then tried to spell path wrong to see if the Inject() method would report an error, and it did not. The only error I can get is if I misspell the path or directory of notepad.
What could be wrong?
|
|
|
|
|
Update:
I have come closer. A clean install on a computer enabled me to get the sample running again. I then upgraded to IE10 and it injects fine on Firefox & IE10. Strangely enough it does NOT work with notepad, so I guess testing the injector on notepad was a bad idea to begin with
However, new problems came up that I am sure others have worked with and found solutions for.
The example shows how to inject a DLL into another application when we know the process & thread handles. However the sample assumes that the application we inject (and its process) is created ON this inject.exe application. If I try to inject into a application that was started manually and then locate the window handle, from that get the processId & threadId, from here on we get handles to the process & thread which is required before calling Inject() and ResumeThread().
hwnd = ::FindWindowA(TEXT("IEFrame"), TEXT("Google - Windows Internet Explorer"))
DWORD dwProcessID
DWORD dwThreadID = GetWindowThreadProcessId(hwnd, &dwProcessID )
hProcess = ::OpenProcess( PROCESS_ALL_ACCESS,false, dwProcessID)
DWORD pID = ::GetProcessId(hProcess)
hThread = ::OpenThread(READ_CONTROL, false, dwThreadID)
While the ID's and handles all look correct, they are not quite good enough, since the injection now fails. It does not inject a single thing, no message box appears, no warnings - nothing.
I tested reading back the ID's from thread and process and are confused to learn that they are empty.
DWORD tid_read = ::GetThreadId(hProcess)
DWORD pid_read = ::GetProcessId(hThread)
Could it be that I need to get some higher privileges in order to get proper access to the other processes? If so how?
|
|
|
|
|
I wanted to share the results for others to enjoy. The issue turned out to be trying to inject into a 64 bit process and not knowing it....
This is the summary of the learning's and how to interpret the results from running the inject app:
- you can inject a DLL into another 32 bit application running as the same user as yourself
- if you try to inject into a 64 bit application, then the DLL is never started, the inject() method fails but no warnings or error messages is presented
- you might (not tested) be able to inject into an application created by another user on the same computer but that would need use of setting higher privileges for the injecting process (THIS). Look for examples involving 'SE_DEBUG_NAME'
- you can not on a Win7x64 intject a dll into notepad. Reason being: its a 64 bit app
- IE9 (x86 version) can be injected on a Win7x64
- IE10 (x86 version) can be somewhat injected on a Win7x64. Due to some sandboxing technique implemented in IE10, a 32 bit AND a 64 bit instance is created in parallel. You can inject your dll if you do so using the process handles retrieved when launching the browser from the inject app (this one). However if you try to inject on an already running IE10 then all I could locate from the handles was a 64 bit process and that will fail. The problem with injecting a DLL based on the process handle used during creating and not from the process located by searching for the window is that you inject a DLL before the window has a handle. This means implementing subclassing in the dll becomes impossible. Thus, as far as I have been able to determine, if subclassing is your goal, you must inject AFTER the window is created, i.e. IE10 is no longer possible to inject into.
- If the application (e.g. FF or IE) is started from the injecting process, then the window will not be created until after we call ResumeThread on the thread handle taken from the process. We can then wait for the window to appear before doing the injection. Alternatively inject right away and get your code inserted before a window is created. In the later case subclassing is not possible though.
Code sample for injecting into an already running instance of FireFox. Replace the code to locate the window handle to your liking. If using subclassing in the injected DLL, then make sure the same search method to locate the handle is made there so that you subclass the correct window inside the process:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
char* winName = "Google - Mozilla Firefox";
char* winClass = "MozillaWindowClass";
char dllPath[MAX_PATH + 1] = {0};
_snprintf(dllPath, MAX_PATH, "C:\\Users\\anoppa\\Desktop\\aMoreCompleteDLLInjectionSolution\\InjectMaxSize.dll");
while(::FindWindowA(winClass, winName) == NULL)
{
Sleep(200);
SwitchToThread();
}
HWND hwnd = ::FindWindowA(winClass, winName);
DWORD dwProcessID;
GetWindowThreadProcessId(hwnd, &dwProcessID );
HANDLE hProcess = ::OpenProcess( PROCESS_ALL_ACCESS,false, dwProcessID);
Inject(hProcess, dllPath, "Initialize");
SetWindowPos(hwnd, NULL, -50, -50, 1970, 1330, 20);
return 0;
}
|
|
|
|
|
|
|
Excellent project, very interesting and well written.
I have the following problem - I try to inject my DLL but get the "Could not load the DLL" message box. Can someone please help me out and tell me what is the next step to debug this ? How can I call GetLastError mayabe ?
Thanks a lot !
|
|
|
|
|
I change the program "Pinball.exe" to "notepad.exe" ,and the dll has been injected successful,but i can't see its window.
modified 24-Feb-12 15:04pm.
|
|
|
|
|
i trange the code to
result = CreateProcess(NULL, exeString, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, workingDir, &si, &pi);
and then comment the code
ResumeThread(pi.hThread);
and it works now.
|
|
|
|
|
Open a Process and call a function and get the return, its possible ?
Very good article. So, i'm write a system what i have to wait requests in a process and pass the request struct to other process what is responsable to process the data,
firstly i was thinking the shared memory would the best choise, but when i see this article i think about use a similar method, please give me you opinion,
best regards and sorry fo my bad english 
|
|
|
|
|
I disable the UAC ,but it's failed too~~ 
|
|
|
|
|
|
The best of the best! 
|
|
|
|
|
Dear Drew_Benton,I build the application on my PC,which has vs2010,and it works very good.
but when I copy the application to an other PC,which without vs2010,it can't works.There are a MegBox-- "Could not load the dll: test.dll";
I'm sure test.dll at the right path!
|
|
|
|
|
The article is very good, and it's the better by far than others which i had read.
I write a sample application follow your guides, but it happens a small problem, let me describe my operation to you, maybe you can help me , thanks in
advance.
So, I inject my own dll into the explorer.exe, it show a successful tips, when i exit my application in debug mode, i can't launch my
application any more unless i kill the explorer and restart it.modified on Monday, February 8, 2010 11:57 PM
|
|
|
|
|
I have resolved it, thanks all the same! 
|
|
|
|
|
Is it possible to use this method to inject more than one DLLs into a process address space. Let me know what will be the code changes if it can be done.
|
|
|
|
|
I like that your code cleans up the memory in the remote process, but you still have a resource leak: You need to close the handle to the remote thread.
Also, you should not revert the memory protections with VirtualProtect. If you do, you may wind up removing the Execute bit. This bit does note control whether you can WRITE executable code, it controls whether you can EXECUTE it. This will play nicer with DEP if you do not revert the protection after writing the code.
I would also like to suggest another change which I have made for my own use: No MessageBox call. Instead, just call ExitThread with a non-zero return result. Use GetExitCodeThread from the parent to determine success or failure.
|
|
|
|
|
Hi Drew,
Excellent article! I also really enjoyed the Codecave article. You have a gift for explaining details that even noobs (like me) can understand!
On to the questions ...
When I inject the sample dll into some apps, the "Locked and Loaded" message appears and I can see the application in the Task Manager (no window yet because the thread is being held). However, when I click "OK" the thread is released but target application exits (or crashes) and the process disappears from the Task Manager. Do you have any idea why this might be happening?
On another note, I tried the same test with Pinball since that is the example used in the Codecave article. Everything worked as expected. The dll was injected and Pinball was launched. I'm at a loss as to what is happening ...
Thanks for the great articles,
Larry
|
|
|
|
|
Thanks for your kind comments!
Unfortunately, this article only deals with a more improved DLL injection process only using CreateRemoteThread. Using CreateRemoteThread is a bit tricky and might not always work as expected due to how the API works. In fact, I am pretty sure your crashes are normal and are just a result of common problems experienced when using CreateRemoteThread.
The problem most of the time has to do with the target application running more than one thread. Sometimes, the CreateRemoteThread DLL injection method executes code in a way that the injected thread causes the host application to crash. It can be hard to put your finger on what is going wrong.
Back when I was working with these DLL injection methods, I went for changing the entry point of the application to load the DLL and then restore it to ensure that the injection would always be successful. Of course, if your target is already running, then that does you no good! I don't have much to suggest to you right now in terms of what to do for the programs that crash after the injection. You might want to try a different DLL injection method rather than this one and see if you can get it working correctly.
The last DLL injection method that I came up with that I loved but did not write an article for involved using the AppInit_Dlls registry key and a more enchanced version of the code shown in the Codecave tutorial. Basically, my injected DLL would be injected to any process that started (since that is what AppInit_Dlls does) but I added in the logic to only keep the DLL loaded in the desired targets based on a file. I've actually stepped out of this low level domain since the end of 2008 and have changed gears towards game programming again. Until I switch back again, I don't think I'll be able to write some more updated tutorials anytime soon, but hopefully I might get around to it before 2009 is over.
Anyways, you might want to take a look at this DLL Injection reference for alternative methods to try. If this method does not work, then most of the time there is not much you can do yourself to get it working for that target, only switching methods.
I hope this helps some, good luck!
|
|
|
|
|
Thanks for the additional info on CreateRemoteThread. I guess I'll need to avoid using that method in the future for multi-threaded apps!
Again, great tutorials and I hope to see more in the future (after the game programming of course )
|
|
|
|
|
Hello
This page contains a ton of interesting hooking related links:
codeplex easyhook
Elmü
|
|
|
|
|
Hi, your article is cool, It works! But is it possible to inject my dll after the target process is launched?
Thank you very much for your kind answer, 
|
|
|
|
|
|
General News Suggestion Question Bug Answer Joke Praise Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
|