An application was reported by the customer as “incompatible” with Windows 7 as it was “crashing” It was an in-house app developed by the customer to provide automated updates to their in-house applications. However the method of configuring the app was long forgotten.
I got a copy and installed it on a Windows 8.1 machine, and sure enough it seemed to crash…
This looked like a Windows Application Crash Box, but not quite. It was a customized one, and clicking click here we got a useful error message:
Interestingly even on a test machine provided for Windows XP the same “crash” occurred, as the .ini files has been delivered separately from the MSI installer.
So I then created a blank client.ini file in that location requested this resulted in a new error message:
Normally in this case I’d like to use Rohitab’s API Monitor (http://www.rohitab.com/apimonitor), however it uses API hooking methods that may make some apps unstable, and in this case using the API monitor function for INI functions caused the app to not operate properly. So we go to use WinDbg…
Ok so we know it’s looking for these 3 values, but what “AppName” was being used in the INI file? That is the section of the INI file in square brackets. To find out I opened the EXE with WinDbg, from the Windows SDK, and set a breakpoint on the Windows API used to read INI files.
bp Kernel32!GetPrivateProfileStringA [enter]
I then hit g [enter] to start executing the app. We quickly hit our breakpoint:
From MSDN we can see this function is called with the following parameters http://msdn.microsoft.com/en-us/library/windows/desktop/ms724353(v=vs.85).aspx
DWORD WINAPI GetPrivateProfileString( _In_ LPCTSTR lpAppName, _In_ LPCTSTR lpKeyName, _In_ LPCTSTR lpDefault, _Out_ LPTSTR lpReturnedString, _In_ DWORD nSize, _In_ LPCTSTR lpFileName );We also know the almost all Windows API functions use the STDCALL calling convention, Parameters are pushed from right to left and the callee cleans the stack. Function names are decorated by a leading underscore and a trailing @-sign followed by the number of bytes of parameters taken by the function. (http://blogs.msdn.com/b/oldnewthing/archive/2004/01/08/48616.aspx) In assembly it looks something like this:
DWORD __stdcall GetPrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize, LPCSTR lpFileName) public _GetPrivateProfileStringA@24_GetPrivateProfileStringA@24 proc near lpAppName= dword ptr 8 lpKeyName= dword ptr 0Ch lpDefault= dword ptr 10h lpReturnedString= dword ptr 14h nSize= dword ptr 18h lpFileName= dword ptr 1Ch
When a function is entered ESP register points to the return address pushed on the stack by the call instruction (that is, previous contents of EIP register). Any argument in stack of higher address then entry ESP is pushed by caller before the call is made; so the first argument is at offset +4 from ESP (EIP is 4 bytes wide), plus 4 more bytes once the EBP is pushed on the stack. Because our script is running before EBP is pushed on the stack, we find our first parameter at ESP+4. (More examples of the different x86 calling conventions with Microsoft C and GCC compilers is here http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Convention_Examples )
We can now build a WinDbg script to set the breakpoint on GetPrivateProfileStringA, but also output the parameters to the WinDbg Screen, and automatically continue:
bp Kernel32!GetPrivateProfileStringA "r @$t0 = poi(@esp+4);r @$t1 = poi(@esp+8);r @$t2 = poi(@esp+0xC);r @$t3 = poi (@esp+0x18);.printf \"App Name: %ma Key Name: %ma Default: %ma Filename: %ma\",@$t0,@$t1,@$t2,@$t3;.echo;g"
The break point command when expanded out looks like this:
r @$t0 = poi(@esp+4); r @$t1 = poi(@esp+8); r @$t2 = poi(@esp+0xC); r @$t3 = poi (@esp+0x18); .printf \”App Name: %ma Key Name: %ma Default: %ma Filename: %ma\”,@$t0,@$t1,@$t2,@$t3;.echo;g”
We now run WinDbg with this command we get info about what INI parameters are being looked for. I then built up the INI file with “guessed” values until the original error was gone.
We then found a new error “Server.ini” not found, we repeated process and built that. The WinDbg output with our breakpoint script then looks like this:
App Name: Settings Key Name: Token Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Settings Key Name: Title Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Settings Key Name: ExeFile Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Settings Key Name: Description Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Settings Key Name: SupportName Default: the IT Help Desk Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Settings Key Name: SupportPhone Default: 12-3456 Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Settings Key Name: SupportEMail Default: IT.HelpDesk@company.com.au Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Server1 Key Name: Path Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Server1 Key Name: Description Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Server1 Key Name: Start Default: 1/1/100 Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Server1 Key Name: End Default: 31/12/9999 Filename: C:\Program Files (x86)\Launcher\Client.Ini App Name: Server2 Key Name: Path Default: Filename: C:\Program Files (x86)\Launcher\Client.Ini
I had specified the “path” in my client.ini to go to C:\SUPPORT, and this was where server.ini was loaded from:
App Name: Settings Key Name: Token Default: Filename: C:\Support\Server.Ini App Name: Env1 Key Name: Folder Default: Filename: C:\Support\Server.Ini App Name: Env1 Key Name: Description Default: Filename: C:\Support\Server.Ini App Name: Env1 Key Name: ForceCopy Default: False Filename: C:\Support\Server.Ini App Name: Env1 Key Name: AllUsers Default: False Filename: C:\Support\Server.Ini App Name: Env1 Key Name: chentiangemalc Default: False Filename: C:\Support\Server.Ini App Name: Env1 Key Name: CompareFileSize Default: False Filename: C:\Support\Server.Ini App Name: Env2 Key Name: Folder Default: Filename: C:\Support\Server.Ini App Name: Extension Maps Key Name: Ext1 Default: Filename: C:\Support\Server.Ini
With all this we found not only the app worked fine on Windows 8.1, I had it updating a copy of “notepad” using a client.ini in the format:
[Settings] Token=NOTEPAD Title=Notepad ExeFile=notepad.exe Description=Notepad SupportName=Notepad SupportPhone=12345Support EMail=notepadsupport@support.com [Server1]Path=C:\Support
And a server.ini in C:\Support like this
[Settings]Token=NOTEPAD[Env1] Folder=Notepad Description=Crappy Apps ForceCopy=True AllUsers=True chentiangemalc=True CompareFileSize=True
Where chentiangemalc was the current logged in username. I then put notepad in C:\support\notepad\notepad.exe and it would install the EXE into C:\Program Files (x86)\Launcher
With this information we were able to scan servers for server.ini and we ended up finding a copy, pointing to this would deploy the application configured in the ini. Using the info from the .ini we were able to determine users the app was deployed to and move the app into a more modern SCCM deployment method.
Your troubleshooting skills are simply amazing. Well done, as usual.