Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum  
    BiW ReversingThe challenge is yours    
 Welcome to BiW Reversing
 Thursday, June 01 2023 @ 02:34 PM CEST

SafeDisc 3.20 - 4.00


TutorialsLevel : intermediate

SafeDisc 3.20 - 4.00

If you ever played games in your life, then you for sure know what is SafeDisc. SafeDisc is one of the today most used CD protections. In a previous tutorial we met SafeDisc "small brother" - SafeCast. SafeDisc is product of the same company and it is almost indentical as SafeCast. Main difference is that SafeDisc checks for bad sectors on the CD to be sure that program is running from original one. Bad sectors cannot be copied to another CD. Well it shouldn't be, but programs like Alcohol 120% and similar can do that. It also depends of SafeDisc version. I'm not quite sure about that, copying CDs doesn't interest me. Removing protection layer is objective of this tutorial. For this tutorial you need original CD and Windows XP.

1. Introduction to SafeDisc

Tutorial in rtf format

Target for this tutorial is "Need For Speed Underground 2", protected probably with some version in range from 3.20 up to 4.00. It is not possible to detect exact version because SafeCast above 3.20 have erased version numbers. PEiD fails to detect protection at all, but Protection_ID v5.1f gives solid information:

Scanning -> C:Need for Speed Underground 2speed2.exe
File Type : Exe, Size : 5987981 (05B5E8Dh) Bytes
-> File has 1166989 (011CE8Dh) bytes of appended data starting at offset 0499000h
[!] Safedisc v3.20 - v4.xx or newer [removed version] detected !
[!] removed version is Safedisc v4.00.000 - v4.00.003
[!] Possible CD/DVD-Check String -> Please insert
- Scan Took : 1.938 Seconds

Protection in this version of SafeDisc consits from:

[1] CD check - bad sectors, we have original CD so this is not problem for unpacking.
[2] Debugger checks - easy to defeat, same as in SafeCast.
[3] Import protection - same as in SafeCast, requires little work but it is not hard.
[4] Emulated opcodes - again, same as in SafeCast.
[5] DebugBlocker and nanomites - the hardest part of protection.

2. OEP, debugger checks and SafeDisc debugger

It is very easy to find whre OEP should be. We open protected file in Olly and we can spot OEP jump:

0093309E > 55 PUSH EBP
009330A1 60 PUSHAD
009330A2 BB 9E309300 MOV EBX,speed2.<ModuleEntryPoint>
009330A7 33C9 XOR ECX,ECX
009330A9 8A0D 3D309300 MOV CL,BYTE PTR DS:[93303D]
009330AF 85C9 TEST ECX,ECX
009330B1 74 0C JE SHORT speed2.009330BF
009330B3 B8 13319300 MOV EAX,speed2.00933113
009330B8 2BC3 SUB EAX,EBX
009330BA 83E8 05 SUB EAX,5
009330BD EB 0E JMP SHORT speed2.009330CD
009330BF 51 PUSH ECX
00933146 8B40 04 MOV EAX,DWORD PTR DS:[EAX+4]
00933149 FFD0 CALL EAX
0093314B 58 POP EAX ; kernel32.7C816D4F
0093314C FF35 53319300 PUSH DWORD PTR DS:[933153]
00933152 C3 RETN
00933153 72 16 JB SHORT speed2.0093316B
00933155 61 POPAD
00933156 1360 0D ADC ESP,DWORD PTR DS:[EAX+D]
00933159 -E9 9388E2FF JMP speed2.0075B9F1
0093315E CC INT3
0093315F CC INT3
00933160 81EC E8020000 SUB ESP,2E8
00933166 53 PUSH EBX
00933167 55 PUSH EBP
00933168 56 PUSH ESI
00933169 57 PUSH EDI ; ntdll.7C910738
0093316A 8D4424 60 LEA EAX,DWORD PTR SS:[ESP+60]

At address 00933159 is jump that points in first section. That is OEP jump.

Debugger checks are same as in SafeCast. We can place breakpoint on IsDebuggerPresent and then return to code:

6670D9F0 56 PUSH ESI
6670D9F1 68 80A77A66 PUSH 667AA780
6670D9F6 33F6 XOR ESI,ESI
6670D9F8 E8 72C00000 CALL 66719A6F
6670D9FD 50 PUSH EAX
6670D9FE E8 90C00000 CALL 66719A93
6670DA03 83C4 08 ADD ESP,8
6670DA06 85C0 TEST EAX,EAX
6670DA08 74 1C JE SHORT 6670DA26
6670DA0A FFD0 CALL EAX <------------------------ IsDebuggerPresent call.
6670DA0E 66:85F6 TEST SI,SI
6670DA11 74 13 JE SHORT 6670DA26
6670DA13 E8 6238FFFF CALL 6670127A <------------------- "BadBoy" procedure!
6670DA18 66:8BF0 MOV SI,AX
6670DA1B 66:F7DE NEG SI
6670DA20 46 INC ESI
6670DA21 66:85F6 TEST SI,SI
6670DA24 75 13 JNZ SHORT 6670DA39
6670DA26 8B4424 08 MOV EAX,DWORD PTR SS:[ESP+8]
6670DA2C 81E1 EA894267 AND ECX,674289EA
6670DA34 66:8BC6 MOV AX,SI
6670DA37 5E POP ESI ; 0012FB10
6670DA38 C3 RETN
6670DA39 8B4424 08 MOV EAX,DWORD PTR SS:[ESP+8]
6670DA3F 81E1 119800EF AND ECX,EF009811
6670DA47 66:8BC6 MOV AX,SI
6670DA4A 5E POP ESI ; 0012FB10
6670DA4B C3 RETN

When we break there, we trace into "BadBoy" procedure. When we enter in it, we just place RETN on first opcode inside and all debugger checks are killed. It is already explained in SafeCast tutorial.

SafeDisc debugger, or self-debugging is something new. Protected program will create couple temporyr files in temp older. One of those files (they all have random names) is executable. Main protected file starts that executable and then waits (WaitForSingleObject) signal that is can continue. Temp executable will attach to protected exe , give it signal and continue to debug it. This prevents debugging with Olly or any RING3 debugger. That can be prevented by not executing CreateProcessA , WaitForSingleObject, and performing couple more small changes to prevent crushing.

Here is how OEP can be found with Olly:

- We place breakpoint on OEP jump:

00933159 -E9 9388E2FF JMP speed2.0075B9F1

- Then we place bp on CreateProcessA and break there:

7C802367 > 8BFF MOV EDI,EDI
7C802369 55 PUSH EBP
7C80236C 6A 00 PUSH 0
7C802371 FF75 28 PUSH DWORD PTR SS:[EBP+28]
7C802374 FF75 24 PUSH DWORD PTR SS:[EBP+24]
7C802377 FF75 20 PUSH DWORD PTR SS:[EBP+20]
7C80237D FF75 18 PUSH DWORD PTR SS:[EBP+18]
7C802380 FF75 14 PUSH DWORD PTR SS:[EBP+14]
7C802383 FF75 10 PUSH DWORD PTR SS:[EBP+10]
7C802389 FF75 08 PUSH DWORD PTR SS:[EBP+8]
7C80238C 6A 00 PUSH 0
7C80238E E8 0BB70100 CALL kernel32.CreateProcessInternalA
7C802393 5D POP EBP ; speed2.00935424
7C802394 C2 2800 RETN 28

We don't execute it, instead we set "new origin" at RETN 28. We run and return to main code.

- We must avoid CloseHandle execution after returning from CreateProcessA because process is not created, there is no handle and we would get INVALID_HANDLE exception. So we set new origin below:

00935424 85C0 TEST EAX,EAX
00935426 5D POP EBP
00935427 74 1E JE SHORT speed2.00935447
00935429 8B5424 20 MOV EDX,DWORD PTR SS:[ESP+20]
0093542D 52 PUSH EDX
0093542E FF53 1C CALL DWORD PTR DS:[EBX+1C] ; kernel32.CloseHandle
00935431 8B4424 24 MOV EAX,DWORD PTR SS:[ESP+24]
00935435 50 PUSH EAX
00935436 FF53 1C CALL DWORD PTR DS:[EBX+1C] ; kernel32.CloseHandle
00935439 5F POP EDI
0093543A 5E POP ESI
0093543B 66:B8 0100 MOV AX,1
0093543F 5B POP EBX

00935440 81C4 70060000 ADD ESP,670
00935446 C3 RETN

- Then we place bp at IsDebuggerPresent and we enter in "BadBoy" procedure where we patch first opcode with RETN.

- After that, we can place bp at the end (end because SafeDisc checks some imports for CC bytes) of SetEvent API. That will bring ous very close to WaitForSingleObject part:

667250A8 |> FF75 FC PUSH DWORD PTR SS:[EBP-4] ; /hEvent = 000000A0 (window)
667250AB |. FF15 64407966 CALL DWORD PTR DS:[<&KERNEL32.SetEvent>] ; SetEvent
667250B1 |. 85C0 TEST EAX,EAX <--------------------------- You should be here!!!
667250B3 |. 75 0C JNZ SHORT ~df394b.667250C1
667250B5 |. FFD3 CALL EBX ; ntdll.RtlGetLastWin32Error
667250B7 |. FF75 FC PUSH DWORD PTR SS:[EBP-4]
667250BA |. FFD6 CALL ESI ; kernel32.CloseHandle
667250BC |. E8 8FC7FEFF CALL ~df394b.66711850
667250C1 |> 6A FF PUSH -1 ; /Timeout = INFINITE
667250C3 |. 57 PUSH EDI ; |hObject = 00000098 (window)
667250C4 |. FF15 90407966 CALL DWORD PTR DS:[<&KERNEL32.WaitForSin>; WaitForSingleObject
667250CA |. FF75 FC PUSH DWORD PTR SS:[EBP-4] <-------------- Set origin here to avoid above API.
667250CD |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
667250D0 |. FFD6 CALL ESI ; kernel32.CloseHandle
667250D2 |. 57 PUSH EDI
667250D3 |. FFD6 CALL ESI ; kernel32.CloseHandle
667250D5 |. 837D F8 00 CMP DWORD PTR SS:[EBP-8],0
667250D9 |. 5F POP EDI
667250DA |. 5E POP ESI ; kernel32.CloseHandle
667250DB |. 74 07 JE SHORT ~df394b.667250E4 <-------------- Execute this jump to avoid error detection.
667250DD |. FFD3 CALL EBX ; ntdll.RtlGetLastWin32Error
667250DF |. E8 6CC7FEFF CALL ~df394b.66711850
667250E4 |> 5B POP EBX ; ntdll.RtlGetLastWin32Error
667250E5 |. C9 LEAVE
667250E6 . C3 RETN

- Run and you'll break at OEP jump. Trace in and you will see OEP:

0075B9F1 > 6A 18 PUSH 18
0075B9F3 . 68 98397D00 PUSH speed2.007D3998
0075B9F8 . E8 D3500000 CALL speed2.00760AD0
0075B9FD . BF 94000000 MOV EDI,94
0075BA02 . 8BC7 MOV EAX,EDI ; ntdll.7C910738
0075BA04 . E8 E7070000 CALL speed2.0075C1F0
0075BA09 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
0075BA0E . 893E MOV DWORD PTR DS:[ESI],EDI ; ntdll.7C910738
0075BA10 . 56 PUSH ESI ; /pVersionInformation = FFFFFFFF
0075BA11 . FF15 64317800 CALL DWORD PTR DS:[783164] ; GetVersionExA
0075BA17 . 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+10]

3. Restoring imports

This feature is completly the same as in SafeCast and it is described in previous tutorial. I will not write whole thing again so please check SafeCast tutorial.

First Type:
Import points to some virtual addres where is code that calls FIND_CORRECT_IMPORT algo. When correct import is found, it jumps there. Using algo against itself we can decrypt all imports.

Second Type:
We have jumps that leads outside of main image. It leads to some code and eventualy it jumps to import.

4. Emulated opcodes

Again, same thing as in SafeCast. This JMP EAX jumps outside of code:

004011E3 $ B8 FB0E0000 MOV EAX,0EFB
004011E8 . 59 POP ECX ; kernel32.7C816D4F
004011E9 . 8D0408 LEA EAX,DWORD PTR DS:[EAX+ECX]
004011EE . FFE0 JMP EAX

And that part of code is called from:

00401089 /$ 51 PUSH ECX
0040108A |. 50 PUSH EAX
0040108B |. E8 53010000 CALL speed2.004011E3

And that part is called from different locations:

References in speed2:.text to 00401089
Address Disassembly Comment
005B7869 CALL speed2.00401089
005B788E CALL speed2.00401089
005B78F5 CALL speed2.00401089
005B7921 CALL speed2.00401089
005C08D7 CALL speed2.00401089
005C08EC CALL speed2.00401089
005C0916 CALL speed2.00401089
005C0923 CALL speed2.00401089
005C0945 CALL speed2.00401089
005C0954 CALL speed2.00401089
005CFDBD CALL speed2.00401089
005D2243 CALL speed2.00401089
005D2569 CALL speed2.00401089
0065A71D CALL speed2.00401089
00670CE1 CALL speed2.00401089
006D9D8F CALL speed2.00401089
006D9EF9 CALL speed2.00401089
006D9FE0 CALL speed2.00401089
006F5D87 CALL speed2.00401089
006FEC11 CALL speed2.00401089

JMP EAX will jump to procedure which will emulate "stolen" opcode, but it will write original back so emulation is performed only one time. Simply by executing those calls we will force SafeDisc to restore stolen code. there can be several this JMP EAX routines.I found two in this example.

5. Nanomites

Nanomites are by far the hardest part. Code section of protected program is full of INT 3 opcodes on unusuall places. For example let's check this procedure:

006717FF /$ 8BFF MOV EDI,EDI ; speed2_.00803F90
00671801 |. 55 PUSH EBP
00671802 |. 8BEC MOV EBP,ESP
00671804 |. 51 PUSH ECX ; ntdll.7C91056D
00671805 |. 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
00671809 |. 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
0067180C |. 50 PUSH EAX
0067180D |. 68 94B47A00 PUSH speed2_.007AB494 ; ASCII "SoftwareMicrosoftDirect3D"
00671812 |. 68 02000080 PUSH 80000002
00671817 |. FF15 10307800 CALL DWORD PTR DS:[783010]
0067181D |. CC INT3
0067181E |. CC INT3
0067181F |. CC INT3
00671820 |. CC INT3
00671821 |. 56 PUSH ESI
00671822 |. 8D45 14 LEA EAX,DWORD PTR SS:[EBP+14]
00671825 |. 50 PUSH EAX
00671826 |. FF75 10 PUSH DWORD PTR SS:[EBP+10]
00671829 |. 8D45 10 LEA EAX,DWORD PTR SS:[EBP+10]
0067182C |. 50 PUSH EAX
0067182D |. 6A 00 PUSH 0
0067182F |. FF75 0C PUSH DWORD PTR SS:[EBP+C] ; speed2_.007AB4DC
00671832 |. FF75 FC PUSH DWORD PTR SS:[EBP-4]
00671835 |. FF15 08307800 CALL DWORD PTR DS:[783008]
0067183B |. FF75 FC PUSH DWORD PTR SS:[EBP-4]
0067183E |. 8BF0 MOV ESI,EAX
00671840 |. FF15 14307800 CALL DWORD PTR DS:[783014]
00671846 |. 85F6 TEST ESI,ESI
00671848 |. 5E POP ESI
00671849 |. CC INT3
0067184A |. CC INT3
0067184B |. 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]
0067184E |. 3B45 08 CMP EAX,DWORD PTR SS:[EBP+8]
00671851 |. 75 05 JNZ SHORT speed2_.00671858
00671853 |. 33C0 XOR EAX,EAX
00671855 |. 40 INC EAX
00671856 |. EB 02 JMP SHORT speed2_.0067185A
00671858 |> 33C0 XOR EAX,EAX
0067185A |> C9 LEAVE
0067185B . C2 1000 RETN 10

INT 3 opcode represent one nanomite. When nanmite is executed, it couse exception. SafeDisc debugger takes control, checks what type of exception occured and where, then it emulate that opcode or it writes original opcode there.

To better understand how does this work, we need to debug SafeDisc debugger. With SoftICE it is easy to see what is going on, but if we want to do that with Olly, we need to perform small ritual. OK, so this is how I was able to attach olly to SafeDisc debugger:

- Load main protected file in Olly and break on CreateProcessA. Stop there and wait.

- Go to "C:Documents and SettingsYour_Name_HereLocal SettingsTemp" and open temporary executable in second Olly. Temp exe is hidden with some random name, in my case it is "~e5.0001". Change it's OEP to infinite jump, EB FE. Save changes and close that Olly.

- Execute CreateProcessA (place bp at the end of API). New process is created and it's looping forever. Wait with this olly.

- Open again second Olly and attach to new created process. F9 to run, F12 to pause. Restore oly OEP bytes. Minimize this olly.

- In first Olly, break at IsDebuggerPresent to kill debugger checks by patching "badboy" procedure. After that just run program. Program will wait for second process to attach. It will wait forever (WaitForSingleObject with PUSH -1 parameter) and that is good for us.

- Now here is confusing part: Open THIRD Olly instance, attach to the FIRST one, and detach FIRST OLLY INSTANCE from main proteced executable. Close third olly and that will close first one too, but protected program will be free in memory.

- Now, only one Olly is left and that Olly debugs SafeDisc debugger. From here you can continue on WaitForDebugEvent.

When INT 3 is executed, SD debugger checks where exception occured. Then it emulates that opcode. If same nanomite is executed second time, SD debugger will write (WriteProcessMemory) original opcode to main executable. That is probably to gain on speed. Non-stop emulation would slow down game to the death. Since opcode is second time written, there must be some check for that. Check is very simple, it is one CMP AX,1 and after it JNZ DONT_WRITE. Patching jump forces SD to always write opcode. Now I patched jump and played game within Olly a little. then I minimized, dumped, fixed IAT and code. I started dump and it worked! Game loaded perfect, but it crushed after some time what is expected because most of nanomites are not restored. So how to fix that?

To tell you the truth, I didn't came out with generic solution so I fixed them manually. I attched olly to temp executable and I patched that check CMP AX,1 - JNZ DONT_WRITE. Then I played game a little, tried all kinds of races, tried online game, etc... everything to make sure that I trigered (and by that restored) as much as possible nanomites. Then I dumped code section. After that, I found OEP in instance without SafeDisc debugger and I pasted this dumped code there. Then I fixed imports and stolen opcodes. Now I got second dump from which I could play game. But on exit it would crush. I decide to open dump and check hom many there is nanomites.

But amounth of CC bytes was huge due to reason that executable has tons of CC bytes that are not nanomites, but leftovers from VC++ compiler. For example:

004017C8 . C3 RETN
004017C9 CC INT3
004017CA CC INT3
004017CB CC INT3
004017CC CC INT3
004017CD CC INT3
004017CE 90 NOP
004017CF 90 NOP

005AFD43 . E9 58220000 JMP dumped1.005B1FA0
005AFD48 CC INT3
005AFD49 CC INT3

I wrote script for NOPing those CC bytes, then I found some nanomites and fixed them by examning SafeDisc debugg loop. In SD debugg loop, I would place bp on WaitForDebugEvent, then I would set address in buffer to point on my nanomite. Then in GetThreadContext buffer I would do the same. SafeDisc would decrypt code and I would just copy it. But as said, this is unfinished buisnes. I will have to examne it better or in a updated version of this tutorial, or in a new tutorial with new target (possible higher version of SafeDisc).

I assume that SafeDisc must have some table with all addresses where are nanomites, table with original code, but I didn't had will to trace trough code whole day. I'm already tired.

6. Custom CD check and a final touch

I assume that this is not part of SafeDisc protection. I removed original CD and started dump, but it asked me for CD 2. Breakpoint on GetDriveTypeA got me to CD check procedure:

005BF120 /$ 83EC 0C SUB ESP,0C
005BF123 |. 53 PUSH EBX
005BF124 |. 55 PUSH EBP
005BF125 |. 56 PUSH ESI
005BF126 |. 57 PUSH EDI
005BF127 |. B3 41 MOV BL,41
005BF129 |. BF 01000000 MOV EDI,1
005BF12E |. 885C24 10 MOV BYTE PTR SS:[ESP+10],BL
005BF132 |. FF15 60518F00 CALL DWORD PTR DS:[<&kernel32.GetLogical>; [GetLogicalDrives
005BF138 |. 33ED XOR EBP,EBP
005BF13A |. 68 00040000 PUSH 400
005BF13F |. 894424 18 MOV DWORD PTR SS:[ESP+18],EAX
005BF143 |. 896C24 1C MOV DWORD PTR SS:[ESP+1C],EBP
005BF147 |. E8 5461FBFF CALL dump.005752A0
005BF14C |. 8BF0 MOV ESI,EAX
005BF14E |. 83C4 04 ADD ESP,4
005BF151 |. 3BF5 CMP ESI,EBP
005BF153 |. C605 7F058000 >MOV BYTE PTR DS:[80057F],0
005BF15A |. 0F84 A1000000 JE dump.005BF201
005BF160 |> 857C24 14 /TEST DWORD PTR SS:[ESP+14],EDI
005BF164 |. 74 3E |JE SHORT dump.005BF1A4
005BF166 |. 0FBEC3 |MOVSX EAX,BL
005BF169 |. 50 |PUSH EAX
005BF16A |. 68 D4387A00 |PUSH dump.007A38D4 ; ASCII "%c:"
005BF16F |. 56 |PUSH ESI
005BF170 |. E8 2FD31900 |CALL dump.0075C4A4
005BF175 |. 83C4 0C |ADD ESP,0C
005BF178 |. 56 |PUSH ESI ; /RootPathName = "A:"
005BF179 |. FF15 5C518F00 |CALL DWORD PTR DS:[<&kernel32.GetDriveT>; GetDriveTypeA
005BF17F |. 83F8 05 |CMP EAX,5
005BF182 |. 74 0A |JE SHORT dump.005BF18E
005BF184 |. 8B0D 60DC7900 |MOV ECX,DWORD PTR DS:[79DC60]
005BF18A |. 85C9 |TEST ECX,ECX
005BF18C |. 74 16 |JE SHORT dump.005BF1A4
005BF18E |> 83F8 02 |CMP EAX,2
005BF191 |. 74 11 |JE SHORT dump.005BF1A4
005BF193 |. 8B4C24 10 |MOV ECX,DWORD PTR SS:[ESP+10]
005BF197 |. 51 |PUSH ECX
005BF198 |. E8 03FFFFFF |CALL dump.005BF0A0
005BF19D |. 83C4 04 |ADD ESP,4
005BF1A0 |. 84C0 |TEST AL,AL
005BF1A2 |. 75 14 |JNZ SHORT dump.005BF1B8
005BF1A4 |> FEC3 |INC BL
005BF1A6 |. D1E7 |SHL EDI,1
005BF1A8 |. 45 |INC EBP
005BF1A9 |. 83FD 20 |CMP EBP,20
005BF1AC |. 885C24 10 |MOV BYTE PTR SS:[ESP+10],BL
005BF1B0 |.^7C AE JL SHORT dump.005BF160
005BF1B2 |. 8B7C24 18 MOV EDI,DWORD PTR SS:[ESP+18]
005BF1B6 |. EB 05 JMP SHORT dump.005BF1BD
005BF1B8 |> BF 01000000 MOV EDI,1
005BF1BD |> 56 PUSH ESI
005BF1BE |. E8 FD60FBFF CALL dump.005752C0
005BF1C3 |. 83C4 04 ADD ESP,4
005BF1C8 |. 74 30 JE SHORT dump.005BF1FA
005BF1CA |. 881D 7F058000 MOV BYTE PTR DS:[80057F],BL
005BF1D0 |. 881D B0E88600 MOV BYTE PTR DS:[86E8B0],BL
005BF1D6 |. C605 B1E88600 >MOV BYTE PTR DS:[86E8B1],3A
005BF1DD |. C605 B2E88600 >MOV BYTE PTR DS:[86E8B2],5C
005BF1E4 |. C605 B3E88600 >MOV BYTE PTR DS:[86E8B3],0
005BF1EB |. E8 4085FFFF CALL dump.005B7730
005BF1F0 |. 5F POP EDI
005BF1F1 |. 5E POP ESI
005BF1F2 |. 5D POP EBP
005BF1F3 |. 8AC3 MOV AL,BL
005BF1F5 |. 5B POP EBX
005BF1F6 |. 83C4 0C ADD ESP,0C
005BF1F9 |. C3 RETN
005BF1FA |> C605 7F058000 >MOV BYTE PTR DS:[80057F],0
005BF201 |> 5F POP EDI
005BF202 |. 5E POP ESI
005BF203 |. 5D POP EBP
005BF204 |. 32C0 XOR AL,AL
005BF206 |. 5B POP EBX
005BF207 |. 83C4 0C ADD ESP,0C
005BF20A . C3 RETN

This one is 3 times called. If this procedure returns EAX=0, CD check fails. This checks wants some files on CD etc. We just make some patch that will return EAX=1 and problem is solved.

EA Games usually have couple logo movies that are shown uppon startup. They are very annoying because they cannot be skiped so I patched them too. But that is not part of protection.

7. The end

And that was it. It was not too hard, but again, my dump is not fully rebuilded. Just as much it needs for playing the game. Some nanomites are stil left unresolved but we learned something about SafeDisc protection features.

Btw, here is script for finding OEP:

//======================= START ================================
SafeDisc v4.0 - Kill debug checks and find OEP script

var OEP

find eip,#E9????????CCCC81ECE802000053555657#

find eip,#751A8B4C243C8B5424348B44242C51525350#
bc eip

add eip,1c

gpa "IsDebuggerPresent","kernel32.dll"
bc eip
fill eip,1,0c3

gpa "SetEvent","kernel32.dll"
bphws $RESULT,"x"
bphwc $RESULT

add eip,9

add eip,9

bp OEP
bc eip
//=========================== END ==============================

Greats goes to caki, human, evrybody on BIW reversing, ARTEAM, SnD, CRACKMES.DE, ... and to you :)

See you all


What's Related

Story Options

SafeDisc 3.20 - 4.00 | 9 comments | Create New Account
The following comments are owned by whomever posted them. This site is not responsible for what they say.
SafeDisc 3.20 - 4.00
Authored by: caki on Monday, October 30 2006 @ 10:13 PM CET
Excellent work haggar :) Very nice tutorial, and here are a couple of suggestions to make it even better :)

When getting to the OEP and fixing imports, you make it too complicated to stop the Safedisc debugger from initializing. Simply nop the routine that the WaitForSingleObject is in (or nop the call to it) and you will be able to get to the OEP with the Safedisc debugger not interfering. Also, the nanomites part is a bit incomplete, but a fabulous analysis nonetheless. I wish you luck in finishing up with them :)


SafeDisc 3.20 - 4.00
Authored by: haggar on Tuesday, October 31 2006 @ 10:06 AM CET
Yeah, I need to work more on nanomites and find way to force decrypton. But that will wait for a while, I'm taking rest couple weeks.

See you
 Copyright © 2023 BiW Reversing
 All trademarks and copyrights on this page are owned by their respective owners.
Powered By Geeklog 
Created this page in 0.92 seconds