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
Extreme Paintbrawl crash and DxWnd APC injection #182
Comments
I got this version https://www.myabandonware.com/game/extreme-paintbrawl-cl3 My Win32 part looks the same (at least with your DxWnd profile), when I go over the various elements of the picture with the mouse, I also get the correct descriptive text, but when I click on one of these items, nothing happens. |
https://www.old-games.ru/game/download/5644.html |
Ok, I managed to find a point where I can click to "Practice" on this Overview map, it's a tiny spot but I found it. This is upon handling the Window-Procedure request WM_COMMAND for pushing the "Play" button: |
Even tough this seems to be a bug in Extreme Paintbrawl launcher, it was enough for me to edit go.bat and lat it launch via dosbox:
|
Forgot that I circumvented dxwnd when testing this, so there also is a bug in dxwnd preventing you to run GO.BAT: dll\createproc.cpp:
When launching GO.BAT, this is a .BAT file, thus it launches So in order to run it properly, you need 3 things:
I can take a look at 3), if necessary, but first dxwnd author needs to fix his bug. |
I changed above line
to:
and recompiled dxwnd.dll (which was a real pain). So:
Hope that helps |
A weird situation here. While testing this game I had PCem on. The strange thing was DOSBox ran the game some times, but when it ran, PCem Win98 received a BSOD. It is like if there's no BSOD on PCem Win98, the mission doesn't launch on DOSBox |
Hi, thank for your help. |
So you can either return a different return value for non-PE executables (your PE-header parser will notice it) then and let these applications resume instead of killing it, then it should work with your injector too (as the checked PE-header is not used anywhere, I guess the intention of it is to just check if it's a PE file). You can also use APC injection, as NtTestAlert() should fire just in time.
I tried different combinations before writing (and trying to compile the source myself), because I thought, maybe this is enough, but I didn't get the nworking, most of them just crashed the original .exe. |
Thank you for the reply. It's quite a lot of stuff to make my brains boiling, so I'm replying now just to some easy questions, though I assure you that I'll consider all your suggestions very carefully. You said that your compilations crashed the original exe. Did you mean the DxWnd.exe GUI? That sounds reasonable: DxWnd.exe and dxwnd.dll use shared memory sections with C structures inside, so it is necessary to make sure they both compile the struct objects with the same spacing. Probably some #pragma pack directive here and there could fix the problem, as well as using the same compiler. I will surely read and take advantage of your injector32.c source. I was happy enough with mine, but I must admit that in some cases the process shows some flaw, like failures on first attempt or unreliable results. Usually with a little patience and some retry the problems are bypassed, but I agree there could be something that doesn't work properly in there. Well, that's even too much for now. I hope to hear you again with more intriguing questions. And congrats for your wonderful work! |
Hehe, I still prefer Visual C 6, that's my IDE, it has a small memory footprint and links everything to msvcrt.dll, so no additional runtime includes, so I fully understand your Compiler choice. I was just in doubt that it would compile with VS 2008, because of these 2 strange header files I mentioned. They include i.e. an inexistant _mingw.h, so I thought that this also cannot work with VS 2008, i.e. because of conflicts with Windows original headers.
Oh, no, that's a misunderstanding. Before I even tried to recompile the source, I first checked if I cannot fix the problem with the .bat file by choosing different hooking methods in the "advanced" GUI, but a lot of combinations mostly crashed the target executable. That's why I gave up on that and finally patched dxwnd. Maybe I was missing a working combination. You may want to try it out yourself with the "RIP" version from oldgames.ru, it is only a few MB in size.
Some of the methods in injector32.c are a bit unreliable too depending on the target, but as you have this nice selection dialog, the user can try out different methods. As I went through and fixed some common problems when writing them, you may profit from it.
Thank you :) |
Forget about NTVDMx64 for that game, performance is inferior, so it is completely unusable.
Which version of DOSbox? With latest DOSBox 0.74-3 directly from the homepage, I don't have any problems so far. |
I tried your configuration as well and it doesn't seem to help. It's still a infinite rotating "Extreme Paintbrwal" logo and I no longer to achieve what I describe here. Can you describe what kind of problem I am facing with launching with NTVDM because as I said it can't execute the file (according to cmd output). |
Check DebugView output, ensure that 32bit loader is injected properly. No problems with starting it in NTVDMx64 via GO.BAT here. You are running it on Win11 as host OS? |
Yes I am using Windows 11. And here's the log: |
Looks like NTVDM is starting up normally as it should:
So you don't see any window with the game in it? |
No as i double click on game exe it starts and crashes |
Your game.exe is 993.885 bytes dated 02.11.1998 ? As your game.exe also crashes in DosBox and other emulators, I suspect that you may have a damaged version? |
Size: 970 KB (9,93,855 bytes)
The same game, from same media installs and run on Windows 98SE |
I tried that RIP from oldgames.ru which indeed does not work, but only leaves you with a black screen. But for god's sake, don't run this with NTVDMx64, it's not designed to run any games that use graphics. Better check with DosBox authors why your version crashes within DosBox. |
The ISO from myabandonware and oldgames ru are identical.
Since DOSBox X didn't help then I came here and while your DOSBox worked, I gave it a go and it didn't work here. Looks like there isn't anything else to try, is it? The most frustrating thing is it doesn't work on Windows 7/10 VM either. |
Did you try DOS32A DOS etender as a DOS4GW replacement? |
There's a lot of complications. Even with this the game doesn't work on Windows XP. BUT... Your NTVDM started working, only after I removed (x86) from the path. It seems NTVDM doesn't like special characters on my PC. Can you check this? Regarding DOSBox IDK what's wrong, it's something else I'll have to figure out! |
I can run game.exe with NTVDMx64 from "c:\Program Files (x86)\HeadGames\PaintBrawl\engine" without any issues. |
I now installed it on "D:\adfhuiahednf\dsafhjmnok e8qu9rhna[\aaaaaaaaaaaaaaaaaaaaaaaaaaaaa(x86)\PaintBrawl" and it runs without issues. Program Files (x86) and Program Files are definitely the culprit! I can't understand why. |
Maybe it has to do with your file system, I don't know. Not reproducable here. You can enable YODA, start vdmdebug, set registry key so that NTVDM breaks on startup into YODA, start the applicationn, attach a debugger (x32dbg) to ntvdm.exe, grab ntvdm.pdb symbols, continue execution in yoda and step through ntvdm.exe to see where it quits. |
Some quick and short comments ...
If you are curious, you can read more at https://sourceforge.net/p/dxwnd/discussion/general/thread/678da4be84/#983a update on issue 1) - it was necessary to uncomment these lines |
Without the code_seg declaration, it could happen that it copies some crap to the taget intead of the real code.
|
Regarding the proposal to free the memory block, I fear this isn't possible, because it could be that the thread's activation context lies within the allocated memory block and would become an invalid pointer once you free the allocated memory block (which is a challenge on its own). |
changing the shellcode array doesn't seem to give any benefit (even a task that was working before is now failing). I was wondering if it was possible to change the segment attributes on the fly after it was allocated, making a programmatic "code_seg" equivalent. Anyway, all these are details, the original schema works better than expected! |
You mean Aha, interesting, do you have any simple test cases where the code above with direct loadlibrary call fails? I'm just curious. |
Sure, as a matter of fact I got a single case where the new procedure works (Soccer Manager '97) so I think you can run any program (or video game) with no risk to miss the trouble.
But I'd like also to ask your opinion about another "thick" problem. Some games are obfuscated, that is they show a stripped PE table with minimal entries that, once run, unzip themselves and populate the actual PE table before running the code. In this case, hooking by PE editing fails and I have to use what I call a "hot patching" method by patching a jump/call in the target function' prologue (using MinHook). The drawback is that this way I have to blindly patch every call without knowing if it was referenced or not. Have you ever had such a problem? Is there some way to tell when the code deflating is finished to defer the PE analysis until when the scene is complete? |
I don't really understand the use of VirtualProtect in your modified code:
So I default to PAGE_READWRITE on Windows XP and below, because there is no ActivationContext to fix there, so we don't need the shellcode and can simply inject LoadLibraryW as APC
On Vista and above, we need the shellcode, so the page doesn't just contain the DLL name (where READWRITE would be enough), but also the shellcode, therefore the page permission is changes to EXECUTE_READWRITE. Now the page gets allocated with the given permissions:
And now, I don't understand this. The page is already allocated with the necessary permissions, why do you change them afterwards (most likely to the same permissions the page already has)? I don't get it...
Can you explain the reason why you do this? |
Hm, I tried the copy of your code, added a starter to it and ran it as a standalone starter for various applications (i.e.: test.exe c:\windows\system32\calc.exe and dxwnd.dll from same directory was always inected on my Windows 7 machine: test.zip So providing an example (preferably something that doesn't need to get installed) would still be interesting. |
Regarding OEP detection of packed/crypted eexecutables, I guess that inline hooking (that you described as "hotpatching") isn't a bad idea, as various packers have a different workflow, so it's not really clear on how to detect when OEP is reached. There are various OEP finders out there that can be of help, but imho it's too specific for the used exe packer / crypter. |
About my improper use of VirtualProtectEx, my bad, it was a wrong attempt to make a programmatic "code_seg" directive, though I must admit that it's not clear to me what is the effect of that __declspec mode and how would it be possible to implement it by code. Please, forget it. About OEP detection of packed/crypted executables, I feared that and you just confirm my suspects. |
The code_seg directive is just because, at least in debug builds, it sometimes copies bullshit when specifying the address of the function to copy, so keeping it in a seperate segment of the executable remedys this problem. |
Oh, my! All of the sudden your code started to work .... after this small fix:
I think the reason is clear: your compiler interprets the statements right to left, while my vs2008 probably does it left to right. This way dwReserve is assigned to dwLen before than dwLen is incremented and doubled, so the valued space is not enough to hold everything. Separating the statements in two lines of code makes it unambiguous. Now it is possible to compare the features of the two APC methods. This last method was proposed as a method able to hook at an earlier stage which makes it quite interesting. But it seems that it doesn't work with all games, there are many of them that don't get run as if the procedure could crash the game in certain circumstances. I am planning to modify the Dxwnd GUI so that it will be easier to add or remove hook procedures, something like the current renderer selection. |
Ah, interesting! I didn't check K&R about that, but I guess, behaviour is indeed undefined in this case, so compiler implementation dependent. Good to know that such constructs should be avoided then. For testing on why games crash on startup, you could use the launcher code, remove "ResumeThread", attach x32dbg to the target game, and then resume thread from the debugger to check why it crashes there (or what bad things it does). |
Another bit of the mystery is revealed: |
Honestly, I consider all this Antivirus-Crap just snakeoil that doesn't have any real use. It slows down the machine with all its realtime scans, it gives loads of false positives (my personal best was a simple C program that I wrote to enumerate network shares and reconnect them, as Windows often suffers from a problem that network drives are not reconnected on startup, only on first use, so it was just a useful simple utility - 50% (!!) of all AV-scanners on virustotal.com flagged it as malware - unbelievable!), it wrongly flags cracks and keygens as malware with partly obscure names (Kaspersky doesn't do that kind of crap, but i.e. Avira is notorious for making intentionally wrong signatures), partly has buggy filter drivers that cause system hangs, etc. |
If you are curious, here https://sourceforge.net/p/dxwnd/discussion/general/thread/678da4be84/#a262 I uploaded a DxWnd upgrade (it should work also with these two files alone) with the new inject modes that, with little fantasy, I called "Inject ACP" and "Inject ACP2". |
Some better (and less bugged) files here: https://sourceforge.net/p/dxwnd/discussion/general/thread/678da4be84/?page=1#7fcb I got a few doubts and I humbly ask your opinion: |
Ah, good analysis, so I'd say you are right with your conclusion that at the time the code runs, there is no kernel32.dll yet. So maybe the APC fires in the loader at a position where DLLs are not bound yet (call stack would be useful to see that). Another method that could work pretty reliably is registering yourself as a debugger and launch the process with debug flags, then you get called on application entry point reliably. I didn't use this method for ldntvdm, because it would possibly prevent a real debugger from working, but for your usecase, it may be a good method, because you only inject into 1 target. Should I try to write some demo code for that method? |
I wrote something that resembles what you proposed, a debugger skeleton just to inject the dxwnd.dll code. It works, but it is a little less reliable that the other methods, maybe because I didn't handle all the many events that could happen during a debug session. If you want, you may start from this file belonging to DxWnd project already, maybe you can see some flaw here ... |
Ah, I see, I must have overlooked it that this is already implemented. You put an infinite loop at EIP and then check when target has reached it. Just out of couriosity: Is there an advantage over placing an INT 3 there? With other methods, INT 3 would have undesirable effects, but as a debugger, you would get notified about it, right? |
Well, now on Win7 it seems pretty good in this release, but after a few attempts with a dozen different games I found a problem in "Premier Manager 97". The game gets hooked, but some desktop resolution setting operation escapes my hooks and the video mode changes, while this doesn't happen with the ISP injection, nor with your ACP method. As you can see there's a lot of events to process. The dxwnd.dll should happen (unlogged) after the process creation, you may notice a second load of dxwnd.dll later at [00000:718], but this should be the redundant window hook that gets executed when it's too late. Update: I repeated the tests disabling the redundant window hook and - surprise - the debug hook is practically useless, it seems to always fail doing its job. The only difference with ACP2 is that it doesn't crash the program, so you don't notice. Ach! |
Uhm... I repeated the test with window hook disabled and the load dll message about dxwnd.dll disappeared. But there should be one right after the process creation, I wonder why it's not there. It seems that the target program never loaded this lib! |
I got it! The problem was in the Inject function within dxwnd.dll where I freed (with VirtualFreeEx) the allocated buffer where the dxwnd.dll path was copied. The operation was deferred by 500mSec that is enough or not depending on the case, so I decided to swipe that call away and everything works much better.
In the new log yu can see that now dxwnd.dll is effectively loaded, but the method can't force the loading sequence, so it is possible that one of the previously loaded dlls has a DllMain procedure that sets a video mode and the toy is broken. |
If you are still interested in injection problems and difficult cases, I just found one with "Jane's Fleet Command" (discussion also here: https://sourceforge.net/p/dxwnd/discussion/general/thread/678da4be84/?page=1#c0e4 ). |
Another curiosity about ACP injection: in my Win11 the difficulties to make an early hook seem increased, also turning the AV off I got games that refused the hook. Then, by chance, I put a MessageBox in the middle of the ACP injection steps and it worked. Maybe the MessageBox provided the necessary suspended state that is necessary for the injected thread to be scheduled. Since the MessageBox call is based on a timed-out inner routine MessageBoxTimeout it could be possible to increase the effectiveness of ACP injection by simply adding a timed-out message like "Wait 1 second ..." and then going on. update: tested & working. This is the code added just before the ResumeThread call in the ACP injection routine. I set a 500 mSec timeout, but I saw it working also with smaller delays. update 2: after writing this code I felt a little stupid and thought: "if all we need is a small delay, why don't do that silently with a Sleep(500) call?". Well, as a matter o fact there is a good reason: the Sleep doesn't fix the problem. Probably it is some obscure reason about context switching or entering kernel mode, anyway MessageBoxTimeoutA worked and Sleep didn't! |
Hmm, SleepEx just does a NtDelayExecution and indeed would trigger a Task switch. Maybe it's GetMessage inside MsgBox that helps? Just as a stupid idea: Did you try with PeekMessage / GetMessage ? |
I tried right now with this code replacing the timed message-box:
The result is puzzling: depending on who knows what, the trick works or doesn't work. The feeling is that it works better at first game start, but as soon as you feel you got a schema, next time the behavior is completely different. The thing doesn't change if I turn the AV off or I increase the 500 mSec timeout. |
Hi @leecher1337 it's been some time and I didn't want to interrupt this discussion of ACP. I tried different applications in this time and it seems no DOS application work in Program Files folder in any partition. I want to follow your instructions but I didn't manage to figure out how to do them. Can you please specify better? |
First of all, you can check for potential file access errors using Sysinternals ProcessMonitor and setting process to ntvdm.exe, maybe its results lead you to some wrong path access or something like that. Otherwise, to debug NTVDM:
|
This game is hilarious, from a gameplay POV. On the technical side of things it is a mixture of Windows and DOS. On Windows 11 it crashes too just like XP. Can you look into getting this game run? On XP this appears:
I am not sure if that's the situation with NTVDM too, but it'll be cool if you can the game to work. Since the game scaled to my entire desktop resolution, I actually had to use DxWnd to run the game. I posted my .dxw profile too
pblaunch.zip (You may want too add the 640x400 resolution to your driver, or use the Run in Window setting of DxWnd)
The text was updated successfully, but these errors were encountered: