In my previous post I mentioned that running the game under a debugger, would, after a while, force terminate the game.
I speculated either the debugger was being found by an API directly, or indirectly via a timing anti-debug.
I did some experiments. And the evidence points to a timing anti-debug. The time it takes to terminate the game is variable, and it turns out, it only happens if the performance of the game is rather bad. In this case, it was Olly 2's fault. There seems to be some kind of bug in Ollydbg 2.01 whereby all threads of a running application are suspended and resumed constantly, the game runs although with a 30-35% penalty. The timing anti-debug sees this, sees that more ticks are being expended than normal and with careful communication between two threads, it calls NtTerminateProcess by spawning several threads that point to a VM program(only of the thread has a different VM program than the rest)..
In most cases, what Olly is doing is normal behaviour, it's how it's usually done, but not in my case, I've observed olly idling and not doing this suspend/resume thing. The bug seems to disappear if I(at least in my case) I hit a memory breakpoint. Then olly is acting normal, and the game does not terminate, or at least not as fast as before, if the avg grows as time passes, because of small slowdowns, then it will terminate eventually.
I looked at my trace log of one of the obfuscated threads, lo and behold, RDTSC on address 3955DEA9(quick reminder there is no ASLR). The result of RDTSC is stored in EDX:EAX, these values are later used in a loop and are encrypted and stored in a table.
Now that I know what is what, I can better understand the underlying algorithm. One thing is certain, the mystery is solved.
Quick reminder that timing anti-debugs are in my opinion, the most difficult to handle, it isn't as easy returning 0 on GetTickCount.
Monday, February 15, 2016
Wednesday, February 10, 2016
Hunting for the mysterious anti-debug.
Well, it's probably not that mysterious. But let me point you to my last post, where I mentioned the problem briefly in the last paragraph.
I mentioned there were two threads, one is 3939EF70 and the other is 3939F9C0. Since the game utilizes no ASLR, and SecuROM expects most addresses to be the same on any system(hardcoded), there is no need to recompute them for each system.
The first thread to be started is 3939EF70, the thread is extremely obfuscated, here is but a sample of it
This is from the tracer, in reality some of these instructions are overlapped. This thread seems to, initially, just loop over, checking for a value if it is bigger or smaller than another at particular hardcoded addresses, and jumps to different piece of code, but they all ultimately end at the same place initially, GetTickCount,+120 seconds to the value returned by GetTickCount, and then Sleep(120 seconds).
It repeats the aforementioned Sleep infinitely, until the other thread 3939F9C0 signals it, by writing different values to these hardcoded addresses, thereby making that thread 3939EF70 take different branches. At some point, 3939EF70 starts another thread with CreateRemoteThread that executes the VM. Interestingly, there is no synchronization between the threads, both threads rely on the fact that the either of them will Sleep when one is modifying the same addresses.
I mentioned an anti-debug, that's right. The game runs under a debugger for as long as thread 3939F9C0 allows it to, then randomly between 5-30 minutes(rarely longer) the thread calls NtTerminateProcess.
I've been speculating this is a timing anti-debug, that there is some 'avg' value that goes up as the debugger handles various events and generally slows down the game by 20-30%. As soon as this value crosses some threshold, it calls NtTerminateProcess. This seems to be further reinforced by the fact, that if I were to start the game under a debugger, detach, the game would never terminate. If that is not the case, then I am being detected by a different method.
Oh yes, I managed to manually patch the exe to disable code verification. Now I can tamper with some of the code(except the packed code which I can only modify at runtime).
I mentioned there were two threads, one is 3939EF70 and the other is 3939F9C0. Since the game utilizes no ASLR, and SecuROM expects most addresses to be the same on any system(hardcoded), there is no need to recompute them for each system.
The first thread to be started is 3939EF70, the thread is extremely obfuscated, here is but a sample of it
lea esp,[esp-4]
mov dword ptr ss:[esp],ebp
mov ebp,esp
sub esp,4
mov dword ptr ss:[esp],32E
xor dword ptr ss:[esp],00000326
sub esp,dword ptr ss:[esp]
lea esp,[esp-4]
mov dword ptr ss:[esp],esi
mov eax,-1D9
mov eax,dword ptr ds:[eax+3C04C67D]
xor eax,0000911A
mov dword ptr ds:[3C04C4A4],eax
mov eax,-0E7
mov eax,dword ptr ds:[eax+3C04C58F]
xor eax,00009A12
mov dword ptr ds:[3C04C4A8],eax
mov eax,-55
mov eax,dword ptr ds:[eax+3C04C4F9]
add eax,dword ptr ds:[3C04C4A8]
mov dword ptr ds:[3C04C4AC],eax
call 3939EFE5
add dword ptr ss:[esp],3E
push dword ptr ss:[esp]
sub dword ptr ss:[esp],3B
push ebx
mov ebx,dword ptr ss:[esp+4]
xchg dword ptr ss:[esp],ebx
xchg dword ptr ss:[esp],ebp
mov ebp,dword ptr ss:[ebp]
sub ebp,13
xchg dword ptr ss:[esp],ebp
mov dword ptr ss:[esp+4],A60004C2
jmp short 3939F00F
retn 4
This is from the tracer, in reality some of these instructions are overlapped. This thread seems to, initially, just loop over, checking for a value if it is bigger or smaller than another at particular hardcoded addresses, and jumps to different piece of code, but they all ultimately end at the same place initially, GetTickCount,+120 seconds to the value returned by GetTickCount, and then Sleep(120 seconds).
It repeats the aforementioned Sleep infinitely, until the other thread 3939F9C0 signals it, by writing different values to these hardcoded addresses, thereby making that thread 3939EF70 take different branches. At some point, 3939EF70 starts another thread with CreateRemoteThread that executes the VM. Interestingly, there is no synchronization between the threads, both threads rely on the fact that the either of them will Sleep when one is modifying the same addresses.
I mentioned an anti-debug, that's right. The game runs under a debugger for as long as thread 3939F9C0 allows it to, then randomly between 5-30 minutes(rarely longer) the thread calls NtTerminateProcess.
I've been speculating this is a timing anti-debug, that there is some 'avg' value that goes up as the debugger handles various events and generally slows down the game by 20-30%. As soon as this value crosses some threshold, it calls NtTerminateProcess. This seems to be further reinforced by the fact, that if I were to start the game under a debugger, detach, the game would never terminate. If that is not the case, then I am being detected by a different method.
Oh yes, I managed to manually patch the exe to disable code verification. Now I can tamper with some of the code(except the packed code which I can only modify at runtime).
Labels:
analysis,
antidebug,
crysis,
denuvo,
obfuscation,
re,
reverse engineering,
securom,
threads
Sunday, February 7, 2016
SecuROM's anti-tampering verification is only one if?
I was wondering how to deal with it, turns out, I didn't have to. The verification was a loop that computed a checksum of the code, then when it did, it compared the resulting checksum to a DWORD from an array.
There were two different instances of this, I simply had to patch the conditional jumps. One was je short, the other was jne. I changed the je to a jmp and then nop'ed the jne.
With this I could modify the code as I please.
There were two different instances of this, I simply had to patch the conditional jumps. One was je short, the other was jne. I changed the je to a jmp and then nop'ed the jne.
With this I could modify the code as I please.
Saturday, February 6, 2016
3DM will stop cracking for a whole year!
In a surprise announcement, 3DM cracking group have said that they will stop cracking single-player games under the pretext that they want to see if sales of games increase.
Why did I say pretext? Remember they last said they had difficulty cracking the latest iteration of Denuvo? As of now I propose either of the following reasons:
More on this here.
Why did I say pretext? Remember they last said they had difficulty cracking the latest iteration of Denuvo? As of now I propose either of the following reasons:
- 3DM cannot crack Denuvo and want to work on it for the next year. This sounds more plausible. They might even drop out of the scene completely, only to re-emerge years later when things around Denuvo had quieted down.
- They are genuinely doing this for the stated reason.
- They have come to an understanding with Denuvo GmbH/VMProtect to cease their activities for now.
Subscribe to:
Posts (Atom)