I've got a closed-source program, compiled without debug symbols, whose behavior I would like to tweak for my own personal use. Specifically, the program provides a network service that I want, but creates a window that I do not want; I would like to be able to launch the program invisibly at startup.
Presumably, the window is created by a call to either CreateWindow or (more likely) CreateWindowEx, in which case I just need to make sure that the 'WS_VISIBLE' bit in 'dwstyle' is set to zero (which can be achieved just by setting the whole DWORD to zero). So that's the one change I want to make: change the fourth argument to CreateWindowEx to zero.
Hence my first question is: If a program is compiled without debug symbols, is it still possible to identify when it is calling which Windows API functions? (for instance, can it be done using the debugger in Visual Studio 2008?) Is there a good way to locate the call to CreateWindow or CreateWindowEx? (Later on in this post, I'll describe my attempts to figure this out.)
Before continuing, let me note that ordinarily, I wouldn't go through all this trouble. The way I would normally run a program invisibly is to just call ShellExecute from a little C++ program with the 'nShowCmd' argument set to SW_HIDE. Equivalently (and this is what I actually tried), I'd use 'shell.Run' from the Windows Scripting Host with the second (intWindowstyle) argument set to zero, like so:
// start-server.js//// Invisibly launches server"use strict";var shell = WScript.CreateObject("WScript.Shell");shell.Run("server.exe -start", 0, false);
However, programs are not required to respect the intWindowstyle argument, and what is interesting is that although this program will listen if I tell it to run minimized, or maximized, or normally, it will not listen if I tell it to run invisibly. This is what leads me to modify the program itself.
So let's dive in.
I opened up the EXE in the Visual Studio debugger, and by stepping through it found exactly where the window is created - I think. I find the call trace,
00481397 call 0048F5D7 0048F5E7 call 00490590 004905F9 call dword ptr [eax+50h] 004011A6 call 00490B1C 00490BF8 call 00493505 00493576 call 00493C9D 00493CAB call dword ptr ds:[49E43Ch] (server.exe) 7E42AF60 call dword ptr [edx] (user32.dll)
where I have represented levels of nested functions with indentation. The innermost function call (I haven't gone any deeper) creates a window without any buttons, and appears to be being made by user32.dll. The preceding function call, which then seems to be into user32.dll, is made by the program itself. Hence, assuming I've interpreted things correctly, the line that I'm looking for is,
00493CAB call dword ptr ds:[49E43Ch]
This line exists in the function starting at 00493C9D, so let me show the code from the start of this functon to the function call (plus two lines afterward):
00493C9D mov eax,dword ptr [ecx+38h] 00493CA0 test eax,eax 00493CA2 jne 00493CB3 00493CA4 push dword ptr [esp+4] 00493CA8 push dword ptr [ecx+1Ch] 00493CAB call dword ptr ds:[49E43Ch] <------------- CreateWindow(Ex)?00493CB1 jmp 00493CC1 00493CB3 mov edx,dword ptr [eax]
Now there seems to be a contradiction: Only two things are pushed on the stack prior to the call to ds:[49E43Ch], which would imply that it is a function that only takes two arguments. Both CreateWindow and CreateWindowEx take many more arguments than this!
Hence, if anyone has any insights for what might be going on here (and where I might have gone astray), I'd appreciate them too.
(Thanks!)