Copylok by Kilby published by +Tsehp Updated 010311 This update is due to some confusion I have caused inserting the IT This was pointed out by BlackBird. Although I included the following line: Why not 761f8, well look at the section information, particularly at the Virtual & Raw Offset fields, on the .data and .rsrc sections. I now realise this was much too obscure, and did not help anybody in the slightest. Additionally there is a note at the end of the file which points out an error of judgement which I made regarding the placement of the IT. --------------------- Work on this protection system was slow, although the actual work on it only took about five evenings (and two early mornings). My five evenings where spread out between mid December 2000 and mid Feb 2001, and required me to learn about Dumping, PE file formats and IAT rebuilding. This research was the reason I victimised Crunch 2 by Bi Tarts . There isn't much if any information regarding Copylok, in the scene so heres what I know. This information is taken from the only example of copylok I have which is Sudden Strike (UK release) Firstly how do I recognise Copylok Well the CD has a visible ring on the data section, and the protected .exe file has .icd sections. This accounts for why some 'crackers' claim that it is a version of Safedisc. They claim to use 128 bit encryption keys, it has been mentioned that once again the TEA algorithm has been used (as in Safedisc.) I only found the old CreateFileA anti-softice code, though Duke (Hi there), says that he has seen versions with better anti-softice code.) The wrapper decrypts itself in phases, and each phase is made up of many small blocks (typically 0x64 to 0x72 bytes), and jumps all over memory like it has a dose of crabs. The CD is easy to copy, despite what Panlok Ltd claim and I can't wait for them to start using their uncopyable CD-Roms as a replacement for smart cards :) You can't run a straight dump the main .exe using pedump or icedump as the imports have been screwed with. Copylok is becoming more common due to Take 2 Interactive using it, along with several other European publishers, so it's time for an essay. The method I am going to use is a bit hack and slash, and requires an original (or working clone) CD. However iNX offers the following information, for those without a suitable CD -------------------- Anyway I can tell you that the cd checks seems not very efficient because there is an average of 10 sectors read and the bad one is always around the 7th or 8th read. If you start sending back result error to aspi calls after the 7th, 6th read, the program starts... I would guess they considered that a cd reader couldn't read some other sectors than the effective bad one (maybe because the cd has got many many bad sctors interleaved with good ones) and so they introduced some sort of tolerance. -------------------- So as Frank would say "Let's hit the fuckin road" Tools Used: Softice (Our old friend) Icedump Procdump Revirgin (Our new friend) Hex Workshop Sudden Strike+CD Pen Paper Sony MZ-R55 playing Gomez. Preparation: In my case I have to stop DBServer from running, along with the creative launcher shit for my SB Live, so I don't get too many false breaks inder softice. Method: As I couldn't be arsed to trace the whole damn thing we will make use of a hole in Copyloks armour. So what kind soul will let us gain entry to the unwrapped .exe Well once again that kind individual is Microsoft, and in particular Visual C. The inital setup code of the wrapped file is Visual C, so let us assume that the original file is also Visual C based. Load Icedump, this will hide us from the debugger checks. Place a breakpoint on GetVersion, then return to windoze. Run SuddenStrike.exe Immeditely sice breaks hit F11 and we see the following :u 415d40 L 100 0167:00415D40 55 PUSH EBP ; Entry point of Executable 0167:00415D41 8BEC MOV EBP,ESP 0167:00415D43 6AFF PUSH FF 0167:00415D45 68B0E74600 PUSH 0046E7B0 0167:00415D4A 684CB14100 PUSH 0041B14C 0167:00415D4F 64A100000000 MOV EAX,FS:[00000000] 0167:00415D55 50 PUSH EAX 0167:00415D56 64892500000000 MOV FS:[00000000],ESP 0167:00415D5D 83C4A4 ADD ESP,-5C 0167:00415D60 53 PUSH EBX 0167:00415D61 56 PUSH ESI 0167:00415D62 57 PUSH EDI 0167:00415D63 8965E8 MOV [EBP-18],ESP 0167:00415D66 FF15F8624700 CALL [KERNEL32!GetVersion] ; The call to GetVersion (Look at the address the call uses) 0167:00415D6C A3B4484700 MOV [004748B4],EAX ; Where we have ended up after F11 As we can see typical Visual C setup code We are now in the .icd1 section of SuddenStrike Hit F5 again to return to executing the code. A splash screen appears, and there is a few seconds pause. Sice breaks again, this time after hitting F11 we find that we are in WNASPI32, (this dosn't matter to us as we are being lazy) so hit F5 again. Another pause and sice reappears hit F11 and we find the following; 0167:00402F8D FF1570A04000 CALL [KERNEL32!GetVersion] ; Where Get version was called from (Look at the address the call uses now) 0167:00402F93 33D2 XOR EDX,EDX If we look back through this section of code we will see this; :u 402f67 L 100 0167:00402F67 55 PUSH EBP 0167:00402F68 8BEC MOV EBP,ESP 0167:00402F6A 6AFF PUSH FF 0167:00402F6C 6880A14000 PUSH 0040A180 0167:00402F71 68905B4000 PUSH 00405B90 0167:00402F76 64A100000000 MOV EAX,FS:[00000000] 0167:00402F7C 50 PUSH EAX 0167:00402F7D 64892500000000 MOV FS:[00000000],ESP 0167:00402F84 83EC58 SUB ESP,58 0167:00402F87 53 PUSH EBX 0167:00402F88 56 PUSH ESI 0167:00402F89 57 PUSH EDI 0167:00402F8A 8965E8 MOV [EBP-18],ESP 0167:00402F8D FF1570A04000 CALL [KERNEL32!GetVersion] ; Where F11 placed us 0167:00402F93 33D2 XOR EDX,EDX More Visual C setup code. In addition we are also in SuddenStrike.Text, this means that we are at the start of the unwrapped file and we have found the OEP (Original Entry Point) So we dump the file, I prefer using icedump for this as we don't have to halt the exe to dump it so; /pedump 400000 2f67 f:\ssdumped.exe The 2f67 is the RVA (Relative Virtual Address) of the programs entry point. As we can see the pointer to the import of GetVersion has moved from 004762F8 to 0004a070, this means that we are working with a new IAT So lets look at the IAT we know it lives around 0040A070 :d 40a070 l 20 0030:0040A070 1B 2F F9 BF E8 FF 42 00-4C 10 43 00 31 0E 43 00 ./....B.L.C.1.C. 0030:0040A080 CF 05 43 00 B3 02 43 00-83 F8 42 00 5C 1D 43 00 ..C...C...B.\.C. Well the address for GetVersion looks pretty normal BFF92F1B. However look at the following address at 40a074 it's pointing to 0042FFE8 which is a strange address for a KERNEL32 call. So lets look at 0042FFE8 :u 42ffe8 l 10 0167:0042FFE8 A1E49CFCBF MOV EAX,[BFFC9CE4] 0167:0042FFED FF742404 PUSH DWORD PTR [ESP+04] 0167:0042FFF1 8B08 MOV ECX,[EAX] 0167:0042FFF3 E90BD5B5BF JMP BFF8D503 So the jmp at 0042FFF3 looks like it's jumping into the right area for a KERNEL32 import, so let's look at the code we are jumping to; :u bff8d4f8 l 20 KERNEL32!ExitProcess 0167:BFF8D4F8 A1E49CFCBF MOV EAX,[BFFC9CE4] 0167:BFF8D4FD FF742404 PUSH DWORD PTR [ESP+04] 0167:BFF8D501 8B08 MOV ECX,[EAX] 0167:BFF8D503 80492220 OR BYTE PTR [ECX+22],20 ; Address jumped to ! 0167:BFF8D507 E8C3FCFFFF CALL BFF8D1CF 0167:BFF8D50C C20400 RET 0004 0167:BFF8D50F 8B442404 MOV EAX,[ESP+04] 0167:BFF8D513 33D2 XOR EDX,EDX 0167:BFF8D515 8B4854 MOV ECX,[EAX+54] As we can see Copylok has; 1: Borrowed the first three instructions of ExitProcess 2: Executed them locally 3: Jumped directly to the fourth instruction in the 'REAL' routine This is known as redirection, and is used to make the reconstruction of the IAT more difficult. So what can we do about this, well in times past it would have involved rebuilding the real calls by hand, or writing a routine to do the job for us. However thanks to +Tshep we have a new friend which can do this for us, and that friend is called ReVirgin. For using ReVirgin we need the start address and length of the IAT, although revirgin can find this on normal programs and some protected apps (ASProtect for example), we will hunt down the table just incase. We know that GetVersion sits around 40a070, so lets start hunting; :d 40a000 l 170 0030:0040A000 E8 19 43 00 3C 13 43 00-E4 F2 42 00 00 00 00 00 ..C.<.C...B..... 0030:0040A010 4B E3 42 00 00 00 00 00-B0 01 42 00 00 00 00 00 K.B.......B..... 0030:0040A020 47 F8 42 00 91 00 42 00-20 1D 43 00 39 1B 43 00 G.B...B. .C.9.C. 0030:0040A030 34 19 43 00 88 12 43 00-A6 F7 42 00 30 F2 42 00 4.C...C...B.0.B. 0030:0040A040 3A E8 42 00 C8 E4 42 00-E3 E3 42 00 97 E2 42 00 :.B...B...B...B. 0030:0040A050 0C 00 42 00 FC 00 42 00-64 B0 46 00 BB 92 43 00 ..B...B.d.F...C. 0030:0040A060 C0 BC 46 00 E8 CD 42 00-18 2D 42 00 9F 00 43 00 ..F...B..-B...C. 0030:0040A070 1B 2F F9 BF E8 FF 42 00-4C 10 43 00 31 0E 43 00 ./....B.L.C.1.C. 0030:0040A080 CF 05 43 00 B3 02 43 00-83 F8 42 00 5C 1D 43 00 ..C...C...B.\.C. 0030:0040A090 75 1B 43 00 70 19 43 00-C4 12 43 00 6C F2 42 00 u.C.p.C...C.l.B. 0030:0040A0A0 76 E8 42 00 04 E5 42 00-D3 E2 42 00 38 01 42 00 v.B...B...B.8.B. 0030:0040A0B0 A0 B0 46 00 F7 92 43 00-FC BC 46 00 24 CE 42 00 ..F...C...F.$.B. 0030:0040A0C0 D8 BF FC BF 54 2D 42 00-DB 00 43 00 BF BF FC BF ....T-B...C..... 0030:0040A0D0 88 10 43 00 6D 0E 43 00-0B 06 43 00 EF 02 43 00 ..C.m.C...C...C. 0030:0040A0E0 BF F8 42 00 98 1D 43 00-17 C2 FC BF B1 1B 43 00 ..B...C.......C. 0030:0040A0F0 AC 19 43 00 00 13 43 00-A8 F2 42 00 B2 E8 42 00 ..C...C...B...B. 0030:0040A100 40 E5 42 00 0F E3 42 00-DF C2 FC BF 74 01 42 00 @.B...B.....t.B. 0030:0040A110 00 00 00 00 DC B0 46 00-91 55 F5 BF 33 93 43 00 ......F..U..3.C. 0030:0040A120 38 BD 46 00 60 CE 42 00-3D 57 F5 BF 90 2D 42 00 8.F.`.B.=W...-B. 0030:0040A130 17 01 43 00 C4 10 43 00-A9 0E 43 00 47 06 43 00 ..C...C...C.G.C. 0030:0040A140 2B 03 43 00 FB F8 42 00-8C 55 F5 BF 0D 58 F5 BF +.C...B..U...X.. 0030:0040A150 D4 1D 43 00 ED 1B 43 00-00 00 00 00 EE E8 42 00 ..C...C.......B. 0030:0040A160 7C E5 42 00 00 00 00 00-00 00 00 00 00 00 00 40 |.B............@ If we work backwards from 40a070 the data is consistant with an IAT down to 0040a000 The data is also consistant up to 40a164, where we find the data terminated by ten 00 bytes -------------------- This isn't a lecture on IAT but here is some helpful information; The IAT data is in sections, each separated by 00 00 00 00 Each section represents a set of imports from a .DLL Therefore if routines are being imported from KERNEL32, GDI32 and SHELL32 This would result in an IAT with 3 sections each with 00 00 00 00 separating them, the final import would be terminated with ten 00 bytes -------------------- The IAT base is 40a000 with a length of 164 bytes The RVA (Relative Virtual Address) is 40a000 - 400000 (the Image Base) Lets place SuddenStrike in a holding Pattern, r eip=402F67 A eip jmp eip Hit Esc Hit F5 We are now back in windoze, so start revirgin running; Select the task SuddenStrike, and look at the imports listed in the window on the right hand side. Hmmm no redirections listed, so somebody is lying and it's not revirgin or me ! If you look at the IAT Start RVA revirgin is showing us we can see that it's pointing to the IAT used BEFORE the unwrapping took place. Let us enter the correct value into the fields; IAT Start RVA A000 (Address - Base Address) IAT Length 164 Hit the IAT resolver button, and we see a lot more imports, most of which are marked as redirected. Now hit the Resolve Again button, after a few seconds, you now get all the normal addresses listed. Hit Save resolved, incase of crashes etc. Now we need to create the import table. We need to place this somewhere, looking at the original executable with procdump, the original and now unused IAT looks fine so 761f8 was chosen. I know I should at this point kill the unused .sections and create a nice shiney new one for the IT but it was 03:00 on Sunday morning and there was two beers still left in the fridge. So we place 761F8 into the IT RVA field. Remember to write down the IT length (it was CC for me) Hit IAT Generator button. You will be prompted for filenames, give them sensible filenames, so you don't get them mixed up, I used SSIAT and SSIT (not exactly easy to tell apart) When you have done this You may enter procdump (again), firstly kill the task SuddenStrike. Then you choose Rebuild PE, on the file f:\ssdumped.exe. Here is what the section table of the file is like; VSize = Virtual Size PSize = Physical Size RVA = Relative Virtual Address POffset = Physical Offset Name VSize Psize RVA POffset .text 00009000 00009000 00001000 00001000 .rdata 00002000 00001014 0000a000 0000a000 .data 00003000 00001224 0000c000 0000c000 .rsrc 00002000 00000808 0000f000 0000e000 .icd1 00056000 00064e30 00011000 0000f000 .idata 00001000 00000a10 00076000 00074000 .icd2 00008000 00007338 00077000 00075000 .idata 00001000 00001000 0007f000 0007d000 The VSize is the amount allocated for that section in RAM The PSize is the amount allocated for that section on disk. Usually they contain the same value. In the case of this file they are different in some places. .rdata the size in RAM is 2000 and on disc it is 1014, however the disk file is only padded to the nearest 1000 byte value, which is 2000. This means that the base address of the next section (.rsrc) is different on disk. The base address in RAM is F000 while on disk it is E000 This will affect where we place the contents of the Import Table Normally we 'fix the dump' so as the VOffset and the POffset are the same, unfortunitly this appears to cause errors on this particular target. Now enter hex workshop (or your preferred hex editor), and paste the newly created .bin files into the appropiate locations of ssdumped.exe In the case of SSIAT.BIN go to file offset a000 (remember 40A000 - 400000), highlight 164 bytes and choose Replace with file. As we have chosen 761F8 (IN RAM) as the position for the IT we must look at the section information for .idata, Name VSize Psize RVA POffset .idata 00001000 00000a10 00076000 00074000 .icd2 00008000 00007338 00077000 00075000 .idata 00001000 00001000 0007f000 0007d000 We can see that the 761f8 is in the section .idata, and that .idata will be at offset 76000 in RAM, but at 74000 on disk. This gives us a difference of 2000 bytes, so rather than pasting the new IT at file offset 761f8 we have to subtract 2000 from that file offset, arriving at 741F8 ATTENTION: read the note at the end of this file Starting at 741F8 highlight 1964 bytes (for that is the length of SSIT), and chose Replace with file, and choose SSIT.BIN After saving this change, choose PE Editor in procdump and open ssdumped.exe and go into Directory and change the following fields: Import Table Length 000761F8 000000CC Close Procdump. Double click ssdumped.exe. We should now see a messagebox appearing stating VStart Error Can't load profile .\SudTest.ini This means that the file is working, simply copy ssdumped.exe into the suddenstrike home directory. I have tested the file on 98 & 2K professional without any problems. An added advantage, is that if a full install was chosen, no CD is required in the drive. As I mentioned earlier I should clean up the file so as the .icd1 & .icd2 sections are removed, and that the IT is in a tidy place (like a section of it's own.) With the aid of revirgin the whole process is pretty smooth. Note: ------------------------------------------ I will be honest and say that I would have done things differently now, with the benefit of hindsight. Although the first .idata contained the original import data and the last three sections (four actually) you should not really dump data across sections. I should have deleted at least the last 3 sections (.idata, .icd2, .idata) then created a new section containing the IT created by revirgin. But this was the first rebuild I had done and it was 03:00 and I was sick of looking at Sudden Strike. ------------------------------------------ Thanks to; +Tsehp, for revirgin and answering a couple of dumb questions ArthaXerXes, for the message board R!sc, for the essay on Settlers 3, supplied me with more IAT info than any other source Duke, early copylok info Hi to; All in pGC iNX Regards, Kilby...