Documente Academic
Documente Profesional
Documente Cultură
2
by quosego/snd
28-03-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
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
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.
// Not important
// Not important
// EBP+xxxxxxxx holds the dword
(The EBP+xxxxxxxx in the third 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)
00 00 00 00 00 00 00 00 81 BD
Finds the following code;
CMP DWORD PTR SS:[EBP+xxxxxxxx],{Variable_2)
(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.)
Put a hardware breakpoint on execute on the found addresses and restart the app. Next
when you stop on these instructions you can read the memory locations which they
compare to or write to. These are the locations were WL stores the is_registered dwords.
Finding the dwords in 2.0.7.0;
New search strings new chances.
Start your app let it run and search for the following strings in the WL sections;
1)
50 53 B8 89 04 00 00
When found scroll up until you find something similar to this;
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;
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
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]
called
[2];
CMP DWORD PTR DS:[ESI],20204C57
JNZ [1]
CMP DWORD PTR DS:[ESI+4],3
JE 0064ECB6
[1];
INC ESI
JMP [2]
SUB ESI,2
MOV DWORD PTR SS:[EBP+9D41076],ESI
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)
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.
Analysis of the origin of the first decryption dword;
Restart the app and use an hw bp on access on the first decryption dword location to once
again break in the VM were it writes this dword. However right now you're already too
late, the decryption has both been calculated and written.
But as you might have noticed the main handler loads VM_code bytes from ESI which makes
it decide which handler it needs to call, this is the actual code being executed right now in
the VM. So why not scroll back a bit here and put a bp earlier in the VM. So follow ESI in
dump, marvel at the encrypted VM_code scroll up about 13 lines. There's lots of junk in
VM_code so you can scroll up quite some bytes. (Please note that the amount of lines is
guesswork, the amount of junkcode is variable.)
Hw bp this byte on access and restart and f9 until you're in the VM. If you now follow EDI in
dump to see the VM_registers, you must check to see if the first decryption dword is no
longer in the VM registers. If it still is then you've not scrolled up enough lines. The first
VM_register ( simply [EDI] ) holds an interesting value, a memory buffer. A few lines down
in another VM_register there's the first dword located at this memory buffer.
00528645 02660000
[..]
00528651 A3D9D3B7
[EDI+4])
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]
XOR EAX, [FIXED_VALUE_85737364]
MOV [WL_DATA_1],EAX
ADD EAX, [FIXED_VALUE_05BE6546]
MOV [WL_DATA_2],EAX
[WL_DATA_1] & [WL_DATA_2] now contain the decryption dwords.
[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;
- Retrieve data_1 and data_2.
- Xor data_2 with fixed_value_2 to get the location of the stackantidump.
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