ez-cdda sleep

James Hawkins truiken at gmail.com
Wed Sep 29 11:38:32 CDT 2004


> 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
<m.hearn at signal.qinetiq.com> 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



More information about the wine-devel mailing list