Академический Документы
Профессиональный Документы
Культура Документы
1
by quosego/snd
17-04-2009
Teddy suggested I put all my small tuts in one big paper, so this document holds
all the tutorials Ive made about Winlicense/Themida. Itll be updated when
needed, so make sure you got the latest version.
Some of the topics discussed in this paper may get outdated quickly other things
may only get slightly changed, if you get stuck its always best to just tinker
along, reversing is only the result of a few idiots that tinkered along.
quosego
Contents
The Winlicense tutorials v1.2.1.............................................................................. 1
Patching the Winlicense 2.0.5.0 - 2.0.7.0 Checksum..............................................3
Intro;................................................................................................................ 3
The checking;................................................................................................... 3
Analysis;........................................................................................................... 3
Patching the checksum 1.x until 2.0.5.0;.........................................................4
Patching the checksum 2.0.5.0 until 2.0.6.5;...................................................4
Patching the checksum 2.0.7.0........................................................................5
Finding the Winlicense Is_Registered dwords and patching them in Winlicense
2.0.6.5-2.0.7.0 (and lower)..................................................................................... 6
Intro;................................................................................................................ 6
Finding the dwords in 2.0.6.5;..........................................................................6
Finding the dwords in 2.0.7.0;..........................................................................7
Patching the dwords;........................................................................................ 8
Final notes;....................................................................................................... 9
The REGISTERED macro unraveled in Winlicense 2.0.6.5.....................................10
Intro;.............................................................................................................. 10
Analysis of the Cryptoblock function;.............................................................10
Analysis of the origin of the decryption dwords;............................................11
Analysis of the origin of the second decryption dword;..................................12
Analysis of the origin of the first decryption dword;.......................................12
Conclusion;.................................................................................................... 13
68 2C31AA09
^ E9 3B7BF9FF
PUSH 9AA312C
JMP 010C20F4
This'll of course leads you to nowhere since it's VM and studying it obfuud is
greatly annoying. However if you look at the stack and scroll up four dwords
you'll see the following;
(these are simply the arguments pushed into CheckSumMappedFile)
(http://msdn.microsoft.com/en-us/library/ms679281(VS.85).aspx)
0007FF6C
0007FF70
0007FF74
0007FF78
012A0000
000B41FC
0112A4D0 cisc_+_f.0112A4D0
0112A4D4 cisc_+_f.0112A4D4
the correct checksum. But that would include hooking the WL decrypting routines
or GetProcAddress or similar. There are easier methods.
WL does not directly compare the CheckSumMappedFile generated checksum
with a checksum it has stored, instead it does some computations before
comparing the checksum.
In my case it did the following computations, extracted from the Cisc Virtual
Machine.
ROL {checksum}, {last byte checksum}
;ROL 000B6D92,92
XOR B648002D,2AC8914C
ADD 9C809161,87C05B78
XOR 2440ECD9,6D10B8E2
= B648002D
= 9C809161
= 2440ECD9
= 4950543B
In this case 4950543B is considered the final calculated checksum. If you now set
a memory breakpoint on access on the BaseAddress of the mapped PE (see the
CheckSumMappedFile structure) in the memory map. You'll see the VM accessing
the very last dword in the mapped file. This will if the executable is unmodified be
the same as the calculated checksum. WL will next do a compare and fail or
proceed.
Patching the checksum 1.x until 2.0.5.0;
So if you've modified your WL protected app you must update the checksum
located in a dword at the end of the file, however as I stated all calculations of
this checksum are done within the VM and it would be tedious to extract them
every time.
However WL stores the calculated checksum in the VM registers before
comparing it with the stored one. So to find it easily you can do the following;
- When you've returned from CheckSumMappedFile API memory bp the
BaseAddress on access in the memory map. Press shift-f9. (It now has obtained
the last dword of the mapped PE.)
- Follow edi in dump and look for the first dword that appears two times, this is
the calculated checksum. In cisc VM's it should be [edi+4] and [edi+8], in risc
VM's it should be further down with some empty dwords between it.
- Copy the calculated checksum to the end of the file (search for the old one or
just scroll down). It'll now run again with its new checksum.
Patching the checksum 2.0.5.0 until 2.0.6.5;
There's not much they've updated, except it gets the CheckSumMappedFile API
through the internal kernel32.dll GetProcAddress of Winlicense. So here's another
method, though you can also bp the internal Getprocaddress and proceed with
the above tutorial the following might be easier.
- HW bp on access the last dword of the file that needs checksum updating and
run. (This is the stored checksum, as you know)
- Wait until it writes it using a rep to a memory buffer.
- Then HW bp the checksum at the memory buffer. (eg. the last dword in the
memory buffer.) Just follow EDI in dump and hw bp that dword. Run and wait until
it gets accessed by the VM.
- Follow edi in dump and look for the first dword that appears two times, this is
the calculated checksum. In cisc VM's it should be easy to spot, in risc VM's it
should be further down with some empty dwords between it.
- Copy the calculated checksum to the end of the file (search for the old one or
just scroll down). It'll now run again with its new checksum.
Patching the checksum 2.0.7.0
Theyve did some updates, not many interesting ones.. Generic idea is still the
same, however one updates makes sure the previous tuts are useless;
-Load the stored checksum prior to the calculations, this means when you hw bp
it the new checksum wont be in [EDI] anymore.
Theyve probably made some other updates but since I didnt notice them they
are of no concern.
But as usual they made a mistake so that once again an easy fix without VM
digging is possible. To update the checksum in 2.0.7.0 follow the following tut.
- bp FreeLibray, run and wait until it breaks, eax and ecx hold the old and
calculated checksum.
- Copy the calculated checksum to the end of the file (search for the old one or
just scroll down). It'll now run again with its new checksum.
Hmm kinda short tutorial.. Was hoping for more, but it seems oreans is not in
the mood.
This also works for the previous versions.
// EBP+xxxxxxxx
Here the EBP+xxxxxxxx in this line of the found code is the location of the first
is_registered dword.
{Variable_1} is a random value, which WL checks for to see if it's not registered,
however the correct value is not 2 here, but also random. If {Variable_1} equals
the stored value then WL accepts the program as unregistered.
2)
50 53 8B C0 B8 9F 04 00 00
When found scroll up until you find the following (~10 lines);
SUB ECX,5
REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
However often olly doesnt analyse the above code correctly so you can also
search for the following byte string; 83 ?? 05 F3 A4. When found, HWBP it on
execute and restart. Now scroll up and youll see another encryption/wiping loop
thats about to get wiped by the rep movs mentioned above. Now HWBP on
execute this loop (wipe the previous HWBP) and restart. Now when you again
scroll up youll once again see a function thats about to get wiped. In this
function search for the following;
CMP DWORD PTR SS:[EBP+xxxxxxxx],{Variable_2)
Here EBP+xxxxxxxx is the location of the second is_registered dword.
{Variable_2) is in this case, the value the second is_registered dword needs to be.
If the stored second is_registered dword equals {Variable_2) then WL accepts the
program as registered. (If of course the first dword is also valid.)
So now youve found the locations of the is_registered dwords in version 2.0.7.0
and the value that the second is_registered dword must be when registered.
Patching the dwords;
In previous versions the correct values of the is_registered dwords were fixed (2 &
500), however now they're randomly generated and using the information
discussed in the previous paragraph you only know the correct value of the
second dword. As for the first dword you only know the correct dword to make it
unregistered. Which pretty much helps you nothing at all.
I will now explain how you can trace the Oreans virtual machines to get the
correct first is_registered dword. As you might know there are two possible Virtual
Machines, Risc and Cisc. Since they differ in complexity and execution methods,
I've made two tuts to find the first is_registered dword. Use the correct one for
the correct VM. A simple rule is if the VM is located inside the WL section it's cisc,
else it's risc.
For version 2.0.6.5;
Winlicense accesses and compares the correct first is_registered dword with the
stored one the second time it accesses it in the VM. So first HW bp on access the
location of the first dword. Then restart and press shift-f9 until you hit the code
were it writes the {Variable_1). (See 1) in Finding the dwords.) Next press f9 and
you'll be at the first access in the VM, next press f9 and you're at the second
access. Next it'll compare the stored first is_registered dword with the correct
one. Now you'll have to choose between the cisc or risc tut.
For version 2.0.7.0;
Winlicense accesses and compares the correct first is_registered dword with the
stored one the third time it accesses it in the VM. So first HW bp on access the
location of the first dword. Then restart and press shift-f9, first time youll break
at a rep that writes zeroes and the second time you end up in the VM. Now press
shift_f9 another two times and youre near the compare to the correct value.
Comparing in cisc Virtual machine;
- Press F7 to arrive at the main lods handler, you can see {Variable_1) in the
stack.
- Put a breakpoint here and f9 until you see {Variable_1) in ecx
- eax now holds the correct is_registered dword. Store this at the first
is_registered dword location and your app will be registered. (If you also fix the
second of course.)
You'll now prolly think, well that's nice but since I'm in this screwy virtual machine
you better tell me why this actually works. Simple see the unobfuscated handler
below which compares values in the cisc VM;
POP EAX
POP ECX
CMP ECX,EAX
PUSHFD
JMP {Main_Handler)
Since your {Variable_1) was pushed first it will be in ecx, and eax must hold
whatever it is compared to. Junk VM code never writes stack values to eax or ecx
so {Variable_1) can never get to ecx unless it's intended. (The above handler is
unobfuscated, it can take extremely long if you try to trace and find this handler
yourself.)
Comparing in risc Virtual machine;
- Trace until {Variable_1) which is retrieved from its store location is located at
second dword of the stack, should be a pretty short trace 20 instructions max.
00391FF8
{Variable_1)
00391FFC
7EAC5730
00000000
- Hardware bp that stack address on access (here 391ff8), and run. When it
breaks the correct is_registered dword is now located in the dword above the old
one in the stack. Store this dword at the first is_registered dword location and
your app will be registered. (If you also fix the second of course.)
Once again you might ask yourself and why does this work?? And again once you
know the unobfuscated code all becomes clear;
MOV ECX,DWORD PTR DS:[ESI]
MOV EBX,DWORD PTR DS:[ESI-4]
PUSH DWORD PTR DS:[EDI+70]
POPFD
CMP ECX,EBX
PUSHFD
Well that's all, after you've unpacked the app just set the first and second dwords
correctly and everything will once be registered. Or use an inline to fix these
dwords after the app has been unpacked in memory.
Final notes;
Once again Winlicense can crypt certain blocks of code using dwords written
when a keyfile is present, these will not be decrypted and can crash. Also the
Get_name API will crash! Since there's no name you can put a ret 0c in its place
which most apps will accept or patch it to return a name.
As often the case with Themida and Winlicense uses EBP to get memory
locations.
(eg MOV DWORD PTR SS:[EBP+9D41076],ESI now equals for instance, MOV
DWORD PTR SS:
[00551849],ESI)
MOV ESI,DWORD PTR SS:[ESP+20]
macro was called
[2];
CMP DWORD PTR DS:[ESI],20204C57
JNZ [1]
Store the address that marks the end of the encrypted data.
CMP DWORD PTR SS:[EBP+9C11311],70D42925
JNZ [end]
CMP DWORD PTR SS:[EBP+9C1219D],72EF7845
JNZ [end]
JMP 0064ECE9
[...]
Check for the is_registered dwords, also a nice place to see what they are
supposed to be. Though not always available.
MOV EAX,DWORD PTR SS:[EBP+9C10F19]
MOV EBX,DWORD PTR SS:[EBP+9C10FDD]
Get the decryption dwords from the Winlicense data.
MOV ESI,DWORD PTR SS:[ESP+20]
ADD ESI,0D
[3];
CMP ESI,DWORD PTR SS:[EBP+9D41076]
JE [exit]
Load start address of the cryptoblock to decrypt, and compare to the end of the
block.
ADD BYTE PTR DS:[ESI],AL
ADD BYTE PTR DS:[ESI],BL
XOR BYTE PTR DS:[ESI],AH
XOR BYTE PTR DS:[ESI],BH
SUB BYTE PTR DS:[ESI],AL
SUB BYTE PTR DS:[ESI],BL
XOR BYTE PTR DS:[ESI],AH
XOR BYTE PTR DS:[ESI],BH
ROR EAX,4
ROR EBX,3
INC ESI
JMP [3]
Decrypt the block using the EAX and EBX dwords a simple decryption routine.
Analysis of the origin of the decryption dwords;
The origin of the decryption dwords as loaded in EBX and EAX is interesting as it
could proof to be an interesting point of attack. Are they simply random numbers
preloaded in a Winlicense protected app or are they actually retrieved from the
keyfile. The first would make the cryptoblocks easily retrievable whilst the keyfile
is not available, the second would make it harder. However I've already spoiled it
somewhat for you guys saying that they are actually retrieved from the keyfile.
I'll now discuss how I achieved this.
--First, something about the VM. It has the following simplified structure, a main
handler interprets bytes and jumps to the accompanying specific handler. This
handler executes its code and the main handler interprets the next byte.
So the main handler gets byte 31, decrypts it and sees that byte 31 means
executing handler number 4 which pushes ebx. It'll jump to the "push
ebx/number 4 handler" which will push ebx and go back to the main handler. Now
it'll get the next byte and so on.
--Analysis of the origin of the second decryption dword;
Now HW bp the location of the first decryption dword and restart, this'll get you
at least to the area of code where this decryption dword is written. Skip a few
reps that write zeroes at this location until you get to the virtual machine handler
that writes this dword.
POP DWORD PTR DS:[EDX]
JMP {Main_Handler)
handler.
Hw bp the VM register (follow EDI in dump and find the decryption value) that
contains the first decryption dword. Next time it'll be accessed it'll be for the
calculation of the second decryption dword, and it'll have pushed it onto the
stack. You can also find this by bping the main handler and see the dword
appearing in the stack. Unless you've deobfuscated your VM I don't suggest
tracing through specific handlers. There are ~30 handlers between the writing of
the first dword and the calculation of the second.
Next you can bp this dword in the stack to see where it gets accessed, (remove
the previous one) once again unless you've got no obfu don't trace since there's a
lot of junk code between normal instructions. (push pops, word loads, 4 adds.
etc.) You'll end up in a handler doing the following;
POP EAX
ADD DWORD PTR SS:[ESP],EAX
PUSHFD
JMP {Main_Handler)
[ESP] holds a fixed value loaded from VM_code and eax holds the first decryption
dword. It should be obvious that they get added to each other here.. Now check
out the value in the stack, indeed it's the second decryption dword.
So you now know the second decryption dword is derived from the first. However
that does not tell us anything about the origin of the first dword. This will be
discussed in the next chapter.
Right now I'll just tell you that the 2660000 is the memory buffer holding the
keyfile which went through a few decryptions. Hw bp this address and restart to
find out for yourself.
Now hw bp the VM_register containing the first dword of the keyfile memory
buffer. (In the above example the one with A3D9D3B7.) and press f9. When you
break it'll have pushed this value on the stack. Next you can bp this dword in the
stack to see where it gets accessed,(remove the previous one). You'll end up in a
handler doing the following;
POP EAX
XOR DWORD PTR SS:[ESP],EAX
PUSHFD
JMP {Main_Handler)
Check the stack and you'll see the first decryption dword.
We now know that the first decryption dword is derived from the first decrypted
dword of the keyfile. The first decryption dword is then used to calculate the
second decryption dword.
Conclusion;
To decrypt certain codeparts in registered Winlicense protected apps Winlicense
uses the first dword of the keyfile to calculate the decryption dwords. These
dwords then get used to decrypt the REGISTERED macro's encrypted code/data.
In pseudocode the following happens to obtain these decryption dwords.
MOV EAX, [FIRST_BYTE_KEYFILE]
[data_1], fixed_value_1
ecx, xxffc0
[ecx], fixed_value_1
eax, [ecx]
ebx, [ecx+4]
eax,ebx
[ecx],eax
ecx, fixed_value_2
[data_2],ecx
The above stores 2 dwords that the VM will later use to check stackantidump,
data_1 and data_2. Data_1 holds the variable that must be the same when
[xxffc0] and [xxffc0+4] are xorred. Data_2 holds the location xorred with
fixed_value_2, the VM will use data_2 to calculate where the stackantidump is
located.
To check stackantidump the VM now only needs to the folowing;
Final Notes;
Thank you all for reading this, hope you enjoyed it.
Greetings to all webscene teams, and the reversers visiting the RE boards and
thnx to a certain provider of executables.
You cannot stop a tide with a spoon. Cracking technology will
always be several steps ahead of DRM and content will be
redistributed on anonymous networks. Giulio Prisco, chief
executive of Metafuturing Second Life, formerly of CERN
-q