TORN@DO presents: cRACKER's n0TES
Commerical Protection Systems: TimeLOCK


TimeLOCK 3.1 (JaZZ)
The problem with TimeLOCK 3+ is that it rips out some code of the app, patching it at runtime if all the lights are green! Needless to say that this crap encrypts the missing bytes, so u won't find this 'ready_to_patch' in a dummy DLL or so ... Marigold had come to the conclusion that the missing snippet had to be copied (but he didn't explain how) then patched directly onto the exe. So I understood 'copied by hand'. What was in his case acceptable, because the snippet wasn't that long ('only' 0x16E bytes), is here impossible: as you'll see, an improvement of TL31INJ.dll (among others?) is that the 'Bag of bytes' (how silly) is growing. For this NetscanTools 3.1 it ranks 0x716 (= 1814) bytes. My aim is to explain how to let bloated TimeLOCK do its dirty work, and then gently stop it for saving the code to a file. So let's start the cracking. A few lines from the dissasembled file of the example NSTSTD.EXE will quickly explain it:


    //******************** Program Entry Point ********
    :004FA000   FF74240C         push [esp+0C]
    :004FA004   FF74240C         push [esp+0C]
    :004FA008   FF74240C         push [esp+0C]
    :004FA00C   68D879A084       push 84A079D8
    :004FA011   68C1FED07D       push 7DD0FEC1
    :004FA016   68A29C5D1E       push 1E5D9CA2
    :004FA01B   FF15D4A24F00     call dword ptr [004FA2D4]    ---> this is the infamous call
    :004FA021   68FFFFFFFF       push FFFFFFFF
    :004FA026   FFD0             call eax                     ---> when returning eax
                                                                   contains the correct adress,
                                                                   which is the targets 'real'
                                                                   entry point
    :004FA028   C20C00           ret 000C                     ---> yes, thats all!
    
As one can easily experiment, bypassing 4FA01B leads to nothing, even with the correct eax, because this is precisely where TimeLOCK will patch. I didn't want to delve into the 'heavy' TimeLOCK encryption, CRC checking (it checks its integrity, as the target's one), all that boring stuff ... Seems that it'd be easy to let it do the job :-)


1) You have a working trial version, then jump over the 50 next lines

2) Like me, you 'burnt' it on the first trial day by pushing the date 1 month ahead, just to see what happens, when restoring back the clock. (No, it's expired for good :=)

So up to work with TL31INJ.DLL. We have at first to bypass the dialogbox with the 'try' button grayed for ever. With SoftICE we find it easily:



    :100081FF   6A00            push 00000000
    :10008201   A164A30310      mov eax, dword ptr [1003A364]
    :10008206   6840830010      push 10008340
    :1000820B   6A00            push 00000000
     
    * Possible StringData Ref from Data Obj ->"TL_DLG_MAIN"
                                 |
                                                          
    :1000820D   687CF60310      push 1003F67C
    :10008212   50              push eax
     
    * Ref To: USER32.DialogBoxParamA, Ord:008Eh
                      --> when ret.,eax=1 if pressed 'try first'
    :10008213   FF1530960410    Call dword ptr [10049630]
    :10008219   8BF8            mov edi, eax
    
So we bpx 10008213 (care with relocation, on my pc it was 508213 ...) When SoftICE pops, F10, press cancel, then r eax=1 to simulate the 'try_it' push.

Useful tip
Don't try to patch at 10008213 to gain time. As you can see, this is an absolute adress, the loader will PATCH after you, in case of relocation (this is highly probable), causing a highly probable cras h... Doing this right you have to patch the relocation table. More on this at the end.


Unfortunately, this is not enough, as marigold noticed with the previous version. Now we are stuck in a routine beginning at 1002B020:



    * Referenced by a CALL at Addresses: (a hundred of these follow)
                                  |
                                                          
    :1002B020   83EC20            sub esp, 00000020
    :1002B023   B908000000        mov ecx, 00000008
    :1002B028   56                push esi
    :1002B029   57                push edi
    :1002B02A   BE28730310        mov esi, 10037328
    :1002B02F   8D7C2408          lea edi, dword ptr [esp+08]
    :1002B033   F3                repz
    :1002B034   A5                movsd
    :1002B035   8B44242C          mov eax, dword ptr [esp+2C]
    :1002B039   8B4C2430          mov ecx, dword ptr [esp+30]
    :1002B03D   8D54241C          lea edx, dword ptr [esp+1C]
    :1002B041   89442420          mov dword ptr [esp+20], eax
    :1002B045   8B442418          mov eax, dword ptr [esp+18]
    :1002B049   52                push edx
    :1002B04A   8B54240C          mov edx, dword ptr [esp+0C]
    :1002B04E   50                push eax
    :1002B04F   894C242C          mov dword ptr [esp+2C], ecx
    :1002B053   8B4C2414          mov ecx, dword ptr [esp+14]
    :1002B057   51                push ecx
    :1002B058   52                push edx
     
    * Reference To: KERNEL32.RaiseException, Ord:01C9h
    :1002B059   FF15F4950410      Call dword ptr [100495F4]
                  --> I'm afraid we won't go back from here :-)
    :1002B05F   5F                pop edi
    :1002B060   5E                pop esi
    :1002B061   83C420            add esp, 00000020
    :1002B064   C20800            ret 0008
   
To get out of this trap, just bpx 1002B020, restart the whole thing and when it pops assemble a: ret 0008 (remember a 'hard' patch won't work coz of the CRC check ... here causing a page fault ... How mighty they are at Previewsoft)!

Now whoever you are, expired or not, the app is working, (although weak cracked !). By pressing F12 one or two times you should be brought back in 4FA021 (look above) just before the damned CALL EAX. As you can see, EAX is now 43D680 and this location contains no longer bogus code... We restart then the whole process with a bpm 43D680, just to see how & where the big patch is aplied. We land here:



    * Reference To: KERNEL32.ReadProcessMemory, Ord:01D8h
                                  |
                                                          
    :10015C23   FF158C940410      Call dword ptr [1004948C]
    :10015C29   85C0              test eax, eax
    :10015C2B   740B              je 10015C38
     .....                                                          ; some code here
    :10015C6A   50                push eax
    :10015C6B   8D8D48FFFFFF      lea ecx, dword ptr [ebp+FFFFFF48]
    :10015C71   E84A140000        call 100170C0
       
    * Referenced by a Jump at Address:10015C5C(C)
                                  |
                                                          
    :15C76      8D45D8            lea eax, dword ptr [ebp-28]
    :15C79      50                push eax
    :15C7A      8B8548FFFFFF      mov eax, dword ptr [ebp+FFFFFF48]
                                     ; this is the length = 0x716
    :15C80      50                push eax
    :15C81      8B854CFFFFFF      mov eax, dword ptr [ebp+FFFFFF4C]
                                     ; where to fetch the patch
    :15C87      50                push eax
    :15C88      8B4DD4            mov ecx, dword ptr [ebp-2C]
                                     --> where to apply (=43D680)
    :15C8B      51                push ecx
    :15C8C      56                push esi
     
    * Reference To: KERNEL32.WriteProcessMemory, Ord:0283h
                                  |
                                                          
    :10015C8D   FF1588940410      Call dword ptr [10049488]
                                      --> go ahead, patch it!
    :10015C93   85C0              test eax, eax
   
Now we have all the elements to kick this TimeLOCK crap out for good: press F12 to get up to 4FA021. It's quite clear: we want to save 716h bytes from 43D680 to a file. How proceed? Well let's assemble a small program using win32 API that'll make it for us! As it seems, the code over 4FA028 is bogus. So We'll work here.

>A 4FA040

Then type the following instructions (I rely on win32 help & include files for the pushes with CreateFileA and WriteFile):



    push 0                ; bogus but necessary
    push dword ptr 80     ; file_attributes_normal (don't forget dword ptr or it will push a
                            'signed' FFFFFF80 ... I spent 2 hours on this bug!)
    push 02               ; create_always
    push 0                ; null (security field)
    push 0                ; file is not shared
    push 40000000         ; file is write only
    push 004FA200         ; the adress of the name of the file
    call KERNEL32!CreateFileA
    push eax              ; eax=handle. We push it for later closehandle
    push 0                ; a dummy one
    push 004FA100         ; where to store the number of bytes written
    db 68 16 07 00 00     ; this is: push dword ptr 0716, I didn't manage a 'real dword' push!
    push 43D680           ; we save from here
    push eax              ; the handle
    call KERNEL32!WriteFile
    call KERNEL32!CloseHandle
                          ; the handle is still on the top of the stack
    push ff               
    jmp 43D680            ; bye bye!
   
Well, just type

>EB 4FA200

And enter the full pathname of the file where the data will be stored, for example 'C:\bigpatch',0 and now .... g=4FA040. I hope your HD hasn't gone formatting, mine is OK :-)




TimeLOCK 3.13 - 3.15 (Marigold)
I overlooked this version when it first appeared, so I'll give some detail here.

New features are:

The whole code section is encrypted.
.idata section is encrypted.
Checksum of the protection DLL image in memory is calculated (Actually, I'm not sure that this feature was not present in previous versions. At least, it made no problem).
*I use word 'checksum' here and below in a sense 'hash function'
Unwrapping is not so simple as with the previous version in addition to the above-described procedure, which generally remains untouched, we need to restore .idata section

Restoration of information from .idata section is similar to that that would be used later in VBox and is described elsewhere.

Differences are as follows: 1) Encrypted information is stored in .idata section itself; 2) The first field of 26-byte record for an imported object always contains the module name index in the name array (-1 with meaning 'previous' is never used).

Let's consider how the necessary information could be collected in the case of TL315INJ.DLL. Differences with TL313INJ.DLL are minimal.



      :1001830D 8BB52CFEFFFF            mov esi, dword ptr [ebp-1D4] 
      :10018313 8B9D30FEFFFF            mov ebx, dword ptr [ebp-1D0] 
      :10018319 85F6                    test esi, esi 
      :1001831B 742E                    je 1001834B 
    
Here [ebp-1D4] contains a length and [ebp-1D0] a pointer to an array of zero terminated strings and names of imported modules and functions and the name array. Dump this array into a file.

Next, we need a description for each imported function. This description is a 26(1A)-byte long record. Records are decrypted one at time to bind an imported function. Binding is being made in a loop 1839A ... 18525.


      :1001839A 813DCC9D0410F8000000    cmp dword ptr [10049DCC], 000000F8 
      :100183A4 7523                    jne 100183C9 
      :100183A6 85FF                    test edi, edi 
      :100183A8 8D8564FAFFFF            lea eax, dword ptr [ebp+FFFFFA64] 
      :100183AE 50                      push eax 
      :100183AF 750C                    jne 100183BD 
      :100183B1 E82A7A0000              call 1001FDE0     ; Decryption of the 
      :100183B6 83C404                  add esp, 00000004 ; first record 
      :100183B9 8BD8                    mov ebx, eax 
      :100183BB EB14                    jmp 100183D1 
      
      * Referenced by a Jump at Address:100183AF(C) 
       | 
      :100183BD E82E7B0000              call 1001FEF0     ; Decryption of 
      :100183C2 83C404                  add esp, 00000004 ; all following records 
      :100183C5 8BD8                    mov ebx, eax 
      :100183C7 EB08                    jmp 100183D1 
      ; Now ebx contains a pointer to the record for the current imported function 
      . . . 
      :10018503 8B4316                  mov eax, dword ptr [ebx+16] 
      :10018506 0305F04E0410            add eax, dword ptr [10044EF0] 
      :1001850C 8BC8                    mov ecx, eax 
      :1001850E 8B8560FFFFFF            mov eax, dword ptr [ebp-A0] 
      :10018514 8901                    mov dword ptr [ecx], eax ; Address of function 
      :10018516 83C71A                  add edi, 0000001A        ; is recorded to Import 
      :10018519 FF45CC                  inc [ebp-34]             ; Address Table 
      :1001851C 8B4DCC                  mov ecx, dword ptr [ebp-34] 
      :1001851F 398D64FFFFFF            cmp dword ptr [ebp-9C], ecx 
      :10018525 0F8F6FFEFFFF            jg 1001839A 
   
One needs to patch code inside this loop to save all records into a mapped file created with SoftDump. As this loop includes error-handling code, which never works, one has plenty of space for this patch. I intentionally give no code for this patch as the next step is rebuilding of .idata section and requires of cracker higher programming skills than this simple task.

One important note about image checksum. The creators of this protection certainly follow our 14 Commandments: the checksum is never checked against its 'right' value. Instead it is incorporated into a decryption key, so if one makes soft-patching (file checksum is another problem) at inappropriate moment, the decryption of the name array goes wrong and system crashes.

Now you should take into account that setting a bpx actually means the changing of a byte on this address to CC, though SoftICE doesn't show it. (Of course, it is applicable to all versions with code checksum checking).

Thus, to get to the code snippets above, you should not set breakpoints there.

Here is a call to the function that calculates checksum:



      :100159DF 8D851CFEFFFF            lea eax, dword ptr [ebp+FFFFFE1C] 
      :100159E5 50                      push eax ; Buffer for checksum 
      :100159E6 8D85B4FDFFFF            lea eax, dword ptr [ebp+FFFFFDB4] 
      :100159EC 50                      push eax 
      :100159ED 8D85BCFDFFFF            lea eax, dword ptr [ebp+FFFFFDBC] 
      :100159F3 50                      push eax 
      :100159F4 8D85C4FDFFFF            lea eax, dword ptr [ebp+FFFFFDC4] 
      :100159FA 50                      push eax 
      :100159FB 81F7494C0000            xor edi, 00004C49 ; "LI" < Weijun Li 
      :10015A01 57                      push edi 
      :10015A02 81F64A570000            xor esi, 0000574A ; "WJ" < initials 
      :10015A08 0335EC4E0410            add esi, dword ptr [10044EEC] 
      :10015A0E 56                      push esi 
      :10015A0F E8ECA50000              call 10020000 
      :10015A14 83C418                  add esp, 00000018 
 
So, you should step all the way through code or you may set bpx 10015A0F, clear it at arrival and set necessary breakpoints after return from this function.





The cRACKER's n0tES are divided into 10 main parts:
 00. INDEX
 01. Assembly for Crackers (CoRN2)
 02. SoftICE (Boot Menu, Setup, Commands)
 03. Breakpoints & Win API Details
 04. Jump Instructions
 05. SET Instructions
 06. Tips & Tricks for Cracking
 07. Window Messages For Crackers
 08. Identifying Functions, Arguments, and Variables (Rhayader)
 09. Commerical Protection Systems
        1 Armadillo
        2 C-Dilla SafeDISC
        3 SalesAgent
        4 SecuROM
        5 softSENTRY
        6 TimeLOCK
        7 VBox
 10. Bitmanipulation (Cruehead)
 11. General Cracking Theory
 12. FAQ

 +A. How to contact me
 +B. What's New?



The cRACKER's n0TES are Copyright © 1998-2000 by TORN@DO of ID. All Rights Reserved. Archived and Re-hosted by Werdstaff