Same code, seen with Olly versions 1 (of 23.5.2004)+2 (3.12.2009):
00401049 |. 56 push esi ; |Arg1 = 00142C00
0040104A |. E8 04150000 call 00402553 ; \ForOlly1.00402553
00401049 |. 56 push esi ; |Arg1 = ASCII "\zip_temp\mdg_test.csv "
0040104A |. E8 04150000 call 00402553 ; \ForOlly2.00402553
Both executables run fine without Olly...
When launched with Olly 1, esi points to roughly the same memory area (00142C00 instead of 00142C20), but that memory is still virgin, meaning that before that something went seriously wrong.
Anybody else had that experience?
Stupid question but i have to ask, do you have any breakpoints set from a previous debug session which are now corrupt? I constantly have to remove my breakpoints and reset them because i edit the code and then upon reloading into the debugger the existing ones are pointing to neverland. Leaving these set can have some unpredictable results, otherwise i don't know why Olly would be doing that.
HR,
Ghandi
Your question is not stupid at all, but that is not the reason. I mostly use int 3 as breakpoints, and even if I delete the udd and bak files, the error persists.
I attach the executable and a file for the commandline. The point where esi should be the commandline arg is after the 10 nops.
Seems to be the same flow to me, it works fine on all cases.
With both ollies, the argument I give it is found and finally passed to ESI here :
0040103F |. 8BF0 MOV ESI,EAX
Then after the nops it gets pushed correctly as a parameter.
You mean it doesnn't work for the old Olly on your system ? Maybe some heap corruption ? Usermode debuggers use different heap options ...
P.S.: I hope I understood the problem properly. Also, the argument is disregarded, right ?
http://www.symantec.com/connect/articles/windows-anti-debug-reference
Which Olly1 version do you have? Same date, 23 May 2004?
Quote from: jj2007 on May 01, 2010, 09:32:14 AM
Which Olly1 version do you have? Same date, 23 May 2004?
Yes. I'm on win7 x64 and using stealth64 plugin with the x64 compatiibility option
So, who knows ... :bg
It is often helpful to delete the corresponding *.udd-file from Olly's directory for solving strange problems.
qWord
Quote from: qWord on May 01, 2010, 02:49:50 PM
It is often helpful to delete the corresponding *.udd-file from Olly's directory for solving strange problems.
qWord
That's true but doesn't help in this case. Further investigation showed that the two Olly versions initialise registers differently - which shouldn't be a problem, of course, but it seems that my app relies on esi being positive at ModuleEntryPoint or something like that. It's commonly called a bug :toothy
Sorry for the late reply JJ, i tested your executable with the Olly which i use on my PC, it is dated: "Saturday, May 22, 2004, 11:10:00 PM"
It behaves the same for me, where ESI is pointing to heap memory which is allocated but where the commandline is copied to this buffer the contents weren't present, instead only being the filler bytes 0xEE 0xFE.
I am curious about your explanation though. I have written a few little projects which make use of the debug API and have never read or heard of having to initialize registers for the debuggee. Can you explain this a little more please?
HR,
Ghandi
Hi Ghandi,
I have no experience with the debug API but I assume the debugging starts with a CreateProcess and DEBUG_PROCESS in the param list. The content of registers at this moment is in the hands of the programmer, so that would explain why different versions of Olly can "initialise" the registers differently. My problem was that esi should have been explicitly set by my own app, instead of letting this be done by Olly.
Now that was clearly my bug; on the other hand, in principle the debugger (Olly) should pass on exactly the same register values to the app that it would get had Windows itself used CreateProcess. Which opens up an interesting argument, i.e. if a programmer should rely on having registers set to specific values at module entry point (NO).
At no point are the registers initialized (directly) by a debugger, it is still in the hands of Windows or the developer of the debugger. If Oleh has explicitly initialized the registers using SetThreadContext, i'm sure he'd have reasons, but it would be surprising to find that he did because it is a debugger and should try to be as transparent as possible to allow viewing the application in its 'native' context. Possibly it is a bug of his own doing?
HR,
Ghandi
Quote from: Ghandi on May 28, 2010, 01:56:38 PM
At no point are the registers initialized (directly) by a debugger
The ABI says that Windows won't change esi edi ebx. At least I thought so... but it seems you are right: At module entry point, you will no longer see what you put in these regs before calling CreateProcess. You get garbage, fullstop.
Thats because the main thread has had to load libraries, such as ntdll and kernel32. Although your value was in the registers at startup, the thread still had to load modules and call the entrypoint of the program. If you want the registers to be the values you set and you don't want to debug the windows loader, you'll need to set them once the process is at entrypoint or at the first TLS callback if any are present.
HR,
Ghandi
Quote from: Ghandi on May 29, 2010, 12:18:31 AM
Thats because the main thread has had to load libraries, such as ntdll and ..
Whatever. The important conclusion is "the ABI is valid but not before the entry point". Don't pass values in esi edi ebx to a thread or new process.
I'm sorry but there seems to be confusion. You cannot pass values to a child process via registers. The only way to do so without hacking into the Windows kernel is to explicitly set the registers via SetThreadContext/ZwSetContextThread (or some derivitive) after it has reached TLS/Entrypoint or to pass a pointer through the STARTUPINFO structure used in the call to CreateProcess. Therefore the ABI does hold true and i cannot recall ever seeing anything documented about pre-entrypoint behaviour, because that is OS specific and i can't imagine Microsoft sharing that knowledge, hehe. :D
HR,
Ghandi
Quote from: Ghandi on June 09, 2010, 03:53:47 AMI'm sorry but there seems to be confusion. You cannot pass values to a child process via registers.
That's what I wrote:
Quote from: jj2007 on May 29, 2010, 06:07:33 AM
Don't pass values in esi edi ebx to a thread or new process.
But I'm not a native English writer, so that might be the reason for your confusion. Apologies.
No apologies necessary jj, i just meant that there isn't actually a way to pass values to the registers without using one of the thread context API, because processes are (supposedly) isolated from each other, self contained in their own little memory space. Its just convenient for us that API exist to be able to examine other processes information, else we'd all be in trouble when time came to debug our applications. ;)
HR,
Ghandi
Quote from: Ghandi on June 09, 2010, 03:53:47 AMor to pass a pointer through the STARTUPINFO structure
Hi Gandhi,
Can you explain what you meant here with "pass a pointer"? AFAIK there is no dedicated user-defined value in STARTUPINFO; and of course, you cannot pass pointers.
What works is misusing certain rather superfluous members:
Parent:LOCAL sinfo:STARTUPINFO
lea ebx, sinfo
invoke GetStartupInfo, ebx ; fill the structure
mov sinfo.dwXCountChars, esi ; pass some values
mov sinfo.dwYCountChars, edi ; to the child process
Child:
.data?
sinfo STARTUPINFO <?>
.code
mov ebx, offset sinfo
sub esp, sizeof STARTUPINFO
invoke GetStartupInfo, esp
mov ecx, [esp.STARTUPINFO.dwXCountChars]
mov edx, [esp.STARTUPINFO.dwYCountChars]
add esp, sizeof STARTUPINFO
Now ecx and edx contain the values that were in the parent's esi and edi.
http://www.catch22.net/tuts/undoc01
Specifically though:
Quote
Pass arbitrary data to a child process!
The last undocumented trick is quite different to the previous ones so I thought I'd save it until last. The STARTUPINFO structure contains two members, lpReserved2 and cbReserved2:
WORD cbReserved2;
LPBYTE lpReserved2;
These two members provide a mechanism for passing arbitrary amounts of data from one process to another, without having to call VirtualAllocEx / WriteProcessMemory. The cbReserved2 member is a 16bit integer and specifies the size of the buffer pointed to by lpReserved2. This means that lpReserved2 can be as big as 65535 bytes.
The example below demonstrates how to pass a buffer from one process to another. When process-B is executed by process-A, it will display a message box saying "Hello from Process A!":
Process A:
int main()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
char buf[4444] = "Hello from Process A!";
// setup the buffer to pass to child process
si.lpReserved2 = buf;
si.cbReserved2 = sizeof(buf);
// create a new process with this data
if(CreateProcess(0, szExe, 0, 0, 0, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
Process B:
int mainCRTStartup()
{
STARTUPINFO si = { sizeof(si) };
GetStartupInfo(&si);
// display what process A sent us
MessageBox(NULL, si.lpReserved2, "Process B", MB_OK);
}So far so good - we have a nice method for passing arbitrary binary data between applications, without having to use the command line. There is a problem though. In the example above process-B must be compiled with absolutely no C-runtime support. The reason is quite complicated but I will explain it now:
The Microsoft C runtime (including Visual Studio.NET) uses the lpReserved2 feature in it's implementation of the C functions: exec, system and spawn. When a new process is executed using these routines, the C-runtime has to be able to give the child process copies of it's open file handles (opened using fopen/open rather than CreateFile).
lpReserved2 is used as a mechanism to pass this file-handles information between programs compiled using MSVC. Before the exec/spawn runtime functions inevitably call CreateProcess, the lpReserved2 buffer is constructed using the following format:
DWORD count;
BYTE flags[count];
HANDLE handles[count];
// in memory these are layed out sequentially:
[ count ][ flags... ][ handles... ]The first field in the lpReserved2 buffer is a 32bit count of the number of file-handles being passed. Next comes a BYTE-array of flags, one for each file-handle. These flags represent the file attributes that were used when the file was opened using fopen/open - i.e. read-only, write-append, text-mode, binary-mode etc. Immediately following the flags array is the HANDLE-array containing each open file. This array contains the actual file handle identifiers, again there are handle_count items in the array.
Any amount of data can follow this structure, up to a maximum of 65536 bytes. The cbReserved2 member must be set to the length (in bytes) of this structure. The actual steps need to construct the lpReserved2 are as follows:
1.Any currently open "runtime" file-handles are enumerated.
2.Each underlying win32 HANDLE is marked as inheritable.
3.The number of file-handles being passed between processes is stored as the first 32bit integer in lpReserved2.
4.The attributes of each open file-handle are stored sequentially in the flags BYTE-array.
5.The file-handle integers are stored sequentially in the handles 32bit int-array.
6.Finally CreateProcess is called, with the bInheritHandles parameter set to TRUE.
It is at this stage that the C-runtime becomes important. When the child process executes, as part of it's initialization before main() is called, the I/O runtime support needs to be initialized. During this initialization the C-runtime calls GetStartupInfo and checks to see if an lpReserved2 buffer has been specified. If lpReserved2 is not NULL (i.e. it points to valid memory) then the C-runtime parses the contents of the buffer and extracts the file-handles contained within - this is so the file-handles will be available to the new process.
1.Call GetStartupInfo and check if lpReserved2 points to valid data.
2.Extract the first 32bit integer - this is the number of file-handles in the buffer.
3.Intialize the current process's I/O state with this number of open files.
4.Loop over the flags[] array in lpReserved2.
5.Loop over the handles[] array, populating the open-file table.
The problem might now be apparent to the more astute readers. Any program compiled using Microsoft's C-runtime will check it's lpReserved2 when it first starts - it should come as no surprise that this probably represents 90% of the C/C++ Windows programs in existence. The lpReserved2 member may also be used by other compiler vendors for the same purpose.
Should you wish to use lpReserved2 in your own programs (using CreateProcess instead of spawn/exec) you will need to be careful because the lpReserved2 buffer must be properly constructed. Failure to do so will result in the child processes crashing, or at the very least becoming unstable - the reason being that the child process is expecting to find lpReserved2 in a particular format.
Getting around this problem is simple. Setting the first 4 bytes in lpReserved to zeros (nulls) indicates that the handle-arrays are empty, and any c-runtime startup code will simply skip this phase. Your arbitrary binary data can come immediately after this zero-marker. The code now looks like this:
Process A:
int main()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
char buf[4444];
// construct the lpReserved2 buffer
*(DWORD *)buf = 0;
lstrcpy(buf+sizeof(DWORD), "Hello from Process A!";
// setup the buffer to pass to child process
si.lpReserved2 = buf;
si.cbReserved2 = sizeof(buf);
// create a new process with this data
if(CreateProcess(0, szExe, 0, 0, 0, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
Process B:
int main()
{
STARTUPINFO si = { sizeof(si) };
GetStartupInfo(&si);
// display what process A sent us
if(si.lpReserved2 != NULL)
MessageBox(NULL, si.lpReserved2 + sizeof(DWORD), "Process B", MB_OK);
}The example above can now be compiled without having to worry about C-runtime considerations.
Note: Apparently this method does not work under 64bit Windows Vista.
Sorry for the large quote but i didn't want to cut of any of the information i meant.
HR,
Ghandi
Interesting, thanks. I chose dw?CountChars because they are safe if STARTF_USECOUNTCHARS is not set in dwFlags. Using a "reserved" member is imho a bit risky. I also would like to see a working example showing the string in the parent's address space ::)