Log in

View Full Version : HexaLock


GoldStar611
December 13th, 2010, 02:08
Hello all, I've been mostly a lurker for some number of years, studying assembly at my own slow pace, but I enjoy teasing my brain from time to time. One such protection that has always held some curiosity is called HexaLock. It's not too common but among document protection.
I wrote a bypass tool which fooled HexaLock into thinking that an authorized program (acrord32.exe) was opening an encrypted file and it would do all the hard work for me. A simple call to an unhooked WriteFile easily saves the plaintext.
But that wasn't enough for me, learning is the best part I beleive. So I've taken a stab at the decryption process using ollydbg and believe to have found a weakness on what I'll call the inner layer.

AL set with "initial condition"
ESI set with pointer to decryption block
EDI set with pointer to encrypted file contents
EBX set to 0
Loop:
MOV CL, BYTE [ESI] ;take byte from decryption block
XOR CL, AL
XOR BYTE [EDI], CL
INC ESI, EDI, EBX
is EBX > 2047?
if No goto loop
if yes do some other stuff

The proposed weakness: Given known plaintext since all valid pdf files start with %PDF and given decryption block, and given initial condition byte (or maybe given ALL possible conditions) it is possible to find the 2048 byte block of memory to decrypt the file given the file is "random enough" This won't however be much help to the outer blocks as they have also initial conditions which do not have much in common with the rest of the loop. This is what I'm having problems with how to go about solving.

niaren
December 14th, 2010, 15:57
I'm not sure exactly what you are trying to do but if you are lucky then the Krypto ANALyzer plugin in PEiD might give you some info....

GoldStar611
December 14th, 2010, 17:43
Thanks for that, I didn't even think about using that plugin. Sadly the only thing it returns is CRC32 so I have a feeling that this sort of encryption is something they came up with.

Here is my analysis of the decryption function. It would seem that this function only works up to theoretical 8GB file, but that works since a CD only holds several hundred megabytes anyway. Oh and the "initial condition" referred to by parameter [EBP+8] is calculated only via file name and file path. That is a pretty simple function actually not work mentioning at the moment.

Code:

...
10004850 |. 60 PUSHAD
10004851 |. 8B7D 10 MOV EDI,DWORD PTR SS:[EBP+10] ; pointer to encrypted file contents
10004854 |. 8B5D 0C MOV EBX,DWORD PTR SS:[EBP+C] ; always zero?? maybe has to do with file length
10004857 |. 8B75 18 MOV ESI,DWORD PTR SS:[EBP+18] ; pointer to base of encryption block
1000485A |. 8BD3 MOV EDX,EBX ; move that zero paramter to ebx (used later)
1000485C |. C1EA 16 SHR EDX,16
1000485F |. 81E2 FF030000 AND EDX,3FF
10004865 |. 33C9 XOR ECX,ECX
10004867 |. 66:8B4D 08 MOV CX,WORD PTR SS:[EBP+8] ; "initial condition" only WORD length
1000486B |. C1E9 08 SHR ECX,8 ; divide by 256
1000486E |. 83E1 0F AND ECX,0F ; mask off high nibble
10004871 |. C1E1 0B SHL ECX,0B ; multiply by 2048
10004874 |. 03D1 ADD EDX,ECX ; EDX usually zero
10004876 |. 03D6 ADD EDX,ESI ; create and offset from base pointer
10004878 |. 52 PUSH EDX ; save to stack later used as [ESP+8] what I call the outer offset
10004879 |. 8BD3 MOV EDX,EBX ; reset EDX
1000487B |. C1EA 0B SHR EDX,0B ; still zero
1000487E |. 81E2 FF070000 AND EDX,7FF
10004884 |. 33C9 XOR ECX,ECX
10004886 |. 66:8B4D 08 MOV CX,WORD PTR SS:[EBP+8] ; move "inital condtion" back to CX again
1000488A |. C1E9 04 SHR ECX,4 ; divide by 16
1000488D |. 83E1 0F AND ECX,0F ; mask off high nibble
10004890 |. C1E1 0B SHL ECX,0B ; multiply by 2048
10004893 |. 03D1 ADD EDX,ECX
10004895 |. 03D6 ADD EDX,ESI ; create "2nd" offset
10004897 |. 52 PUSH EDX ; push to stack used as [ESP+4]
10004898 |. 8BD3 MOV EDX,EBX
1000489A |. 81E2 FF070000 AND EDX,7FF
100048A0 |. 33C9 XOR ECX,ECX
100048A2 |. 66:8B4D 08 MOV CX,WORD PTR SS:[EBP+8]
100048A6 |. 83E1 0F AND ECX,0F ; divide by 1 and mask off high nibble and high byte
100048A9 |. C1E1 0B SHL ECX,0B ; multiply by 2048
100048AC |. 03D1 ADD EDX,ECX
100048AE |. 03D6 ADD EDX,ESI ; create inner offset
100048B0 |. 52 PUSH EDX ; save to stack as [ESP]
100048B1 |. 33C0 XOR EAX,EAX ; clear eax
100048B3 |. 8B55 14 MOV EDX,DWORD PTR SS:[EBP+14] ; size of file
100048B6 |> 8B7424 08 /MOV ESI,DWORD PTR SS:[ESP+8] ; "outer" offset
100048BA |. 3206 |XOR AL,BYTE PTR DS:[ESI] ; take byte at esi XOR with whats there
100048BC |> 8B7424 04 |/MOV ESI,DWORD PTR SS:[ESP+4] ; "2nd" offset
100048C0 |. 3206 ||XOR AL,BYTE PTR DS:[ESI]
100048C2 |. 8AE0 ||MOV AH,AL
100048C4 |. 66:8BC8 ||MOV CX,AX
100048C7 |. C1C0 10 ||ROL EAX,10
100048CA |. 66:8BC1 ||MOV AX,CX ; make it repeat throughout eax
100048CD |. 8B3424 ||MOV ESI,DWORD PTR SS:[ESP] ; "inner" offset
100048D0 |> 8A0E ||/MOV CL,BYTE PTR DS:[ESI] ; take byte from esi and move to CL
100048D2 |. 32C8 |||XOR CL,AL
100048D4 |. 300F |||XOR BYTE PTR DS:[EDI],CL ; reveal plain text and save to EDI
100048D6 |. 4A |||DEC EDX ; decrease file length
100048D7 |. 74 3E |||JE SHORT HCPSTool.10004917 ; check if done
100048D9 |. 46 |||INC ESI
100048DA |. 47 |||INC EDI
100048DB |. 43 |||INC EBX ; increase byte counter
100048DC |. F7C3 FF070000 |||TEST EBX,7FF ; decrypted > 2048 bytes?
100048E2 |.^ 75 EC ||\JNZ SHORT HCPSTool.100048D0
100048E4 |. 81EE 00080000 ||SUB ESI,800 ; reset pointer
100048EA |. 893424 ||MOV DWORD PTR SS:[ESP],ESI ; and save it
100048ED |. 8B7424 04 ||MOV ESI,DWORD PTR SS:[ESP+4] ; pick up "2nd" offset
100048F1 |. 3206 ||XOR AL,BYTE PTR DS:[ESI]
100048F3 |. 46 ||INC ESI ; increase ESI
100048F4 |. 897424 04 ||MOV DWORD PTR SS:[ESP+4],ESI ; and save it
100048F8 |. F7C3 FFFF3F00 ||TEST EBX,3FFFFF ; decrypted > 4megabytes?
100048FE |.^ 75 BC |\JNZ SHORT HCPSTool.100048BC
10004900 |. 81EE 00080000 |SUB ESI,800 ; reset pointer
10004906 |. 897424 04 |MOV DWORD PTR SS:[ESP+4],ESI ; outer pointer
1000490A |. 8B7424 08 |MOV ESI,DWORD PTR SS:[ESP+8]
1000490E |. 3206 |XOR AL,BYTE PTR DS:[ESI]
10004910 |. 46 |INC ESI ; reset
10004911 |. 897424 08 |MOV DWORD PTR SS:[ESP+8],ESI ; and save
10004915 |.^ EB 9F \JMP SHORT HCPSTool.100048B6
10004917 |> 83C4 0C ADD ESP,0C ; clean up stack
1000491A |. 61 POPAD
...


I think the theory of this decryption block, so I recall reading on WinAsm forums, is that if I have a completely random encryption/decryption block then it is impossible to decrypt the data without knowledge of that block. Or I might be making that up, but I'm sure I remember reading that somewhere.

I'm at a stopping point though, because I'm lost as to how the decryption block gets to the hooked dll.

GoldStar611
December 15th, 2010, 19:32
Ok I have done a little more analysis on the decryption blocks that are stored in memory. I set a memory breakpoint on access and got nothing, but then I decided to use a hardware breakpoint and got a hit when a REPNE MOVB instruction was writing to the memory block updating the contents.
Following back I see that the decryption block comes from a memory mapped file. This is how the hooked DLL connects to the main program. That an also using WaitForSingleObject to synchronize timings.

Also I thoroughly beleive that this protection uses twin sectors. I was analyzing the DeviceIoControl reads from the CD and verifying the information with alcohol's CD read window. I looked at the data in memory and compared it to Alcohol's view and it was different! Pressing the refresh button changed the current data to match exactly what was in memory.

The DeviceIoControl reads are strange too, Read sector 0x0884, seek sector 0x3e2c, read 0x0884. This pushes the cd lens across data tracks from track 1 to track 2 back to track 1.

evlncrn8
December 17th, 2010, 03:31
Quote:
The DeviceIoControl reads are strange too, Read sector 0x0884, seek sector 0x3e2c, read 0x0884. This pushes the cd lens across data tracks from track 1 to track 2 back to track 1.


yep, timing it.. so it can determine copy vs. original.. not quite rocket science.. other prots do it too like tagés etc..

GoldStar611
December 21st, 2010, 21:44
Yes I have read about tages protection, I think was the first commercial product to introduce them. A very interesting idea I must say.

Copying HexaLock weak sectors and subchannel data is easily accomplished by hot swap method. Basically we bypass error detection codes (EDC) and error correcting codes (ECC) because we swap an audio disk with a data disk (our target). This allows us to read weak sectors and twin sectors because no error detection is occuring and usually the sync header is not parsed by the extracting program nor drive itself (mileage may vary by drive). It was this method that I was able to locate the twin sectors locations, just view the sectors one by one until the sector number dramatically decrements (or decrements at all).

Method goes like this:
Load in large audio cd.
Press stop in wxRipper
Use paperclip to eject disk
Swap with target disk
Press play in wxRipper
Extract data

All that is needed now is a good MDS, CUE, CCD file etc which can be obtained by normally inserting the target disk. Oh and a way to descramble the "audio" back to data or just update CCD file so that UserDataScrambled=1.

Now that I understand we are using both subchannel data tricks and twin sectors I can analyze the other DeviceIoControl call I didn't know what was doing. I bet that was getting the subchannel data. And wxRipper can be found here: http://gael360.free.fr/

evlncrn8
December 30th, 2010, 13:58
actually i think starforce did it before tages did, you tried using latest alcohol / daemon tools to make an image? cos wxRipper sounds old / dated..

GoldStar611
December 30th, 2010, 23:25
I wasn't aware that alcohol or daemon tools would automatically find twin sectors. I still don't think it does.
WxRipper is old, but its functionality doesn't seem to exist in any other program. It allows to easily hot swap disks. Extracting data disks as audio easily extracts twin sectors without need to look for them. All that needs to be done is to unscramble the 2352 bytes of data.

evlncrn8
January 1st, 2011, 20:18
latest alcohol and daemon tools have tages capabilities (twin sectors) emulation... haven't tested it myself though

GoldStar611
January 19th, 2011, 22:16
I actually determined that twin sectors is the only protection the EXE is identifying. For emulation I have written a twin sector injector that injects the 20 sectors needed for authentication (those sectors have special data needed).

There is a physical protection blocking the ability to copy the disk 1:1. I have identified 2 weak sectors, 5 bad sectors, and some sort of custom session at once burning which works only with the supplied software from the company (autolock wizard).

I'm going to try to extract as audio to avoid weak sectors and bad sectors ruining the rip. Next will be an analysis of the Track Descriptor Indexes (TDI) in the lead out and lead in of the first and second session respectively.