On Wed, 29 Sep 2004 12:38:32 -0400, James Hawkins <[EMAIL PROTECTED]> wrote: > > Let's engage on a little log analysis shall we? This is something that > > just comes with practice ... > > Wow...very impressive. > > > Here's an idea. Hack the Sleep() call like this: > > > > if (delay == 64) delay = 3000; > > ok I will try this out and get back to you on what happens. > > > > > On Wed, 29 Sep 2004 16:51:23 +0100, Mike Hearn > <[EMAIL PROTECTED]> wrote: > > I think Marcus is right, this looks like copy protection. > > > > Let's engage on a little log analysis shall we? This is something that > > just comes with practice ... > > > > Just after the program starts, it does a CreateProcess: > > > > > 0023:Call kernel32.CreateProcessA(406cd2f0 "C:\\Program Files\\Easy CD-DA > > > Extractor 7\\ezcddax.exe",403810d8 > > > "ezcddax.exe",00000000,00000000,00000001,00000004,00000000,00000000,406cd2ac,406cd61c) > > > ret=00750ec9 > > > > ezcddax is the real program and what you launched is just a stub. > > > > Note the 6th parameter: it's 4, which is CREATE_SUSPENDED. So, the new > > process isn't supposed to start. > > > > [ snip lots of traces from CreateProcess ] > > > > > 0023:Ret kernel32.CreateProcessA() retval=00000001 ret=00750ec9 > > > > Here we are at the end. > > > > > 0023:Call kernel32.GetModuleHandleA(00000000) ret=00750fdb > > > 0023:Ret kernel32.GetModuleHandleA() retval=00400000 ret=00750fdb > > > 0023:Call kernel32.GetModuleHandleA(00000000) ret=00750ffe > > > 0023:Ret kernel32.GetModuleHandleA() retval=00400000 ret=00750ffe > > > 0023:Call kernel32.GetModuleHandleA(00000000) ret=00751012 > > > 0023:Ret kernel32.GetModuleHandleA() retval=00400000 ret=00751012 > > > > Interesting. GetModuleHandle(NULL) returns the HMODULE of the current > > process. An HMODULE is simply a pointer to the base of the file which is > > mapped in: in the case of an EXE it'll be the headers. > > > > So, it looks like this program is walking its own headers in memory - > > probably inspecting them for signs of tampering. More and more likely > > that this is copy protection. > > > > > 0023:Call > > > kernel32.ReadProcessMemory(00000058,00761060,0078c96c,00000002,406cbc0c) > > > ret=007556bd > > > > It then reads the memory of the newly created process, 2 bytes from > > 0x761060. I wonder what is at that address? > > > > Grep the log for it and bingo! > > > > > 0025:Starting process L"C:\\Program Files\\Easy CD-DA Extractor 7\\ezcddax.exe" > > > (entryproc=0x761060) > > > > So it's reading the first two bytes of the entry point. Checking for a > > breakpoint perhaps? > > > > > 0023:Call > > > ntdll.NtReadVirtualMemory(00000058,00761060,0078c96c,00000002,406cbc0c) > > > ret=404fab7a > > > 0023:Ret ntdll.NtReadVirtualMemory() retval=00000000 ret=404fab7a > > > 0023:Ret kernel32.ReadProcessMemory() retval=00000001 ret=007556bd > > > 0023:Call > > > kernel32.WriteProcessMemory(00000058,00761060,406cbc08,00000002,406cbc0c) > > > ret=00755723 > > > > Then it writes back 2 bytes to the same address. Maybe this is the bit > > that lets ezcddax know it was started by the launcher program and not > > directly by the user. I suspect if you suppress this WPM call, the > > program will pop up an error asking you to run the launcher app > > directly. It's probably writing a jump opcode. > > > > > 0023:Call > > > ntdll.NtWriteVirtualMemory(00000058,00761060,406cbc08,00000002,406cbc0c) > > > ret=404fabea > > > 0023:Ret ntdll.NtWriteVirtualMemory() retval=00000000 ret=404fabea > > > 0023:Ret kernel32.WriteProcessMemory() retval=00000001 ret=00755723 > > > 0023:Call kernel32.GetExitCodeProcess(00000058,0078ca58) ret=0074f235 > > > > Now it goes into a loop, attempting to get the exit code of the process. > > > > > 0023:Call > > > ntdll.NtQueryInformationProcess(00000058,00000000,406cb86c,00000018,00000000) > > > ret=404f5dfa > > > 0023:Ret ntdll.NtQueryInformationProcess() retval=00000000 ret=404f5dfa > > > 0023:Ret kernel32.GetExitCodeProcess() retval=00000001 ret=0074f235 > > > > It's trying to find out what the exit code of the process was. > > Unfortunately, we don't know what the answer is because the return code > > is a success/failure bool, not the actual exit code. You'd have to whack > > an ERR in here or something to find out. > > > > > 0023:Call kernel32.ResumeThread(0000005c) ret=007557c5 > > > 0023:Call ntdll.NtResumeThread(0000005c,406cb8a0) ret=4050e85e > > > 0023:Ret ntdll.NtResumeThread() retval=00000000 ret=4050e85e > > > 0023:Ret kernel32.ResumeThread() retval=00000001 ret=007557c5 > > > 0023:Call kernel32.Sleep(00000064) ret=007557cd > > > > Then it tries to wake it up (remember, the remote process was started > > suspended) and sleeps for a moment. > > > > > 0023:Call ntdll.NtDelayExecution(00000000,406cb888) ret=40507cff > > > trace:relay:RELAY_InitDebugLists RelayExclude = > > > L"RtlEnterCriticalSection;RtlLeaveCriticalSection;_EnterSysLevel;_LeaveSysLevel;LOCAL_Lock;LOCAL_Unlock;TlsGetValue;kernel32.GetLastError;kernel32.SetLastError" > > > 0025:Call PE DLL (proc=0x401d3bb4,module=0x401c0000 > > > L"ntdll.dll",reason=PROCESS_ATTACH,res=0x1) > > > > At this point the kernel does a context switch into the new process, and > > it begins initializing. Note that CREATE_SUSPENDED doesn't mean nothing > > runs in the new process. It still gets the ATTACH notifications (at > > least, it does in Wine ... maybe not in real windows). So there's a lot > > of stuff we can ignore here generated by the startup sequence. > > > > Let's find out what the first process is doing: > > > > > 0025:Ret ntdll.RtlAllocateHeap() retval=40393550 ret=4083acae > > > 0023:Ret ntdll.NtDelayExecution() retval=00000000 ret=40507cff > > > 0023:Ret kernel32.Sleep() retval=00000000 ret=007557cd > > > 0023:Call kernel32.SuspendThread(0000005c) ret=007557da > > > 0023:Call ntdll.NtSuspendThread(0000005c,406cb8a0) ret=4050e80e > > > 0023:Ret ntdll.NtSuspendThread() retval=00000000 ret=4050e80e > > > 0023:Ret kernel32.SuspendThread() retval=00000000 ret=007557da > > > 0023:Call kernel32.GetThreadContext(0000005c,406cb948) ret=0075580e > > > 0023:Call ntdll.NtGetContextThread(0000005c,406cb948) ret=4050e7ae > > > 0023:Ret ntdll.NtGetContextThread() retval=00000000 ret=4050e7ae > > > 0023:Ret kernel32.GetThreadContext() retval=00000001 ret=0075580e > > > > Context switch after the first line, and it awakens from its sleep. > > > > It then suspends the thread, and grabs its context. Why does it suspend? > > Reading MSDN reveals that the target thread has to be suspended for > > GetThreadContext to work. The CONTEXT structure holds the register state > > of the thread. I wonder what it's looking for in this structure? > > > > > 0023:Call kernel32.GetExitCodeProcess(00000058,0078ca58) ret=0074f235 > > > 0023:Call > > > ntdll.NtQueryInformationProcess(00000058,00000000,406cb86c,00000018,00000000) > > > ret=404f5dfa > > > 0023:Ret ntdll.NtQueryInformationProcess() retval=00000000 ret=404f5dfa > > > 0023:Ret kernel32.GetExitCodeProcess() retval=00000001 ret=0074f235 > > > 0023:Call kernel32.ResumeThread(0000005c) ret=007557c5 > > > 0023:Call ntdll.NtResumeThread(0000005c,406cb8a0) ret=4050e85e > > > 0023:Ret ntdll.NtResumeThread() retval=00000000 ret=4050e85e > > > 0023:Ret kernel32.ResumeThread() retval=00000001 ret=007557c5 > > > 0023:Call kernel32.Sleep(00000064) ret=007557cd > > > > OK, and we go back into a loop. In fact, this is an infinite loop. > > > > Probably it looks like this: > > > > while (1) > > { > > int code; > > CONTEXT86 context; > > > > GetExitCodeProcess(process, &code); > > > > if (code == ???) do something; > > > > ResumeThread(thread); > > > > Sleep(64); > > > > SuspendThread(thread); > > GetThreadContext(thread, &context); > > > > // do something with context here > > if (context.???) break ??? > > } > > > > So the question is, what condition will make it break out of the loop, > > and why isn't it getting it in Wine? > > > > It looks like it's waiting for some condition to become true in the > > remote process. This will never happen because ResumeThread here doesn't > > seem to be waking it up! We just loop over and over, resuming it, > > sleeping for a while, grabbing its context to check something which > > never changes, and starting over. > > > > So, I guess the problem is that ResumeThread isn't actually waking up > > the suspended process. Question is, why not? > > > > Here's an idea. Hack the Sleep() call like this: > > > > if (delay == 64) delay = 3000; > > > > Ie, rule out the possibility that the delay between resume and suspend > > is so short Wine can't react in time. Then continue your investigation > > from there. > > > > Good luck! > > > > thanks -mike > > > > > -- > James Hawkins >
> > Ie, rule out the possibility that the delay between resume and suspend > > is so short Wine can't react in time. Then continue your investigation > > from there. I modified Sleep to do an ERR("USING MODIFIED SLEEP!\n") and then added the check for timeout==64, set it to 3000, but it still calls sleep over and over again without any progress. I even took out the check for timeout==64 and just timeout to 3000 for the second test, but I get the same results. -- James Hawkins