telock 0.98 - manually unpacking

Thursday, July 14 2005 @ 11:49 AM CEST

Contributed by: haggar

Level : beginner

Manually unpacking TElock 0.98

tElock 0.98 - manually unpacking

Author: haggar
Objective: tElock 0.98 unpacking
OS: Windows XP
Level: easy


- target
- OllyDbg v1.10
- LordPE
- ImportREC


Hi and welcome to my tElock unpacking tutorial :)!
I was thinking to write more detailed tutorial about tElock, but such text would become too large and I didn't have strength to explain all things that I would love to say, so I wrote this shorter version. Consider this tutorial as a simple guideline for unpacking and not as brief explanation of tElock features.

Tutorial is dedicated for beginers who has some experience with simple packers and who are familiar with usuall tools like Olly, LordPE and ImpREC.

tElock 0.98 is simple protector. It's main features are:

- obfuscated code with exceptions, encrypted sections with couple layers
- some antidebug stuff
- first CRC check that checking is file modified
- second CRC check that checks file in memory and discover breakpoints
- imorts splitting and redirection, this is tE best weapon.

Our way of unpacking is:

- find OEP
- restore redirected imports
- dump file and use ImpREC to rebuild IAT.


3.1. tElock API's

Uncheck all exceptions in Olly options but those in KERNEL. Press Shift+F9 three times and you'll stop here:

0040608D 90 NOP
0040608E 8BC0 MOV EAX,EAX
00406090 F9 STC
00406091 90 NOP
00406092 8D045D 34120000 LEA EAX,DWORD PTR DS:[EBX*2+1234]

When you came to this location, tE has already decrypted lot of code that it need for it's work. One part of that code is below this point. Scroll down and place bp on this place:

00406161 2C 04 SUB AL,4
00406163 8885 99000000 MOV BYTE PTR SS:[EBP+99],AL
00406169 8B95 AF1B0000 MOV EDX,DWORD PTR SS:[EBP+1BAF]
0040616F 81E2 0000FFFF AND EDX,FFFF0000
00406175 8BC4 MOV EAX,ESP
00406177 33E4 XOR ESP,ESP
00406179 8BE0 MOV ESP,EAX
0040617B 66:813A 4D5A CMP WORD PTR DS:[EDX],5A4D
00406180 74 08 JE SHORT Packed_I.0040618A

Now press Shift+F9 untill you break on your bp and then remove bp. In this procedure below, tE will search some API's names in kernel32.dll. Those API's tE needs for unpacking/protecting file. Search goes like this: tE will take name of every single API in kernel32.dll in alphabetic order, starting from the first one "ActivateActCtx" to the last, and compare it's name to the names that it has in it's own list. If some API name coresponds to one from list, it will take that API address and place it to some place for further usage. This is not important for our OEP finding, but it's interesting to see what API's tE uses. Let's see how it does. Trace with F7 untill you reach this line:

004061E4 8138 4C6F6164 CMP DWORD PTR DS:[EAX],64616F4C

Now follow in dump that address DS:[EAX] and youll see that it points to some location in kernel32.dll where API's names starts:

77E85F3F 41 63 74 69 76 61 74 65 41 63 74 43 74 78 00 41 ActivateActCtx.A
77E85F4F 64 64 41 74 6F 6D 41 00 41 64 64 41 74 6F 6D 57 ddAtomA.AddAtomW
77E85F5F 00 41 64 64 43 6F 6E 73 6F 6C 65 41 6C 69 61 73 .AddConsoleAlias

Now take look what tE is comparing here:

004061E4 8138 4C6F6164 CMP DWORD PTR DS:[EAX],64616F4C
004061EA 75 3A JNZ SHORT Packed_I.00406226
004061EC 8178 04 4C696272 CMP DWORD PTR DS:[EAX+4],7262694C
004061F3 75 31 JNZ SHORT Packed_I.00406226
004061F5 8178 08 61727941 CMP DWORD PTR DS:[EAX+8],41797261
004061FC 75 28 JNZ SHORT Packed_I.00406226

It will check does first 12 bytes/characters of API name corespnds to 64616F4C7262694C41797261 bytes. If you translate those bytes from ACII to characters, you will se that they mean "LoadLibraryA". First API that tE search is LoadLibrary. If you remember, I sad that tE has list of those API's, but as you can see, that list isn't in the form of of some strings written someware. List is made like bunch of comparations. Scroll down and you'll see that there is 12 more this similar comparations. That means that tE will use 13 API functions. Check next check:

00406226 8138 45786974 CMP DWORD PTR DS:[EAX],74697845
0040622C 75 19 JNZ SHORT Packed_I.00406247
0040622E 8178 04 50726F63 CMP DWORD PTR DS:[EAX+4],636F7250
00406235 75 10 JNZ SHORT Packed_I.00406247
00406237 8178 08 65737300 CMP DWORD PTR DS:[EAX+8],737365
0040623E 75 07 JNZ SHORT Packed_I.00406247
00406240 68 C7030000 PUSH 3C7
00406245 ^EB BC JMP SHORT Packed_I.00406203

Rest of 12 this checks all ends with jump to 406203 location. That mean, if API name is found, it will jump to this location so put bp on 406203 and press F9. After you break on bp, trace to this line:

00406218 891C28 MOV DWORD PTR DS:[EAX+EBP],EBX ; kernel32.CloseHandle

Here, tE will store address of founded API to some location. Remove last bp and place new on to this line and press F9. You will see what is next API that tE wants. Do this 13 times and you'll see name of all API's:


3.2. Decryption layers

That are some interesting API's. But let's go on. Place bp at

00406437 F8 CLC

and press F9, remove bp and trace further with F7. You will stuck on exception at address 004066A8, but nevermind that. Take a look below at:

004066DB 49 DEC ECX
004066DC ^7F ED JG SHORT Packed_I.004066CB
004066DE 3A82 C69E00CE CMP AL,BYTE PTR DS:[EDX+CE009EC6]

This is one decrypting loop that will decript code below that JG SHORT opcode. Place bp on JG SHORT instruction and press F9 untill you break on it. Leave bp and keep pressing F9 untill instruction below JG doesn't change to:

004066DB 49 DEC ECX
004066DC ^7F ED JG SHORT Packed_I.004066CB

Now remove bp and place new bp on 4066DE and press F9. When you break on bp, code below JG is now decrypted. If you are corious how decryption goes, you could just pass it couple times with F7 to better see what it does. It's not much interesting. After this decryption is done, you'll notice another similar below at:

004066FC 49 DEC ECX
004066FD ^7F F0 JG SHORT Packed_I.004066EF
004066FF F9 STC

Do the same as first time. Then, again you will notice couple more loops that decrypts each other and you just do the same. These loops are:

0040671A 86D8 XCHG AL,BL
0040671C ^E2 F2 LOOPD SHORT Packed_I.00406710
0040671E A8 0C TEST AL,0C

0040673B 49 DEC ECX
0040673C ^7F F0 JG SHORT Packed_I.0040672E
0040673E 40 INC EAX

00406765 49 DEC ECX
00406766 ^7F E9 JG SHORT Packed_I.00406751
00406768 B0 58 MOV AL,58

00406788 49 DEC ECX
00406789 ^7F ED JG SHORT Packed_I.00406778
0040678B 05 B560A3C5 ADD EAX,C5A360B5

004067AB 49 DEC ECX
004067AC ^7F EB JG SHORT Packed_I.00406799
004067AE 8E4E 9D MOV CS,WORD PTR DS:[ESI-63]

00406880 49 DEC ECX
00406881 ^7F E0 JG SHORT Packed_I.00406863
00406883 F8 CLC

After you pass these decryption layers, scrol down and you will notice some calls.

3.3. First CRC check

These calls are:

0040694E FF95 EF030000 CALL DWORD PTR SS:[EBP+3EF]
00406996 FF95 EB030000 CALL DWORD PTR SS:[EBP+3EB]
004069A3 FF95 E7030000 CALL DWORD PTR SS:[EBP+3E7]
004069A9 FF95 E3030000 CALL DWORD PTR SS:[EBP+3E3]

Place bp on each CALL and press F9. You will stop at first call that calls kernel32.GetModuleFileNameA. From API reference: "The GetModuleFileName function retrieves the full path and filename for the executable file containing the specified module." tElock is searching for it's packed file. Press F9 and you'll stop on next bp where kernel32.CreateFileA is called. tE is opening packed file. Next, F9 again and you stop on next bp where kernel32.ReadFile is called - tE is reading our exe file. And next is kernel32.CloseHandle. This is begining of first CRC check. Next you have lots of some code which is not so interesting so scroll down to:

00406C4B 8B85 80D24000 MOV EAX,DWORD PTR SS:[EBP+40D280]
00406C51 50 PUSH EAX
00406C52 35 BB2C7E8E XOR EAX,8E7E2CBB
00406C5C 5B POP EBX
00406C5D 6A 01 PUSH 1
00406C5F 58 POP EAX
00406C60 6A 08 PUSH 8
00406C62 59 POP ECX
00406C63 0F85 12060000 JNZ Packed_I.0040727B
00406C69 74 19 JE SHORT Packed_I.00406C84
00406C6B 78 50 JS SHORT Packed_I.00406CBD

This is end of first CRC check. Place bp on first above line and press Shift+F9 untill you stop on bp. Remove it. [ebp+40d280] holds calculated checksum that will be XOR-ed and substracted. Finally EAX must be equal to 0 so that JNZ wouldn't be executed. That JNZ at 406C63 leads to CRC error message and exits file.

3.4. Second CRC - checking memory

In a command bar or command line place bp on VirtualAlloc+1. Press Shift+F9 untill you break in kernel, than remove bp and return to user code. We placed bp on second opcode in VirtualAlloc API because tE checks is there bp on that API. We are now in second CRC procedure. This procedure is checking does file is modified in memory so it can find our breakpoints. Scroll little up and you'll see:

0040704D 61 POPAD
0040704E 0F85 27020000 JNZ Packed_I.0040727B
00407054 50 PUSH EAX
00407055 51 PUSH ECX
00407056 E8 1A000000 CALL Packed_I.00407075
0040705B 83F8 FF CMP EAX,-1
0040705E 0F84 17020000 JE Packed_I.0040727B
00407064 83C7 0C ADD EDI,0C
00407067 4E DEC ESI
00407068 7E 06 JLE SHORT Packed_I.00407070
0040706A FFA5 48C34000 JMP DWORD PTR SS:[EBP+40C348]
00407070 E9 02010000 JMP Packed_I.00407177
00407075 60 PUSHAD
00407076 8B5C24 24 MOV EBX,DWORD PTR SS:[ESP+24]
0040707A 83C3 10 ADD EBX,10
0040707D 6A 04 PUSH 4
0040707F 68 00100000 PUSH 1000
00407084 53 PUSH EBX
00407085 6A 00 PUSH 0
0040708D 85C0 TEST EAX,EAX
0040708F 0F84 D6000000 JE Packed_I.0040716B

That first jump abowe (below POPAD) is end of this CRC check and it leads to error message. Place bp on it and press Shift+F9 to break on it. This CRC check is now passed.

3.5 To the OEP

Next, tE will allocated some blocks of memory for redirecting imports, but about that latter. Press Shift+F9 three times untill you break here:

00407824 ^73 DC JNB SHORT Packed_I.00407802
00407826 CD 20 INT 20
00407828 64:67:8F06 0000 POP DWORD PTR FS:[0]
0040782E 58 POP EAX
0040782F 61 POPAD
00407830 EB 02 JMP SHORT Packed_I.00407834

Place bp on POPAD, Shift+F9, remove bp. This was the last exception. Now, you could trace with F7 for hours and finaly you would get to last RETN at 407902, so you just press Ctrl+F9. That return will throw you to:

0040773F 8B9D 82D34000 MOV EBX,DWORD PTR SS:[EBP+40D382]
00407745 33F6 XOR ESI,ESI
00407747 F7D3 NOT EBX
00407749 0BF3 OR ESI,EBX
004077A9 EB 02 JMP SHORT Packed_I.004077AD
004077AB CD 20 INT 20
004077AD 61 POPAD
004077AE FF6424 D0 JMP DWORD PTR SS:[ESP-30]

This last jump after POPAD will throw you to the OEP at 401000.


So we finaly found OEP. But we spend quite some time on that , are we ;) Isn't that little slow? Don't wory, you shouldn't alway search OEP by this way. Now when you know something more about tE you can use fast way to reach OEP:

Press Shift+F9 20 times and you'll stop at last exception, place bp below on POPAD, again Shift+F9 and remove bp. Press Ctrl+F9 to run to RETN opcode, F8 one time, find jump below popad that leads to OEP. And that's it ;)


Now when we had reach the OEP, we need to restore imports. tElock has allocated (reserved) couple memory blocks in which he splitted and redirected imports. The number of allocated blocks is usually 4~5. When you're at OEP of our target, scroll down to this address:

00402D18 $-FF25 14304000 JMP DWORD PTR DS:[403014]
00402D1E $-FF25 04304000 JMP DWORD PTR DS:[403004]
00402EC2 $-FF25 20304000 JMP DWORD PTR DS:[403020]
00402EC8 $-FF25 1C304000 JMP DWORD PTR DS:[40301C]

These jumps where originaly jumps to imports. Those [xxxxxxxx] dwords where holding addresses of API's functions, but tE has placed in their place values that leads to allocated blocks of memory. From there, API functions will be called, but before that they are little messed to prevent tools and dumper engines like ImpREC or ProcDump. This is biggest problem when unpacking tElock and this is (or it should be) core of this tutorial. If you dump file and try to use ImpREC, you will fail to repair IAT and fille will crush. We will fix that by this way:

5.1. First redirection

Left click on first jump:

00402D18 $-FF25 14304000 JMP DWORD PTR DS:[403014]

You'll see below that DS:[00403014]=009B0052. In your computer that value will probably be different. Right click on CPU window, select "Go to" and "Expression" and enter 009B0052 in the field. Click OK and you'll land here:

009B0052 EB 01 JMP SHORT 009B0055 ; This is only junk.
009B0054 66:90 NOP
009B0056 2BC5 SUB EAX,EBP
009B0058 B8 09019B00 MOV EAX,9B0109 ; This is only important to us, EAX=9b0109,
009B005D 40 INC EAX ; Add 1 to EAX,
009B005E FF30 PUSH DWORD PTR DS:[EAX] ; Push that address,
009B0060 C3 RETN ; Jump to that address.

This small pice of code above is our cloacked import jump. EAX=9B0109+1=9B010A. Follow that address in dump and you will found that it points to some API address:

009B010A D7 3C 00 7F

Select that four bytes and Binary copy them. Then return to JMP at 402D18 and follow it's walue in dump window:

00403014 52 00 9B 00

Select those four bytes and Binary paste. You have just restored one import to it's original place. Go again to 9B010A in dump and scroll little up. You'll see five more imports:

009B00EA 00 00 00 00 00 00 00 00 00 00 00 00 D7 1C 00 7F
009B00FA E4 20 00 7F CC 39 00 7F 00 1B 00 7F 53 1E 00 7F

Now Binary copy those values and return to that one import that you have restored. Scroll up and Binary paste five new import values to five places above first restored. You have just restored whole one block of redirected imports. This block is small and contains only six imports.

5.2. Second redirection

Now select next unresolved jump:

00402D4E $-FF25 98304000 JMP DWORD PTR DS:[403098]

Do the same as first time and follow this DS:[00403098]=009E00A3 in CPU window to:

009E00A3 F8 CLC
009E00A4 73 02 JNB SHORT 009E00A8
009E00A6 0F21 ??? ; Unknown command
009E00A8 48 DEC EAX
009E00A9 B8 33079E00 MOV EAX,9E0733
009E00AE 40 INC EAX
009E00B1 C3 RETN

As you can see, this is very similar as in ou first example. Follow EAX=9E0733+1=9E0734 in dump. There you can see lot of imports:

009E0734 69 5F D4 77 5E 9C D4 77 CB 80 D4 77 88 32 D7 77 i_w^wˀw2w
009E0744 38 53 D4 77 52 65 D6 77 1D 5F D4 77 0F 6E D5 77 8SwRew_wnw
009E0754 9B 79 D4 77 4E 7A D4 77 E6 7E D4 77 0E 5F D4 77 ywNzw~w_w
009E0764 0F 79 D4 77 60 2B D9 77 CC 65 D4 77 E2 3D D4 77 yw`+wew=w
009E0774 84 5B D4 77 E4 D9 D4 77 2C 50 D5 77 BF 57 D6 77 [ww,PwWw
009E0784 69 43 D4 77 E0 56 D6 77 80 A1 D4 77 50 5C D4 77 iCwVwwPw
009E0794 21 8B D4 77 26 5B D6 77 76 8D D4 77 E4 9F D6 77 !w&[wvww
009E07A4 46 C2 D4 77 E4 B9 D4 77 8D BD D4 77 42 B7 D4 77 FwwwBw
009E07B4 7C 9C D4 77 64 B0 D6 77 |wdw

Binary copy them, return to unresolved jump JMP DWORD PTR DS:[403098] and follow it in dump and paste copyed bytes. Return again to place fom where you have taken bytes, scroll up and you'll see couple more imports:

009E0704 77 43 D4 77 50 D1 D5 77 wCwPw
009E0714 FE 65 D6 77 C9 48 D5 77 00 53 D4 77 0A 59 D6 77 ewHw.Sw.Yw
009E0724 31 6E D5 77 FF 5E D4 77 29 53 D4 77 1F 97 D4 77 1nw^w)Sww

Copy them and paste them above those first fixed, just like in the first example. Just watch that you properly count lines so that you don't mess values because you will get wrong imports. With this you have fixed second redirected imports.

5.3. Last one

Follow next jump in CPU:

00402E56 $-FF25 60304000 JMP DWORD PTR DS:[403060]

You're here:

00A00113 EB 02 JMP SHORT 00A00117
00A0011C B8 7703A000 MOV EAX,0A00377
00A00121 40 INC EAX
00A00124 C3 RETN

Again, this is mangled jump to some API. Follow EAX=00A00377+1=00A00378 in dump and you'll find couple imports:

00A00378 43 A6 E7 77 AE 98 F7 77 91 4B E7 77 CwwKw

Do again like ine previous examples, binary copy and paste that values, scroll upp and copy rest of values:

00A00328 0C 61 E7 77 .aw
00A00338 35 4E E7 77 8C 5F E7 77 8D F0 E7 77 A1 AA E7 77 5Nw_www
00A00348 C4 6F ED 77 5F 8C F5 77 6B 15 F5 77 A1 16 F5 77 ow_wkww
00A00358 D2 B6 E7 77 C2 28 E7 77 D9 AC E7 77 FC AC E7 77 Ҷw(w٬ww
00A00368 85 94 E7 77 DF E5 E7 77 63 98 E7 77 C9 B3 E7 77 wwcwɳw

and paste them above first ones. You have just restored last redirected import block. Now, all the imports should be on their original places.

Now dump file to hard drive with LordPE.
Open ImpREC, enter OEP, click IAT auto search and get imports. You will see that it found one thunk, but it 3 invalid pointers. Cut that three invalid and fix dumped file. Open dumped file and olay a little with it and you'll see that it works fine.


tE has old SoftICE check that is no comatibile with XP and that is the reason why I didn't descover it in the first way. If you have Windows 98 or some other that support this check, you will probably have problems following this tutorial because one window will popup teling you that file wont run with active debugger. Now we gona find that check. Restart target and press Shift+F9 about 13 times untill you break here:

00406B67 F7F3 DIV EBX
00406B93 C3 RETN
00406B94 66:B8 0043 MOV AX,4300
00406B98 EB 02 JMP SHORT Packed_I.00406B9C
00406B9A CD 20 INT 20
00406B9C 81B5 591C0000 AD>XOR DWORD PTR SS:[EBP+1C59],0B4806AD
00406BA6 CD 68 INT 68
00406BA8 66:05 7B0C ADD AX,0C7B
00406BAC 66:48 DEC AX
00406BAE 74 55 JE SHORT Packed_I.00406C05

This last jump must not be executed because it leads to error message. This check will not work on XP machines.


tElock isn't hard protector and it's quite old one (2001 was relised). I don't believe that you will found many targets packed with it. Today most of programs use ASProtect or Armadillo or some other more pwerfull protector. But point of this tutorial was to learn something.

If this method of fixing IAT will look confusing to you, pack couple programs with tE by your self and try to unpack them. To test this method, I packed Mozilla Thunderbird which is 7 MB big and it has 2090 imports. After packing there were about 300 redirected imports to 6 allocated blocks of memory. With copy-paste I manage to restore them all really fast. ImpREC found then 6 invalid pointer which I just cutted. It seams that ImpREC will always find one invalid pointer in every allocated block. Maybe bug or something.

That's all. I hope you like this tutorial and that it would be of some help for somebody. I was planing to write shorter one, as I sad on the begining, but it again become prety large tut. If you had some questions, feel free to post comment here or on the forum.

I posted attachment in which you will find target and this tutorial in rtf format.

Big greets goes to detten and all crew at BIW reversing.

haggar [ A.D. 14.07.2005 ]