On Mon, 17 Dec 2001 14:49:35 +1000 Mike McCormack mike_mccormack@start.com.au wrote:
Did you try running wine with -winver win98?
Yes, Shrinker tries to load a VXD which is not allowed under Wine. At first I was going to implement a special Shrinker VXD replacement for Wine, but then I found that it wouldn't try to load the VXD under --winver nt40. Thus, I figured I would try to resolve the problems with running under nt40, since that would (at least theoretically) improve the existing functionality of Wine, rather than add new functionality.
Did you try finding an Un-Shrinker? A quick
search using google turned
up a few candidates. Haven't tried any out though.
Yes, I've looked at them. They all seem to be h4x0r toolz with no source code.
Some new news on the Shrinker front. I've managed to determine, for the most part, what Shrinker's exception filter function does. It's a minefield! Recall that earlier I said it was looking for a magic signature in ntdll.dll. I found all of the landmines.
The first landmine makes sure the exception handler was called with the code "call ecx". If not, the process dies.
Next, we read 8 bytes from the process around that location. If the read fails, the process dies.
If the code starting with the call is not FF D1 64 8B 25 or FF D1 64 8B 0D, the process dies.
It then tries to allocate some memory, and copies some of the process code into that memory. If the allocation fails, the process dies.
With that in mind, it seems that the only thing it's really looking for is that signature. So one idea that I had is a "Shrinker fixer". When Wine loads an executable, it would look for this specific exception handling code, and then NOP out the checks.
Is that a reasonable approach?
--Rob
"ek sqrl" autophile@starband.net writes:
With that in mind, it seems that the only thing it's really looking for is that signature. So one idea that I had is a "Shrinker fixer". When Wine loads an executable, it would look for this specific exception handling code, and then NOP out the checks.
Is that a reasonable approach?
No; first I don't really see how you would do it reliably, and even then it could be considered copy protection circumvention thanks to the lovely DMCA.
But I see no reason we cannot have the code that calls the exception handler be identical to the NT code, since it seems to be checking only a couple of instructions.
On 17 Dec 2001 11:17:22 -0800 Alexandre Julliard julliard@winehq.com wrote:
With that in mind, it seems that the only thing it's really looking for is that signature. So one idea that I had is a "Shrinker fixer". When Wine loads an executable, it would look for this specific exception handling code, and then NOP out the checks.
Is that a reasonable approach?
No; first I don't really see how you would do it reliably, and even then it could be considered copy protection circumvention thanks to the lovely DMCA.
Well, reliably because you just scan the code section of the executable for the sequence of bytes representing that procedure, since it will always be the same and always be in the code section.
I think I might be willing to risk going to jail to implement this; I think the risk is low. But that is off-topic.
But I see no reason we cannot have the code that calls the exception handler be identical to the NT code, since it seems to be checking only a couple of instructions.
That is a nifty idea. Unfortunately it obfuscates the code a little bit. Instead of calling the handler, we'd have to code it in assembly with "call ecx" at the end. Shrinker looks for the next instruction after that to be something like mov eax,fs:[00000000], so we'd have to do that as well.
--Rob
Robert Baruch autophile@starband.net writes:
Well, reliably because you just scan the code section of the executable for the sequence of bytes representing that procedure, since it will always be the same and always be in the code section.
But then it's tied to a very specific Shrinker version, and will need changing basically every time the Shrinker guys touch something. Or you make it less specific and risk triggering it on other apps that have similar code sequences.
That is a nifty idea. Unfortunately it obfuscates the code a little bit. Instead of calling the handler, we'd have to code it in assembly with "call ecx" at the end. Shrinker looks for the next instruction after that to be something like mov eax,fs:[00000000], so we'd have to do that as well.
Yes, it's a bit ugly, but the exception code is already quite obfuscated as it is ;-)
Well, reliably because you just scan the code section of the executable for the sequence of bytes representing that procedure, since it will always be the same and always be in the code section.
But then it's tied to a very specific Shrinker version, and will need changing basically every time the Shrinker guys touch something. Or you make it less specific and risk triggering it on other apps that have similar code sequences.
That's true.
That is a nifty idea. Unfortunately it obfuscates the code a little bit. Instead of calling the handler, we'd have to code it in assembly with "call ecx" at the end. Shrinker looks for the next instruction after that to be something like mov eax,fs:[00000000], so we'd have to do that as well.
Yes, it's a bit ugly, but the exception code is already quite obfuscated as it is ;-)
Hmm. Well, it looks for one of these two code sequences:
call ecx mov esp, fs:[00000000]
-or-
call ecx mov ecx, fs:[00000000]
If it finds the mov ecx code then it unprotects the page of memory where the access failed and sets a pointer to somewhere behind the call ecx code, and then compares the contents to another signature. So the safest code sequence would be the mov esp code, but that mucks with esp :(
One clue is that the signature is inside the executable. Scanning through the executable, there's a call ecx/mov ecx, fs:[00000000] sequence in the executable itself. My guess is that after finding the mov esp code, it installs an exception handler which uses the mov ecx code from then on. Additional evidence is that when it sees the mov esp code, it allocates some memory and copies that section of the executable into the memory.
So the real question is, can we use
call ecx mov esp, fs:[00000000]
as the handler caller?
--Rob
Robert Baruch autophile@starband.net writes:
So the real question is, can we use
call ecx mov esp, fs:[00000000]
as the handler caller?
What you probably want to do is rewrite EXC_CallHandler in assembler, making sure you use the right instructions. This would also address the issue with %ebp where we currently depend on the way gcc compiles the code, which is not ideal.
What you probably want to do is rewrite EXC_CallHandler in assembler, making sure you use the right instructions. This would also address the issue with %ebp where we currently depend on the way gcc compiles the code, which is not ideal.
Excellent idea!
Something like this should work:
// prologue
push ebp push ecx mov ebp, esp mov ecx, handler
// create a new frame on the stack
push ??? // stack low push ??? // stack top push fs:[00000000] // previous frame pointer
// set the new frame
mov fs:[00000000], esp
// call the handler
push dispatcherpush context push frame push record call ecx // exact instruction required by Shrinker
// eax now contains the return code. // restore the stack
mov esp, fs:[00000000] // exact instruction required by Shrinker
// pop off the previous frame to be current.
pop fs:[00000000]
// epilogue
mov esp, ebp pop ecx pop ebp ret
What I'm not certain about is what to put in as the top of the stack and the minimum stack. I also don't know what the purpose of nested_handler is in the arguments, and how it works.
--Rob
Oops, I misunderstood the purpose of the new frame.
Here's a piece of an article, http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/exception/excep..., figure 14 (KiUserExceptionDispatcher Pseudocode):
int ExecuteHandler( PEXCEPTION_RECORD pExcptRec PEXCEPTION_REGISTRATION pExcptReg CONTEXT * pContext PVOID pDispatcherContext, FARPROC handler ) // Really a ptr to an _except_handler() { // Set up an EXCEPTION_REGISTRATION, where EDX points to the // appropriate handler code shown below PUSH EDX PUSH FS:[0] MOV FS:[0],ESP
// Invoke the exception callback function EAX = handler( pExcptRec, pExcptReg, pContext, pDispatcherContext );
// Remove the minimal EXCEPTION_REGISTRATION frame MOV ESP,DWORD PTR FS:[00000000] POP DWORD PTR FS:[00000000]
return EAX; }
So based on that, I think this is the final code:
// prologue: save esp, ebp and ecx.
push ebp push ecx mov ebp, esp mov ecx, [ebp+1C] // handler
// create a new exception record on the stack // to catch exceptions in the exception handler.
push [ebp+20] // nested_handler push fs:[00000000] // previous frame pointer
// set the new frame to point at that record
mov fs:[00000000], esp
// call the handler
push [ebp+18] // dispatcher push [ebp+14] // context push [ebp+10] // frame push [ebp+0C] // record call ecx // exact instruction required by Shrinker
// eax now contains the return code from the exception handler. // Restore the stack.
mov esp, fs:[00000000] // exact instruction required by Shrinker
// pop off the previous frame to be the current frame.
pop fs:[00000000]
// epilogue: restore esp, ebp and ecx
mov esp, ebp pop ecx pop ebp ret
Sorry for the Intel-speak rather than the GNU-speak, but I was brought up on x86 assembly in DOS :P
That should do it. I'm going to code this up to see if it works and doesn't kill my system! But first I need to brush up on GNU-assembly :)
--Rob
"Robert" == Robert Baruch autophile@starband.net writes:
Robert> Oops, I misunderstood the purpose of the new frame. Here's a Robert> piece of an article, Robert> http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/exception/excep..., Robert> figure 14 (KiUserExceptionDispatcher Pseudocode):
B.t.w. can this article perhaps help us to bring our builtin msvcrt exception handling code to a better shape? At present with --dll msvcrt=b and an exception we reach an infinite loop... No chance to enter winedbg.
Bye
"Robert" == Robert Baruch autophile@starband.net writes:
Robert> Oops, I misunderstood the purpose of the new frame. Here's a Robert> piece of an article, Robert> http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/exception/excep..., Robert> figure 14 (KiUserExceptionDispatcher Pseudocode):
showsehframes.exe from the examples there foinds an endless loop even with native msvcrt:
wine showsehframes.exe --dll msvcrt=n
Could not stat /dosa (Not a directory), ignoring drive A: _except_handler3 is at address: 004012A8
Frame: 40616E1C Handler: 004012A8 Prev: 40616E4C Scopetable: 00404000 scopetable[0] PrevTryLevel: FFFFFFFF filter: 00401180 __except: 00401188 scopetable[1] PrevTryLevel: 00000000 filter: 00401162 __except: 0040116A scopetable[2] PrevTryLevel: 00000001 filter: 00401144 __except: 0040114C
Frame: 40616E4C Handler: 004012A8 Prev: 40616E8C Scopetable: 00404028 scopetable[0] PrevTryLevel: FFFFFFFF filter: 004011E8 __except: 004011F0 scopetable[1] PrevTryLevel: FFFFFFFF filter: 00401219 __except: 00401224
Frame: 40616E8C Handler: 004012A8 Prev: 40616F40 Scopetable: 00404040 scopetable[0] PrevTryLevel: FFFFFFFF filter: 0040153F __except: 0040155A
Frame: 40616F40 Handler: 4006A480 Prev: FFFFFFFF Scopetable: 400DD0E0
scopetable[0] PrevTryLevel: 57E58955 filter: 00E85356 __except: 5B000000
scopetable[1] PrevTryLevel: 6339C381 filter: 758B0002 __except: 8BFF3108 ...
Mat Pietreks example shows an PrevTryLevel of FFFFFFFF in the line marked with >>> and end the loop there.
Bye