Breaking SecuROM 7 - A Dissection

This is an archive of an old whitepaper made by three reverse engineering experts on breaking down SecuROM and its multiple layers of protection.

Archive Opening Notes

As mentioned, this is a whitepaper released by ARTeam in 2007/8, revealing how SecuROM 7 can be broken down using reverse engineering techniques. The tutorial is now around 10 years old, so many of the tools used are outdated or hard to find. All of the tools used in this tutorial have been added to the global index, and are stored on this site.

This gives a very interesting look at how the technology was implemented, essentially stitching two executables together, and how to dump and remove the DRM altogether.

Please note that this was written by an external source, ARTeam, and formatted by yours truly for use in a blog, as well as archiving all the images and tools required.

LostFileArchives is not responsible for any repercussions that may
come from using this document, and is simply storing it for archival purposes.

Foreword

After the publication of our first essay on SecuROM I received a lot of interest and replies on this argument, the first essay was contributed by AnonymouS and was just a bit, an insight into SecuROM, a lot of things were still missing, like fixing other anti-debugging and anti-dump methods, fixing redirections and VM. I then organized with deroko, Human and AnonymouS again to write a more complete walkthrough on SecuROM, this time covering all the required issues to successfully own it.
The result is this new Special Issue collecting the contributions of these authors.

This protector is on the scene since time began, and all the games protected with it have already been cracked then it’s time to make these things clearly public so as anyone can understand them.
I am also distributing the tools and scripts used in the tutorials composing this special issue.

Of course I want to thanks authors here for their time and their tools. SecuROM was a protection which unprotection was kept secret for a long time, now no more.

Have phun,
Shub

Disclaimers

All code included with this tutorial is free to use and modify; we only ask that you mention where you found it. This tutorial is also free to distribute in its current unaltered form, with all the included supplements.

Archive note: Supplements for this tutorial can be found in The Index’s cold storage.

All the commercial programs used within this document have been used only for the purpose of demonstrating the theories and methods described. No distribution of patched applications has been done under any media or host. The applications used were most of the times already been patched, and cracked versions were available since a lot of time. ARTeam or the authors of the paper cannot be considered responsible for damages to the companies holding rights on those programs. The scope of this tutorial as well as any other ARTeam tutorial is of sharing knowledge and teaching how to patch applications, how to bypass protections and generally speaking how to improve the RCE art and generally the comprehension of what happens under the hood. We are not releasing any cracked application. We are what we know.

Table of Contents

Some Insights into SecuROM 7.30.0014 by AnonymouS (3)

  1. Forewords (3)
  2. Settings and Target (3)
    2.1. Target (3)
    2.2. Tool Used (3)
  3. Defeating the mysterious debug-detection (4)
  4. Reaching OEP (5)
  5. Defeating the anti-dumping trick. (7)
  6. Conclusion (9)
  7. Final words and greetings (9)

Complete Cracking SecuRom 7.xx by Human (10)

  1. Foreword and needed tools (10)
  2. First step: start of journey (10)
  3. Second step: prepare things (10)
  4. Third Step: Load the game into Olly and rebase (11)
  5. Fourth Step: daemons tools and OEP (13)
  6. Fifth Step: Fixing Anti-dumps (17)
  7. Sixth Step: fixing the CRCChecks (18)
  8. Seventh Step: taking care of antidumps (20)
  9. Conclusions (34)

SecuROM for the masses by deroko (35)

  1. Forewords (35)
  2. Tools and Target (35)
  3. Few words about SecuROM (36)
  4. Dumping SecuROM (37)
  5. Anti-Dump fixing (48)
  6. Conclusion (55)
  7. References (55)
  8. Greetings (55)

Well deep Inside SecuRom by AnonymouS (56)

  1. The funny side of things (56)
  2. Code morphing (56)
  3. Basic API redirection (57)
  4. Code splicing (58)
  5. Advanced API redirections (59)
  6. Virtual Machine (60)

Archive Note: All of these tutorials use extremely old tools. The same effect can be achieved with more modern decompilation programs such as AIDA64 and others.

Some Insights into SecuROM 7.30 by AnonymouS

1. Forewords

It’s been a while since I did any reversing. It hasn’t been much reversing since the release of the X-Prot v2 unpacker. First of all because I’m lazy, but real life also had a lot going on. Anyway, I have been following the SecuRom thread (http://forums.accessroot.com/index.php?showtopic=4361&st=0) on ARTeam’s forum for a while, decided to look deeper into SecuRom 7.

Initially I wanted to code an unpacker and started coding unpackers for SecuRom 7.10 through 7.12. As I began coding on an unpacker for SecuRom V7.18 I found out that the task was quite demanding so I abandoned 7.18 and moved on to 7.30.0014.

This small tutorial/essay is not about completely reversing SecuRom 7.30.0014. It’s just a help for people on how to reach OEP and on the way defeating the anti-debugger trick that apparently stops a lot of people. I will also show I bypass the anti-dump trick used by SecuRom.

The tutorial/essay is not very explaining as I do think that people reading this will be somehow more than just a newbie reverser. SecuRom is a tough protection and good reversing skills are needed in order to fully reverse this protection.

2. Settings and Target

2.1. Target

Resident Evil 4 from Capcom

2.2. Tool Used

OllyDbg V1.10 OllyDbg plugin (HideOD) with the following settings:

securom_backup_1

TaskMngr by drizz (Link to Offline Site)

3. Defeating the mysterious debug-detection

Okay, let’s go to work… Run Olly, select executable and let Olly loose. We hit to exceptions before get this error message:

securom_backup_2

In the earlier versions I used to get this whenever the ZwQueryObject trick was launched, but when I patch this I still get the error message, so I tend to think it’s the mysterious debugger detection people are talking about on the ARTeam thread.

How does it detect us?? We’re using HideOD and ZwQueryObject is not the reason, so this must be new debugger detection. Let’s start tracing… I will spare you for the agonizing of tracing through huge amount for checksums with SecuRom and let you straight to the answer.

securom_backup_3

Notice the CMP BYTE PTR DS:[EAX+4],0 ??? It’s really a part of a much large procedure… Anyway, modify this by setting TRUE back to FALSE (1 to 0).

securom_backup_4

To be honest, I’m not quite sure what exactly happen, but take a look at this clean code, stripped from obfuscation, trap-flag protection and checksums:

0577F556 FFD5             CALL EBP    <-- call RtlAcquirePebLock 
056D89E3 8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]  <-- [EBP+8] == 30h
056D89E6 64:8B00       MOV EAX,DWORD PTR FS:[EAX]  <-- Get PEB address
0577FAA2 8B40 0C       MOV EAX,DWORD PTR DS:[EAX+C]  <-- PPEB_LDR_DATA
0577FDC9 8078 04 00    CMP BYTE PTR DS:[EAX+4],0  <-- TRUE if debugged
0577FDD3 75 12         JNZ SHORT game.0577FDE7  <-- jump bad boy 

From Microsoft’s library I came across this:

securom_backup_5

From what I can see the flag at [EAX+4] is somehow switched on when debugged. I tried coding a little test myself but I always came up with a TRUE result even if I was not running a debugger !?!

4. Reaching OEP

Fire up Olly and run through all exceptions, including fixing the anti-debug trick, until you reach:

securom_backup_6

Then set a conditional BPX on VirtualProtect and run it.

securom_backup_7

Once Olly breaks clear BPX and suspend all threads except the main one.

securom_backup_8

Now set a breakpoint on ReleaseMutex and let Olly run until it breaks again. CTRL+F9 will lead you to the RETN in ReleaseMutex. Trace back into user-code and you will end up here:

securom_backup_9

Now search for the pattern: C9 87 3C 24

securom_backup_10

Be aware there are more offsets that match this pattern but the first one found should be the “good” one. If this is not the case, you better start tracing ;) Anyway should look something like this:

securom_backup_11

Place a Hardware breakpoint on the instruction after the LEAVE opcode and run Olly. When Olly breaks go to Memory Map and place a breakpoint code section using F2 and run Olly.

securom_backup_12

Next time Olly breaks we’re at OEP.

securom_backup_13

Now we can dump…. Or can we ???

5. Defeating the anti-dumping trick.

As one can see, reaching the OEP of SecuRom 7.30.0014 is fairly easy. However, the authors did another attempt to slow us down. When we fire up our PE-dumper we get this message:

securom_backup_14

Hmm… What happens here ?? Our dumper won’t dump ?!? Don’t worry… This is easily defeated … Luckily for you I did all the tracing through SecuRom’s checksum hell. Let’s rewind time a little bit ;)

As I encountered this I decided to find out where exactly made this anti-dump trick possible. I let the executable run until the first exception and tried to dump. Here I also got the error message… So, this means that the anti-dump trick is setup before the first exception. Now I simple started the slow process of tracing through tons of checksums. First I encountered this:

securom_backup_15

This let me to the thought that SecuRom for some strange reason GUARD PAGE the .securom section. So I set at breakpoint on VirtualProtect and after several hits I ended here:

securom_backup_16

I then retraced my way back to the user code (CTRL-F9) and ended here:

securom_backup_17

Now we know what to do when reach the OEP, so let’s fast forward to the OEP and open up the Memory Map and set the access back to FULL ACCESS on the .securom section:

securom_backup_18

Now we can finally dump the executable without any problems.

securom_backup_19

6. Conclusion

As one can see it is fairly easy to reach the OEP of SecuRom 7.30.0014. It’s also possible to dump the executable once we set back the page access to FULL. As for the mysterious debugger detection, we found it and is able to fix the problem. However, reaching OEP and being able to dump the executable is not enough to defeating SecuRom. The dump is filled with VM code, code-splicings etc. etc. I only showed the way to the promised land, it’s now up to you to work your way through it yourself ;)

7. Final words and greetings

As stated in the forewords this is not a complete tutorial on how to reverse SecuRom, but merely a quick step-by-step on how to reach the OEP of version 7.30.0014. I must admit that I haven’t looked into the VM and code-splicing of this version. I initially kind of promised that I would code another unpacker but I doubt that I will ever code any again. Basically I haven’t got the time anymore and the credits you get from your many hours of work are minimal. However, if my good friend, Nacho DJ, talk me into it I might do some more tutorials ;)

Last but not least I would like to send out my greetings to:

  • My wife, Kristine and my son Frederik
  • All ARTeam member (especially Nacho DJ)
  • Drizz
  • All I forgot

Sincerely, AnonymouS / ARTeam

Complete Cracking SecuROM 7 by Human

1. Foreword and needed tools

Hello and Welcome to my tut about cracking securom 7.xx (something around 7.30)

What we need..

  • Target: Resident evil 4 or Biohazard 4 ISO
  • Resident evil 4 1.1 patch
  • Resident evil 4 maxi image (included)

    Archiver’s Note: Maxi image not included, as it’s against copyright law.

    Tools:

    • Winhex
    • Cff Explorer
    • Ollydbg 1.10
    • Ollydump plugin
    • My oepfind:) newest ofcourse.

    Archiver’s Note: OEPFind is not required, but can be used.

And my scripts:

  • Securom 7.x Cpuid Fixer (included into this distribution)
  • Securom 7.x CRC Check Fixer (included into this distribution)
  • Securom 7.x Jump Bridge & Crypted Code Fixer (included into this distribution)

    Archiver’s Note: Find the above files in Cold Storage under the name “SecuROM Writeup Required Files”.

    Note: this tutorial is more like unpacking tutorial and not a deep analyze why I do things, I spent a lot of time analyzing this, so if you want understand better.

Do it alone, check how secuRom uses those. With this tut it would be easier for you.

2. First step: start of journey

Lets start with installing game.

3. Second step: prepare things

Install patch(if you have biohazard you still can install patch after some steps)

First insert Reg file with path to your biohazard 4

REGEDIT4 [HKEY_LOCAL_MACHINE\SOFTWARE\CAPCOM\resident evil 4] "PATH"="e:\\Games\\biohazard 4\\" 

Next lets load patch into ollydbg and patch its complain about no game to update :P

securom_backup_20

You see 00407E4B change it to JMP SHORT upd_pal1.00407EAB and patch will start patching us to 1.1 and securom protected (well it takes a while)

securom_backup_21

4. Third Step: Load the game into Olly and rebase

Lets load game.exe into olly. Press alt+M to see memory.
As you can see d3dx9_30.dll blocks our dump to have linear regions :(

securom_backup_22

So we have to use rebaser from Dr.Golova to fix it to our needs (dont worry All Works after it) rebase it to 20000000h

securom_backup_23

Lets reload game.exe and voila All is fine All range after exe till 20000000h is free:)

securom_backup_24

5. Fourth Step: daemons tools and OEP

  1. Lets load maxi image into daemon tools 4.10

  2. Run Yasu to cloak virtual drives

Archiver’s Note: Don’t bother using YASU, just use SecuROM Loader 1.2.

  1. Set break point on CreateProcessInternalA and run, after break you will see on stack param to use with oepfind, so lets use it.

  2. For me command to run In Total commander is like this:

    oep game.exe /Sonydadc /05f0612d /05f0612d /220792E1 /1

Last param is time in ms, antidebug that spawns another instance of exe and kills parent if difference is too high from current GetTickCount. But due we patch GetTickCount to count slower in rate of +1, we use /1 instead of original time

With oepfind we use these settings:

  • kill low Alloc so it will not Alloc Any memory under imagebase
  • hook virtualalloc to Alloc memory linear not random
  • GetTickCount +1 to disable spawn of another proper process

    securom_backup_25

Now Press detach to look for OEP. After about 10 seconds we have:

securom_backup_26

Fine so Press yes or Any key In your language. Run Olly and attach to our game.exe. Remember PID due we will need it later, mine is here 75C

Don’t run just in menu select view/threads and double click that one suspended.

securom_backup_27

Like you can see we are AT OEP :)

Press OK In oepfind to remove infinite jump. But dont close it yet!!

Again we must Press alt+M and change .securom section axx rights to full so right click on it and do Set Access/Full Access Now it’s time to dump memory regions I know it’s a lot of memory but I’m too lazy to code proper memory manager. As you can see our memory starts at 0x6130000

securom_backup_28

And ends at 94CF000+1000 so its 94D0000 (last region that doesn’t show Any file name, here last before d3dx9_30.dll). Let put those into oepfind to Fields that are now not grayed out.

securom_backup_29

Press dump and in your dir you’ll find a file named DUMP_06130000-094D0000 of 54mb, with all the dumped range. Now you can close oepfind.

6. Fifth Step: Fixing Anti-dumps

Now we are ready to dump exe, but before that we must do one thing. Another securom protection is left. Securom uses also EP as antidump. So when ollydump change now EP to OEP we will be screwed. Securom adds return params from many Apis to address. So when for example original PID is 200 and dumped program’s PID is 100, we have 200-100+address gets wrong data and end with a page fault. So it always should be 0, when both are same.

Ok, then back to our EP, what to do? Simply move the header somewhere else. Select all from 400140 till 400300 do binary copy and binary paste it to 4001C0

Then we change 40 to C0 at 40003C to point to a new place.

securom_backup_30

Now we can dump our exe with unticked rebuild imports.

As you can see exe is 97Mb, biohazard exe without securom is Just 6Mb, that’s how protections hurt customers, not my fault then.

Next step will be to fix imports and add our regions. So let fire up CFF Explorer (I patched mine due I was pissed with asking should I load more than fucked 20MB, what a dumb question today, when minimum memory is 2GB).

Click section headers and right click to do “Add Section (file data)” to add our dumped regions.

Fix virtual address with start address-imagebase 6130000400000=5D30000 and change section rights to E0000020

securom_backup_31

Do right click, rebuild image size, header and save exe.

Now fix imports, well we don’t need imprec all we need is into the .idata section. Run Winhex with game.exe and press alt+G now go to 66b000 can you see those addresses? So copy those till API names.

Close it and now run Winhex on dump.exe, press alt+G and go to 53E8000, now Ctrl+B to paste our IAT, that now as you can see is not RVA, only 7Cxxxxxx. Save the exe and rebuild from the beginning. After the rebuild we will again paste IAT due from Olly we will dump exe again with API addresses changed from RVA to 7Cxxxxxx.

7. Sixth Step: fixing the CRCChecks

Till now we dumped the exe, fixed the IAT and the PE header, and added missing file regions. We can start with fixing rest.

We start with fixing CRCchecks, for this we will use my Securom 7.x CRC Check Fixer.txt script (included in goodies folder of this distribution). CRCchecks comes in 2 flavours: memory and register. Memory CRC updates always some address [esp+xx] and later uses it, other type updates one of registers eax,ebx,ecx etc.

So what this script does? It searches for patterns of CRCcheck and sets an HW breakpoint, after calculation loop it replaces everything with nops and paste there just calculated value that will be then hardcoded.

securom_backup_32

Like you can see 057EE02D ADD DWORD PTR SS:[ESP+14],EDI. This is the place of our fix. (I noticed that, and really don’t know why, odbgscript runs faster when I have some some movie in background, open but stopped).

It takes a lot of time even on my Intel C2D E6700 (with a movie stopped it runs 4x faster :P). Anyway the homework this script does is really huge, it has to fix more than 100.000 locations. The works is done into a temporary memory buffer: CRCchecks calc CRCs using their own native loops, after this the temp buffer is copied back into sections substituting the CrCCheck just executed. Minimizing Olly window also speeds up because Windows doesn’t redraw all.

A result of the script is shown here:

securom_backup_33

Like you can see in above figure, ESP+14 updates always to 164E, and log shows script took 724983ms so 12 minutes :P

Don’t alter script, it will not be faster, I already optimized it to max. Even direct write of opcodes as bytes is faster than assembling new instruction. CRCcheck pattern is in 2 sections, so both those are In script.

  1. Seventh Step: taking care of antidumps

Now its time to take care of antidump APIs. In this version securom uses the following:

  • GetCurrentProcessId: every process gets PID but to make it work, dump needs to return PID as original process
  • GetVersion: every windows have own version, so to make it work on vista but dumped on xp we need to return xp version
  • CPUID: every CPU have own ID returned in eax & edx, so to make it work on other CPUs it has to return the ID stored in the dump, ours then.
  • ResetEvent: every securom exe creates event or events, dump doesn’t have them so we need to return 1
  • GetComputerNameA: every pc has a name, so other pc must match what we store into the dump
  • GetUserNameA: every user logged has a name, other user logged and it crashes, so same applies here
  • RtlGetLastWin32Error: here again we need to return 1, in case there are errors we tell there aren’t any
  • GetSystemInfo: every pc has own 20 bytes System info table returned, again it must match our

Knowing all this we can start patching securom. For this we need a place, I choose 9BFF00. And that’s how it will look:

009BFF00    B8 88030000             MOV EAX,388 
009BFF05    034424 04               ADD EAX,DWORD PTR SS:[ESP+4]                   ; ntdll.7C910738 
009BFF09    C2 0400                 RETN 4 
009BFF0C    B8 0501280A             MOV EAX,0A280105 
009BFF11    2B4424 04               SUB EAX,DWORD PTR SS:[ESP+4]                   ; ntdll.7C910738 
009BFF15    C2 0400                 RETN 4 
009BFF18    B8 D6060000             MOV EAX,6D6 
009BFF1D    334424 04               XOR EAX,DWORD PTR SS:[ESP+4]                   ; ntdll.7C910738 
009BFF21    C2 0400                 RETN 4 
009BFF24    A1 88BAD705             MOV EAX,DWORD PTR DS:[5D7BA88] 
009BFF29    C2 0400                 RETN 4 009BFF2C    8B4424 04               MOV EAX,DWORD PTR SS:[ESP+4]                   ; ntdll.7C910738 
009BFF30    8B0D 98BAD705           MOV ECX,DWORD PTR DS:[5D7BA98] 
009BFF36    03C1                    ADD EAX,ECX 
009BFF38    35 A416827C             XOR EAX,7C8216A4 
009BFF3D    C2 0400                 RETN 4 

/

009BFF40    A1 F4BAD705             MOV EAX,DWORD PTR DS:[5D7BAF4] 
009BFF45    8B0D 04BBD705           MOV ECX,DWORD PTR DS:[5D7BB04] 
009BFF4B    03C1                    ADD EAX,ECX 
009BFF4D    0305 ECBAD705           ADD EAX,DWORD PTR DS:[5D7BAEC] 
009BFF53    C2 0400                 RETN 4 
009BFF56    B8 E0F7CB85             MOV EAX,85CBF7E0 
009BFF5B    C2 0400                 RETN 4 
009BFF5E    E8 00000000             CALL b.009BFF63 
009BFF63    5E                      POP ESI                                        ; kernel32.7C816FD7 
009BFF64    83C6 19                 ADD ESI,19 
009BFF67    57                      PUSH EDI                                       ; ntdll.7C910738 
009BFF68    8B7C24 08               MOV EDI,DWORD PTR SS:[ESP+8] 
009BFF6C    B9 24000000             MOV ECX,24 
009BFF71    F3:A4                   REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] 009BFF73    BE 10000000             MOV ESI,10 
009BFF78    5F                      POP EDI                                        ; kernel32.7C816FD7 
009BFF79    C2 0400                 RETN 4

Here you have binary copy of the patch you can paste in binary format:

B8 74 05 00 00 03 44 24 04 C2 04 00 B8 05 01 28 0A 2B 44 24 04 C2 04 00 B8 D6 06 00 00 33 44 24 04 C2 04 00 A1 88 BA D7 05 C2 04 00 8B 44 24 04 8B 0D 98 BA D7 05 03 C1 35 A4 16 82 7C C2 04 00 A1 F4 BA D7 05 8B 0D 04 BB D7 05 03 C1 03 05 EC BA D7 05 C2 04 00 B8 CF F7 4B 87 C2 04 00 E8 00 00 00 00 5E 83 C6 19 57 8B 7C 24 08 B9 24 00 00 00 F3 A4 BE 10 00 00 00 5F C2 04 00 00 00 00 00 00 10 00 00 00 00 01 00 FF FF FE 7F 03 00 00 00 02 00 00 00 4A 02 00 00 00 00 01 00 06 00 06 0F 

Now I will explain why it looks like it. First of all go to address 0x5A61A7B

securom_backup_34

Lets see what’s inside CALL DWORD PTR DS:[5EA4634]?

05A66760    FF15 7CBAD705           CALL DWORD PTR DS:[5D7BA7C]             ; kernel32.GetCurrentProcessId 
05A66766    034424 04               ADD EAX,DWORD PTR SS:[ESP+4]            ; ntdll.7C910738 
05A6676A    C2 0400                 RETN 4 

This code wants our PID, and later adds value pushed on stack to it. That’s why we should have here:

MOV EAX,75C                       ; is my PID of securom process that I dumped ADD EAX,DWORD PTR SS:[ESP+4] RETN 4 

Now get back out of it. Do you see this?

05A61A7A    50                      PUSH EAX 
05A61A7B    FF15 3446EA05           CALL DWORD PTR DS:[5EA4634]                 ; b.05A66760 
05A61A81    35 6067A605             XOR EAX,5A66760 
05A61A86    3305 3446EA05           XOR EAX,DWORD PTR DS:[5EA4634]              ; b.05A66760 

Tricky, it does a XOR on EAX, with the address of this small code part and then does again a XOR with the address of small code part that is held in [5EA4634] so we can’t change address for our patch. But hey just change 2nd XOR to same as 1st one, 2 chained XORs with same value will give us 0 so EAX will not change. Same trick we will do on other below, and under [5EA4634] we will now put 9BFF00 for our patch.

One done few more to go.

What have we in 05A61A92 CALL DWORD PTR DS:[5EA4638] ?

Oh its:

05A66770    FF15 80BAD705           CALL DWORD PTR DS:[5D7BA80]                 ; kernel32.GetVersion 
05A66776    2B4424 04               SUB EAX,DWORD PTR SS:[ESP+4]                ; ntdll.7C910738 
05A6677A    C2 0400                 RETN 4 

So replacing it with:

009BFF0C    B8 0501280A             MOV EAX,0A280105                            ; my xp version 009BFF11    2B4424 04               SUB EAX,DWORD PTR SS:[ESP+4]                ; ntdll.7C910738 
009BFF15    C2 0400                 RETN 4 

We will defeat that part too.

Next

05A61AA9    FF15 3C46EA05           CALL DWORD PTR DS:[5EA463C]                 ; b.05A66780 
05A66780    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A66784    892C24                  MOV DWORD PTR SS:[ESP],EBP 
05A66787    8BEC                    MOV EBP,ESP 
05A66789    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A6678D    890C24                  MOV DWORD PTR SS:[ESP],ECX 
05A66790    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A66794    891C24                  MOV DWORD PTR SS:[ESP],EBX 
05A66797    8B0D 2046EA05           MOV ECX,DWORD PTR DS:[5EA4620] 
05A6679D    85C9                    TEST ECX,ECX 
05A6679F    7E 16                   JLE SHORT b.05A667B7 
05A667A1    A1 B4BAD705             MOV EAX,DWORD PTR DS:[5D7BAB4] 
05A667A6    3345 08                 XOR EAX,DWORD PTR SS:[EBP+8]                ; b.<ModuleEntryPoint> 
05A667A9    2B0D 2446EA05           SUB ECX,DWORD PTR DS:[5EA4624] 
05A667AF    890D 2046EA05           MOV DWORD PTR DS:[5EA4620],ECX 
05A667B5    EB 41                   JMP SHORT b.05A667F8 
05A667B7    D13D 2446EA05           SAR DWORD PTR DS:[5EA4624],1 
05A667BD    53                      PUSH EBX 
05A667BE    51                      PUSH ECX 
05A667BF    52                      PUSH EDX                                    ; ntdll.KiFastSystemCallRet 05A667C0    B8 01000000             MOV EAX,1 
05A667C5    0FA2                    CPUID 
05A667C7    5A                      POP EDX                                     ; kernel32.7C816FD7 
05A667C8    59                      POP ECX                                     ; kernel32.7C816FD7 
05A667C9    5B                      POP EBX                                     ; kernel32.7C816FD7 
05A667CA    83E0 DF                 AND EAX,FFFFFFDF 

/

05A667CD    A3 B4BAD705             MOV DWORD PTR DS:[5D7BAB4],EAX 
05A667D2    3345 08                 XOR EAX,DWORD PTR SS:[EBP+8]                ; b.<ModuleEntryPoint> 
05A667D5    8945 FC                 MOV DWORD PTR SS:[EBP-4],EAX 
05A667D8    833D 2446EA05 00        CMP DWORD PTR DS:[5EA4624],0 
05A667DF    8B45 FC                 MOV EAX,DWORD PTR SS:[EBP-4] 
05A667E2    C705 2046EA05 00000100  MOV DWORD PTR DS:[5EA4620],10000            ; UNICODE "=D:=D:\" 
05A667EC    75 0A                   JNZ SHORT b.05A667F8 
05A667EE    C705 2446EA05 01000000  MOV DWORD PTR DS:[5EA4624],1 
05A667F8    5B                      POP EBX                                     ; kernel32.7C816FD7 
05A667F9    C9                      LEAVE 
05A667FA    C2 0400                 RETN 4 

Oh it’s the CPUID!!!

And as you can see it does on it:

05A667CA    83E0 DF                 AND EAX,FFFFFFDF 
05A667D2    3345 08                 XOR EAX,DWORD PTR SS:[EBP+8] 

So proper patch for my CPU, a little optimized, is:

009BFF18    B8 D6060000             MOV EAX,6D6 
009BFF1D    334424 04               XOR EAX,DWORD PTR SS:[ESP+4]                ; ntdll.7C910738 
009BFF21    C2 0400                 RETN 4 

Why not esp+8? Why I don’t push and pop ebx?

Next

05A61AC0    FF15 4046EA05           CALL DWORD PTR DS:[5EA4640]                 ; b.05A66800 
05A66800    A1 90BAD705             MOV EAX,DWORD PTR DS:[5D7BA90] 
05A66805    3B05 2846EA05           CMP EAX,DWORD PTR DS:[5EA4628] 
05A6680B    7C 2C                   JL SHORT b.05A66839 
05A6680D    FF35 88BAD705           PUSH DWORD PTR DS:[5D7BA88] 
05A66813    FF15 84BAD705           CALL DWORD PTR DS:[5D7BA84]                 ; kernel32.ResetEvent 
05A66819    85C0                    TEST EAX,EAX 
05A6681B    75 22                   JNZ SHORT b.05A6683F 
05A6681D    2105 90BAD705           AND DWORD PTR DS:[5D7BA90],EAX 05A66823    813D 2846EA05 00001000  CMP DWORD PTR DS:[5EA4628],100000 
05A6682D    7D 06                   JGE SHORT b.05A66835 
05A6682F    D125 2846EA05           SHL DWORD PTR DS:[5EA4628],1 
05A66835    33C0                    XOR EAX,EAX 
05A66837    EB 0B                   JMP SHORT b.05A66844 
05A66839    FF05 90BAD705           INC DWORD PTR DS:[5D7BA90] 
05A6683F    A1 88BAD705             MOV EAX,DWORD PTR DS:[5D7BA88] 
05A66844    C2 0400                 RETN 4 

And all we need of this is:

009BFF24    A1 88BAD705             MOV EAX,DWORD PTR DS:[5D7BA88] 
009BFF29    C2 0400                 RETN 4 

Next

05A61AE5    FF15 4446EA05           CALL DWORD PTR DS:[5EA4644]                 ; b.05A66850 
05A66850    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A66854    EB 00                   JMP SHORT b.05A66856 
05A66856    893424                  MOV DWORD PTR SS:[ESP],ESI 
05A66859    A1 94BAD705             MOV EAX,DWORD PTR DS:[5D7BA94] 
05A6685E    3B05 2C46EA05           CMP EAX,DWORD PTR DS:[5EA462C] 
05A66864    7C 70                   JL SHORT b.05A668D6 
05A66866    BE D4BAD705             MOV ESI,b.05D7BAD4 
05A6686B    56                      PUSH ESI 
05A6686C    FF15 B8BAD705           CALL DWORD PTR DS:[5D7BAB8]               ; ntdll.RtlEnterCriticalSection 
05A66872    68 98BAD705             PUSH b.05D7BA98 
05A66877    68 C4BAD705             PUSH b.05D7BAC4                             ; ASCII "HUMAN" 
05A6687C    C705 98BAD705 10000000  MOV DWORD PTR DS:[5D7BA98],10 
05A66886    FF15 C0BAD705           CALL DWORD PTR DS:[5D7BAC0]                 ; kernel32.GetComputerNameA 
05A6688C    85C0                    TEST EAX,EAX 
05A6688E    75 24                   JNZ SHORT b.05A668B4 
05A66890    813D 2C46EA05 00100000  CMP DWORD PTR DS:[5EA462C],1000 
05A6689A    7D 06                   JGE SHORT b.05A668A2 

/

05A6689C    D125 2C46EA05           SHL DWORD PTR DS:[5EA462C],1 05A668A2    8325 94BAD705 00        AND DWORD PTR DS:[5D7BA94],0 
05A668A9    56                      PUSH ESI 
05A668AA    FF15 BCBAD705           CALL DWORD PTR DS:[5D7BABC]               ; ntdll.RtlLeaveCriticalSection 
05A668B0    33C0                    XOR EAX,EAX 
05A668B2    EB 3A                   JMP SHORT b.05A668EE 
05A668B4    56                      PUSH ESI 
05A668B5    FF15 BCBAD705           CALL DWORD PTR DS:[5D7BABC]               ; ntdll.RtlLeaveCriticalSection 
05A668BB    8325 94BAD705 00        AND DWORD PTR DS:[5D7BA94],0 
05A668C2    813D 2C46EA05 00100000  CMP DWORD PTR DS:[5EA462C],1000 
05A668CC    7D 0E                   JGE SHORT b.05A668DC 
05A668CE    D125 2C46EA05           SHL DWORD PTR DS:[5EA462C],1 
05A668D4    EB 06                   JMP SHORT b.05A668DC 
05A668D6    FF05 94BAD705           INC DWORD PTR DS:[5D7BA94] 
05A668DC    8B4424 08               MOV EAX,DWORD PTR SS:[ESP+8] 
05A668E0    8B0D 98BAD705           MOV ECX,DWORD PTR DS:[5D7BA98] 
05A668E6    03C1                    ADD EAX,ECX 
05A668E8    3305 C0BAD705           XOR EAX,DWORD PTR DS:[5D7BAC0]              ; kernel32.GetComputerNameA 
05A668EE    5E                      POP ESI                                     ; kernel32.7C816FD7 
05A668EF    C2 0400                 RETN 4 

And proper patch of this code will be? Well do you know already?

009BFF2C    8B4424 04               MOV EAX,DWORD PTR SS:[ESP+4]                ; ntdll.7C910738 
009BFF30    8B0D 98BAD705           MOV ECX,DWORD PTR DS:[5D7BA98] 
009BFF36    03C1                    ADD EAX,ECX 
009BFF38    35 A416827C             XOR EAX,7C8216A4 
009BFF3D    C2 0400                 RETN 4 

Again no esp+8 no need to push pop esi. Well now you wonder why XOR EAX,7C8216A4

Look closer and you will see it XOR with address of GetComputerNameA, so for me address of this api is 7C8216A4

Next

05A61AF9    FF15 4846EA05           CALL DWORD PTR DS:[5EA4648]                 ; b.05A66900 

Here as you will follow you will see nothing is conditional or memory dependent:

08AF0000    E8 00000000             CALL b.08AF0005 
08AF0005    58     POP EAX                                     ; kernel32.7C816FD7 
08AF0006    05 AA4EA605             ADD EAX,b.05A64EAA 
08AF000B    2D AF4EA605             SUB EAX,b.05A64EAF 
08AF0010    C3                 RETN

So we leave it as it is

Next

05A61B1E    FF15 4C46EA05           CALL DWORD PTR DS:[5EA464C]                 ; b.05A66910 
05A66910    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A66914    892C24                  MOV DWORD PTR SS:[ESP],EBP 
05A66917    8BEC                    MOV EBP,ESP 
05A66919    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A6691D    890C24                  MOV DWORD PTR SS:[ESP],ECX 
05A66920    8D6424 FC               LEA ESP,DWORD PTR SS:[ESP-4] 
05A66924    90                      NOP 
05A66925    893424                  MOV DWORD PTR SS:[ESP],ESI 
05A66928    A1 68BAD705             MOV EAX,DWORD PTR DS:[5D7BA68] 
05A6692D    3B05 3046EA05           CMP EAX,DWORD PTR DS:[5EA4630] 
05A66933    7C 6B                   JL SHORT b.05A669A0 
05A66935    BE F0BBD705             MOV ESI,b.05D7BBF0 
05A6693A    56                      PUSH ESI 
05A6693B    FF15 08BCD705           CALL DWORD PTR DS:[5D7BC08]               ; ntdll.RtlEnterCriticalSection 
05A66941    8D45 FC                 LEA EAX,DWORD PTR SS:[EBP-4] 
05A66944    50                      PUSH EAX 
05A66945    68 ECBAD705             PUSH b.05D7BAEC                             ; ASCII "Human" 
05A6694A    C745 FC 01010000        MOV DWORD PTR SS:[EBP-4],101 
05A66951    FF15 6CBAD705           CALL DWORD PTR DS:[5D7BA6C]                 ; ADVAPI32.GetUserNameA 
05A66957    85C0                    TEST EAX,EAX 
05A66959    75 23                   JNZ SHORT b.05A6697E 
05A6695B    2105 68BAD705           AND DWORD PTR DS:[5D7BA68],EAX 
05A66961    813D 3046EA05 00100000  CMP DWORD PTR DS:[5EA4630],1000 

/

05A6696B    7D 06                   JGE SHORT b.05A66973 
05A6696D    D125 3046EA05           SHL DWORD PTR DS:[5EA4630],1 
05A66973    56                      PUSH ESI 
05A66974    FF15 0CBCD705           CALL DWORD PTR DS:[5D7BC0C]               ; ntdll.RtlLeaveCriticalSection 
05A6697A    33C0                    XOR EAX,EAX 
05A6697C    EB 3B                   JMP SHORT b.05A669B9 
05A6697E    56                      PUSH ESI 
05A6697F    FF15 0CBCD705           CALL DWORD PTR DS:[5D7BC0C]               ; ntdll.RtlLeaveCriticalSection 
05A66985    8325 68BAD705 00        AND DWORD PTR DS:[5D7BA68],0 
05A6698C    813D 3046EA05 00100000  CMP DWORD PTR DS:[5EA4630],1000 
05A66996    7D 0E                   JGE SHORT b.05A669A6 
05A66998    D125 3046EA05           SHL DWORD PTR DS:[5EA4630],1 
05A6699E    EB 06                   JMP SHORT b.05A669A6 
05A669A0    FF05 68BAD705           INC DWORD PTR DS:[5D7BA68] 
05A669A6    A1 F4BAD705             MOV EAX,DWORD PTR DS:[5D7BAF4] 
05A669AB    8B0D 04BBD705           MOV ECX,DWORD PTR DS:[5D7BB04] 
05A669B1    03C1                    ADD EAX,ECX 
05A669B3    0305 ECBAD705           ADD EAX,DWORD PTR DS:[5D7BAEC] 
05A669B9    5E                      POP ESI                                      ; kernel32.7C816FD7 
05A669BA    C9                      LEAVE 
05A669BB    C2 0400                 RETN 4 

So proper patch is:

009BFF40    A1 F4BAD705             MOV EAX,DWORD PTR DS:[5D7BAF4] 
009BFF45    8B0D 04BBD705           MOV ECX,DWORD PTR DS:[5D7BB04] 
009BFF4B    03C1                    ADD EAX,ECX 
009BFF4D    0305 ECBAD705           ADD EAX,DWORD PTR DS:[5D7BAEC] 
009BFF53    C2 0400                 RETN 4 

And finally last one is

05A61B32    FF15 5046EA05           CALL DWORD PTR DS:[5EA4650]                 ; b.05A669C0 
 05A669C0    8B15 74BAD705           MOV EDX,DWORD PTR DS:[5D7BA74]              ; ADVAPI32.77DC0000 
 05A669C6    66:813A 4D5A            CMP WORD PTR DS:[EDX],5A4D 
 05A669CB    75 0C                   JNZ SHORT b.05A669D9 
 05A669CD    A1 78BAD705             MOV EAX,DWORD PTR DS:[5D7BA78] 
 05A669D2    66:8138 4D5A            CMP WORD PTR DS:[EAX],5A4D 
 05A669D7    74 04                   JE SHORT b.05A669DD 
 05A669D9    33C0                    XOR EAX,EAX 
 05A669DB    EB 38                   JMP SHORT b.05A66A15 
 05A669DD    8B48 3C                 MOV ECX,DWORD PTR DS:[EAX+3C] 
 05A669E0    03C8                    ADD ECX,EAX 
 05A669E2    8B42 3C                 MOV EAX,DWORD PTR DS:[EDX+3C] 
 05A669E5    03D0                    ADD EDX,EAX 
 05A669E7    8B42 58                 MOV EAX,DWORD PTR DS:[EDX+58] 
 05A669EA    0B42 28                 OR EAX,DWORD PTR DS:[EDX+28] 
 05A669ED    56                      PUSH ESI 
 05A669EE    0B42 08                 OR EAX,DWORD PTR DS:[EDX+8] 
 05A669F1    8B71 58                 MOV ESI,DWORD PTR DS:[ECX+58] 
 05A669F4    0B71 28                 OR ESI,DWORD PTR DS:[ECX+28] 
 05A669F7    0B71 08                 OR ESI,DWORD PTR DS:[ECX+8] 
 05A669FA    03C6                    ADD EAX,ESI 
 05A669FC    0FB772 42               MOVZX ESI,WORD PTR DS:[EDX+42] 
 05A66A00    0FB752 40               MOVZX EDX,WORD PTR DS:[EDX+40] 
 05A66A04    03C6                    ADD EAX,ESI 
 05A66A06    03C2                    ADD EAX,EDX                                 ; ntdll.KiFastSystemCallRet 05A66A08    0FB751 42               MOVZX EDX,WORD PTR DS:[ECX+42] 
 05A66A0C    0FB749 40               MOVZX ECX,WORD PTR DS:[ECX+40] 
 05A66A10    03C2                    ADD EAX,EDX                                 ; ntdll.KiFastSystemCallRet 
 05A66A12    03C1                    ADD EAX,ECX 
 05A66A14    5E                      POP ESI                                     ; kernel32.7C816FD7 
 05A66A15    C2 0400                 RETN 4 

What it does? Well calculates CRC of advapi32.dll, with other dll version, language all will be wrong. So, the proper patch will be just to return the value of EAXx, just before returning. For me it is:

009BFF56    B8 E0F7CB85             MOV EAX,85CBF7E0 
009BFF5B    C2 0400                 RETN 4 

Now final move is update address table of antidumps to our patches so at 5EA4634 we will binary paste:

00 FF 9B 00 0C FF 9B 00 18 FF 9B 00 24 FF 9B 00 2C FF 9B 00 00 69 A6 05 40 FF 9B 00 56 FF 9B 00 

It’s better to not touch addresses and opcodes due securom also uses those as encryption, when it calculates some value in EAX or other register it likes to do ROL with value of some memory.

For example from

05A61B32    FF15 
5046EA05           CALL DWORD PTR DS:[5EA4650]   

it can do:

ROL EAX,[05A61B34] 

so it does ROL EAX, 50 when we change address in that call then encryption is screwed.

It’s like a mine field or small CRCs, you must really watch out what you change even with CRCchecks fixed.

Next step is:

05ADC7D5    E8 18440000             CALL a.05AE0BF2 
05ADC7DA    8B48 14                 MOV ECX,DWORD PTR DS:[EAX+14] 
05ADC7DD    69C9 FD430300           IMUL ECX,ECX,343FD 
05ADC7E3    81C1 C39E2600           ADD ECX,269EC3 
05ADC7E9    8948 14                 MOV DWORD PTR DS:[EAX+14],ECX 

And inside 5AE0BF2 we have:

05AE0BF2    53                      PUSH EBX 
05AE0BF3    56                      PUSH ESI 
05AE0BF4    FF15 D8990306           CALL DWORD PTR DS:[60399D8]                 ; ntdll.RtlGetLastWin32Error 
05AE0BFA    FF35 7473F405           PUSH DWORD PTR DS:[5F47374] 
05AE0C00    8BD8                    MOV EBX,EAX 
05AE0C02    FF15 2CC3D705           CALL DWORD PTR DS:[5D7C32C]                 ; kernel32.TlsGetValue 
05AE0C08    8BF0                    MOV ESI,EAX 
05AE0C0A    85F6                    TEST ESI,ESI 
05AE0C0C    75 49                   JNZ SHORT b.05AE0C57 
05AE0C0E    68 8C000000             PUSH 8C 05AE0C13    6A 01                   PUSH 1 
05AE0C15    E8 E7D0FFFF             CALL b.05ADDD01 
05AE0C1A    8BF0                    MOV ESI,EAX 
05AE0C1C    85F6                    TEST ESI,ESI 
05AE0C1E    59                      POP ECX                                     ; kernel32.7C816FD7 
05AE0C1F    59                      POP ECX                                     ; kernel32.7C816FD7 
05AE0C20    74 2D                   JE SHORT b.05AE0C4F 
05AE0C22    56                      PUSH ESI 
05AE0C23    FF35 7473F405           PUSH DWORD PTR DS:[5F47374]
05AE0C29    FF15 30C3D705           CALL DWORD PTR DS:[5D7C330]                 ; kernel32.TlsSetValue 
05AE0C2F    85C0                    TEST EAX,EAX 
05AE0C31    74 1C                   JE SHORT b.05AE0C4F 
05AE0C33    C746 54 2877F405        MOV DWORD PTR DS:[ESI+54],b.05F47728 
05AE0C3A    C746 14 01000000        MOV DWORD PTR DS:[ESI+14],1 
05AE0C41    FF15 BC9A0306           CALL DWORD PTR DS:[6039ABC]                 ; kernel32.GetCurrentThreadId 
05AE0C47    834E 04 FF              OR DWORD PTR DS:[ESI+4],FFFFFFFF 
05AE0C4B    8906                    MOV DWORD PTR DS:[ESI],EAX 
05AE0C4D    EB 08                   JMP SHORT b.05AE0C57
05AE0C4F    6A 10                   PUSH 10 
05AE0C51    E8 0DCAFFFF             CALL b.05ADD663 
05AE0C56    59                      POP ECX                                          ; kernel32.7C816FD7 
05AE0C57    53                      PUSH EBX 
05AE0C58    FF15 209B0306           CALL DWORD PTR DS:[6039B20]                ; ntdll.RtlSetLastWin32Error 
05AE0C5E    8BC6                    MOV EAX,ESI 05AE0C60    5E                      POP ESI                                          ; kernel32.7C816FD7 
05AE0C61    5B                      POP EBX                                          ; kernel32.7C816FD7 
05AE0C62    C3                      RETN 

So as I said we must return 1, assemble just there a MOV EAX,1 and RET and all is fine.

You can now ask why not change call to 05AE0BF2 into MOV EAX,1 it’s also 5 bytes? Well, because I bet that E8 from call is used as ROL in some place. I have already seen that in older securom when I tried to fix E8 call that leads to some winapi to point to my IAT table jump.

Well its not over yet with RtlGetLastWin32Error! Why?

Lets look at:

05ADC7DA    8B48 14                 MOV ECX,DWORD PTR DS:[EAX+14] 
05ADC7E9    8948 14                 MOV DWORD PTR DS:[EAX+14],ECX 

When we return 1 and add 14 then we have 15 (lol I’m so good at math :P, but I still don’t know how much is 2*2 :P) and when those two execute then we get page fault :( So only possible way is to NOP those two and all is fine.

Next step is:

05BB8F15    E8 2CB0C7FF             CALL a.05833F46 
05BB8F1A    83FE 04                 CMP ESI,4 
05BB8F1D    7C 05                   JL SHORT a.05BB8F24 

And 5833F46 after many instructions leads to:

05833FA6    68 EC55FD05             PUSH b.05FD55EC                             ; ASCII "GetSystemInfo" 05833FAB    68 1454FD05             PUSH b.05FD5414                             ; ASCII "KERNEL32.dll" 
05833FB0    FF15 A09A0306           CALL DWORD PTR DS:[6039AA0]                 ; kernel32.GetModuleHandleA 
05833FB6    50                      PUSH EAX 
05833FB7    FF15 5C9A0306           CALL DWORD PTR DS:[6039A5C]                 ; b.05813282 
05833FBD    A3 E066D505             MOV DWORD PTR DS:[5D566E0],EAX 
05833FC2    EB 05                   JMP SHORT b.05833FC9 

With GetProcAddress and call so only possible solution is to copy those 20 bytes, paste them at 9BFF7A, change that call to point into our patch and it looks like it:

009BFF5E    E8 00000000             CALL b.009BFF63 
009BFF63    5E                      POP ESI                                     ; kernel32.7C816FD7 
009BFF64    83C6 19                 ADD ESI,19 
009BFF67    57                      PUSH EDI                                    ; ntdll.7C910738 
009BFF68    8B7C24 08               MOV EDI,DWORD PTR SS:[ESP+8] 
009BFF6C    B9 24000000             MOV ECX,24 
009BFF71    F3:A4                   REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] 009BFF73    BE 10000000             MOV ESI,10 
009BFF78    5F                      POP EDI                                     ; kernel32.7C816FD7 
009BFF79    C2 0400                 RETN 4 

And finally again at

05BBD15E    FF15 A49A0306           CALL DWORD PTR DS:[6039AA4] 

This one can be replaced with

MOV EAX, 75C    ;my PID

And we are almost home. When you now run exe it will work, but only on your machine, but goal is to make it run on any other.

Now after we patched all antidump apis run this script: Securom 7.x Jump Bridge & Crypted Code Fixer.txt (included into goodies folder).

Do you see it? And where it goes?

securom_backup_35

What here script does it will set HWBP on write into that DWORD when securom will update it with new address. That’s how it looks now:

securom_backup_36

Well after write it looks like it:

securom_backup_37
And destination looks like it:
securom_backup_38

Well at this point we could finish and go to next jump bridge, but as you can see I have in script now HWBP on execute. It’s not needed here, just to make loop universal for crypted code, instead of code splicing or virtualized code by securom it can lead to crypted code. That we must execute on our machine to decrypt it, due inside there is crypted CPUID check and will not uncrypt on other CPU just crash. So let analyze another jump bridge:

004DDD50  - FF25 54E40606           JMP DWORD PTR DS:[606E454]                  ; b.06073A10 

That leads us to:

securom_backup_39

You see that call? Its call to decrypted code at and after address of that call, so after execute and HWBP on execute on 4DDD50 we get:

Nice uncrypted code that will stay same after we again dump and run on any other machine, due no more checks there. Run whole script to achive that. Again 54890ms (only asm can make it faster)

And now last part to fix is CPUID checks that can be in spliced code.

securom_backup_40

Run Securom 7.x Cpuid Fixer.txt to fix all those (included into the goodies folder of this tutorials original package):

securom_backup_41

What script does is to search for CPUID and checks if there is an instruction like “and eax,FFFFFFDF”, just after the CPUID also there can be EB jump to “and eax,FFFFFFDF”, so we must also handle it. Simplest way to fix it is to replace “and eax,FFFFFFDF” with a “mov eax,6D6” that is my CPUID after and’ing. Script gets your CPUID automatically so you don’t need to modify it.

  1. Conclusions

Finally we are at end and can now dump and again fix IAT with Winhex. It should run now on any pc we like. Of course you can dump just once at end, but I did this few times due its better for me to write this tut and get pictures of code so I could paste them here. I hope you enjoyed it and learned something.

Best regards to: all scene competition, all people that make scene alive, ARTeam, exetools, SND and unpack dot cn.

Human/MiNT

SecuROM for the Masses by deroko

1. Forewords

SecuROM is a famous protection used by many games nowadays. Funny thing is that it’s cracking doesn’t take too much time, only takes time when you are doing it for the first time, after that it goes flowless. There aren’t tutorials about SecuROM in the public as far as I’m aware. First tutorial which deals with SecuROM 7.xx was submitted to ARTeam by AnonymouS author, so I think we should write more about it, just for fun…

2. Tools and Target

Target that we will be using is Command & Conquer : Tiberium Wars v1.0, so you will need to get that DVD to follow this tut.

Archive Note: The method of inspecting the ASS is the same here as for other SecuROM 7.xx games.

Well any SecuROM will work as approach is kinda generic.

Tools:

  • SoftICE
  • Olly only for kewl screenshots…
  • IDA
  • Asm/C compiler

Also make sure that you have original DVD as game developers deserve money for their work…

To run this game you will have to copy cnc3game.dat from RetailExe\1.0 to folder where is located cnc3.exe and simply type:

cnc3game.dat –win –config  CNC3_english_1.0.SkuDef 

-win is for windowed mode

Of course, you don’t have to do this while you are unpacking protection, it is only required when you are testing your dump, otherwise you will get this message if you run dump without that command line:

securom_backup_42

Similar output you will get when your dumped file is started without proper command line.

3. Few words about SecuROM

SecuROM protection consists of one .exe attached to original .exe. If you trace a little bit trough SecuROM layer you will see 2nd exe being appended, even standards MSVC initialization routine are held in this upper layer. If you want you may dump at this layer and analyze securom protection, but that is not very important for us atm.

SecuROM uses a lot of buffers to make itself anti-dump which are always allocated on different base addresses which leads us to simple conclusion that some kind of random generator is used to allocate those buffers. Also SecuROM uses several anti-dump tricks such as GetCurrentProcessId, OpenEventA/CloseHandle, ResetEvent, TlsSet/GetValue, entrypoint address, imagesize from PE header etc… which we can patch of course.

It allocates a lots of buffers to execute its code and when dumped those buffers take ~40mb. Luckily when compressed they take around 1MB or less, as all redirections are allocated at 64K boundary but only 1KB is commited. This is result of calling VirtualAlloc a lots of times (try making infinite loop with VirtualAlloc and watch what happens). Thus 64K-1K is padded with zeros in your dump and giving really really good compression ratio. Just for comparison, I have dump of 70MB compressed back to 7MB, well, almost like virgin file  Well I’ll also discuss about making this dump smaller : virgin + ~20mb.

Before we even think to fire up SoftICE with SecuROM protection we have to hide it properly, well, my softice is hidden it this way:

- NtCreateFile - Int1/int3/int41 patched - NtQuerySystemInformation - UnhandledExceptionFilter 

One more way remains to detect SoftICE and that’s to query it’s service and check if it is active, for this I use hook of OpenServiceA to eliminate NTICE service opening. This hook code and other stuff used to log SecuROM execution may be found in hookdll.c. Also SoftICE activity can be detected with EnumServicesStatusExA which is used by ActiveMARK but that’s another story.

4. Dumping SecuROM

There are a few ways to dump SecuROM at the OEP:

  • Use method described by anonymous author [see first chapter of this document]
  • Find vm_exit, hook it, and wait when it jumps back to 1st section
  • Hook commonly used APIs at MSVC oep and watch when those are called from 1st section
  • Just dump .exe, load it in IDA, and apply MSVC signatures, then search for OEP
  • Use PAGE_GUARD to find when code section jmp to oep

Well there are several ways as you may see, but it is upto you to find method which fits your needs the best.

OEP in this target is located at: 0x40A1B3

.text:0040A1B3                 call    ___security_init_cookie .text:0040A1B8                 jmp     ___tmainCRTStartup 

.text:00409EF2 ___tmainCRTStartup: .text:00409EF2                 push    58h 
.text:00409EF4                 push    offset unk_B95678 
.text:00409EF9                 call    sub_40A250 
.text:00409EFE                 xor     ebx, ebx 
.text:00409F00                 mov     [ebp-1Ch], ebx 
.text:00409F03                 mov     [ebp-4], ebx 
.text:00409F06                 lea     eax, [ebp-68h] 
.text:00409F09                 push    eax 
.text:00409F0A                 call    ds:GetStartupInfoA 
.text:00409F10                 mov     dword ptr [ebp-4], 0FFFFFFFEh 
.text:00409F17                 mov     dword ptr [ebp-4], 1 
.text:00409F1E                 mov     eax, large fs:18h 
.text:00409F24                 mov     esi, [eax+4] 
.text:00409F27                 mov     edi, offset unk_C5DD54 

When dumping SecuROM you have to know that it’s PE header in memory is actually PE header of a virgin file (except AddressOfEntryPoint is foobared and used as anti-dump on several places).

As we know this fact we may write dumper for SecuROM and dump virgin file to the disk. You may see sromd.asm for detailed code (nothing special just read PE header from memory and dump image to disk + add extra section for SecuROM sections).

SecuROM uses jmp to execute some stolen procedures which are mixed with SecuROM code. To find such procedure you won’t have to search much, first call in OEP leads us to SecuROM code:

securom_backup_43

This jmp only first time will take you to SecuROM virtual buffers:

securom_backup_44

If you keep traceing trough it you will eventually end up here:

securom_backup_45

Now look, it will take you to stolen and mixed procedure here:

securom_backup_46

You may see how this procedure is mixed with addresses from .securom section, and some jccs are leading to that section. If we take one step back and look again at the jmp from which we have started to trace, we may see that SecuROM wrote to jmp dword ptr[] correct address:

securom_backup_47

Note address in 2nd column, it is same as address of stolen procedure. There are at least 2 reasons why this is done in such way:

  1. Numerous execution of same procedure would slow down game execution
  2. Make code anti-dump as you may not simply dump it without fixing those jmps

Those jmp dword ptr[] can be easily found by using byte search, thus, you will either have to write your own tool, or use olly scripts to do the job for you. Before we even dump file, we have to fix those jmps, and for that I use srom_logger.exe and vmtrace.dll.

Srom_logger.exe is very simple search engine which searches for jmp dword ptr[] and checks if they are leading us to .securom section. If such jmp is found, loader is injected into remote process which will be responsible for loading vmtrace.dll and calling it’s export vmtrace!tracer.

Let’s analyze those codes a little bit so you may know how and why I’m doing what:

loader:                                          call    __delta __delta:                pop     ebp                         sub     ebp, offset __delta                         

                    x_push  ecx, <vmtrace.dll~>                         call    [ebp+loadlibrarya], esp                         x_pop                                                  x_push  ecx, <tracer~>                         call    [ebp+getprocaddress], eax, esp                         x_pop                                                        push    0deadc0deh trace_addr              =       $-4                         call    eax                         mov     [ebp+redirection], eax                         call    [ebp+exitthread], 0 

loadlibrarya            dd      ? getprocaddress          dd      ? exitthread              dd      ? redirection             dd      ? size_loader             =       $-loader 

tracer export from vmtrace.dll is very simple and it’s job is to set HWBP on write at address from jmp dword ptr[]:

tracer                  proc                         arg     trace_address                         pusha                         mov     eax, trace_address                         mov     global_trace_address, eax                                                  xor     eax, eax                         push    offset setdr0                         push    dword ptr fs:[eax]                         mov     dword ptr fs:[eax], esp                         mov     eax, [eax]                         pop     dword ptr fs:[eax]                         add     esp, 4                                                                         call    hook_kiuser                         mov     save_esp, esp                         mov     eax, trace_address                         jmp     [eax]                          __baby:                 call    unhook_kiuser                         mov     eax, destination                                                  mov     [esp.Pushad_eax], eax                         popa                         leave                       retn    4 

Also you may see in vmtrace.asm code responsible for hooking KiUserExceptionDispatcher, which will perform stealth tracing from context of our target.

Now simply execute that address and we are going to nonintrusive tracer:

kiuser_hook:            mov     ecx, [esp+4]                         mov     ebx, [esp]                         pusha                         cmp     dword ptr[ebx], EXCEPTION_SINGLE_STEP                         je      __checkdrx                                      xor     eax, eax                         mov     [ecx.context_dr6], eax                         mov     [ecx.context_dr0], eax                         mov     [ecx.context_dr1], eax                         mov     destination, eax                         mov     [ecx.context_eip], offset __baby                         mov     eax, save_esp                         mov     [ecx.context_esp], eax                         jmp     __allgood                          __checkdrx:             test    [ecx.context_dr6], 4000h        ;single steping...                         jnz     __goback                                                 test    [ecx.context_dr6], 1                                                     jnz     __write                                       test    [ecx.context_dr6], 2                         jz      __goback        ;dr1 hit...                         xor     eax, eax                         mov     [ecx.context_dr6], eax                         mov     [ecx.context_dr0], eax                         mov     [ecx.context_dr1], eax                         mov     eax, save_esp                         mov     [ecx.context_esp], eax                         mov     eax, offset __baby                         xchg    [ecx.context_eip], eax                         mov     destination, eax                         jmp     __allgood                        


                    ;dr0 hit... __write:                mov     esi, ecx                         call    ReadProcessMemory, -1, global_trace_address, o destination, 4, 0                         call    ReadProcessMemory, -1, destination, o old_data, 4, 0                         test    eax, eax                         jz      __goback                                                                                          mov     eax, destination                         mov     [esi.context_dr1], eax                         or      [esi.context_dr7], 4                                     __allgood:              popa                         call    NtContinue, ecx, 1                         nop                         nop 


__goback:               popa                         jmp     old_kiuser 

You would probably wonder why I am using ReadProcessMemory to read address from my own process. Well trick is simple, as I used HWBP on r/w each read from r3 would cause HWBP to generate exception, even when you are reading from exception handler, now as code is rewritten to use HWBP on write this is not needed anymore. Also you may see that I’m setting HWBP on execution on final destination. This is done to avoid wrong identification of destination when SecuROM writes 2 times to this address. Whenever write there occurs we take that address and HWBP on execution on that address. When dr1 is hit (execution HWBP) we know we have good pointer, and we log it.

Also it will occur that procedures are in virtual memory so we dump that too to the disk which will allow us to fix them easily. Also srom_logger.asm will produce log file which will look something like this:

redirection at 0x00401023 to 0x013D34C3 
redirection at 0x00401098 to 0x013F11F8 
redirection at 0x004010C5 to 0x013F66A5 
redirection at 0x00401103 to 0x00434D33 
redirection at 0x00401141 to 0x0044D2F1 
redirection at 0x0040116F to 0x013EA3CF 
redirection at 0x004011AD to 0x013F2BBD 

There is 2957 those jmps so we need to automate process of fixing. Also regions with procedures are dumped to the disk for later fixing (again in another tool named sfixer.asm). Note that after you run srom_logger.asm you will have fixed jmp dword ptr[] in your target in memory so this is the point when you dump it to the disk with fixed jmps.

Here is an example of one stolen procedure stored somewhere in virtual memory:

seg000:03A5000B                 push    esi 
seg000:03A5000C                 mov     esi, [esp+8] 
seg000:03A50010                 jmp     short loc_3A5002A 
seg000:03A50012 
seg000:03A50012 loc_3A50012: 
seg000:03A50012                 mov     ecx, esi 
seg000:03A50014                 push    offset loc_3A50027 
seg000:03A50019                 push    7FE20Fh 
seg000:03A5001E                 retn 
seg000:03A5001F                 dd 0DF8A7B4Ah 
seg000:03A50023                 dd 0E5B9FAh 
seg000:03A50027 
seg000:03A50027 loc_3A50027: 
seg000:03A50027                 add     esi, 10h 
seg000:03A5002A
seg000:03A5002A loc_3A5002A: seg000:03A5002A                 cmp     esi, [esp+0Ch] 
seg000:03A5002E                 jnz     short loc_3A50012 
seg000:03A50030                 pop     esi 
seg000:03A50031                 retn 

You can’t put it back to its original place as SecuROM uses that space in code section for other anti-dump tricks.

We are almost close to dumping this target.

Next thing we should know is where are all virtual buffers that we have to dump and append. For this purpose I use hookdll.c which can be used with my Ultimate Hooking Engine [1]. Also it is recommended to use hook of rdtsc to avoid randomness in memory allocation.

Oki, inject hookdll.dll into target by typing: hook cnc3game.dat, and after a few seconds you will be greeted with MessageBoxA similar to this one on the picture:

securom_backup_48

Write down those memory addresses, and press ok. Your target will be in the infinite loop in hook of GetTickCount. Do not attach Olly yet!!! Run srom_logger.exe and you will get output similar to this one :

securom_backup_49

Oki doki, we have produced 0xXXXXXXXX.dmp files and splices.bin + we have fixed jmp dword ptr[] in protected program. Now you may attach olly, or simply ctrl+d if you are using SoftICE and you will be here:

securom_backup_50

What I have done here is to check for return address in GetTickCount, and if it is called from certain location I put target into infinite loop, this giving me possibility to always break at OEP fast. You should nop out this jmp $ but do NOT run target yet, as you will need to get also TlsValue with index 0xF which is used as antidump at one point. Simply assemble this code:

securom_backup_51

The reason why I’m doing this at this moment is to save you some time, you could easily figure this thing later on, and then you will have to dump all over again. Not letting you know at this point about this trick would be mean. Also you have to write down PID of this process!!!!

Now, you may see that in PE header there is valid virgin header, so you may dump it like that. I use my dumper for SecuROM which dumps that pe header from memory and image as it was before packing, and appends to it other memory occupied by SecuROM.

After running sromd.asm you will have dumped_securom.exe which will look like this:

securom_backup_52

So far so good. You wrote down addresses displayed in MessageBoxA? If not go all over again.

Now take a closer look at spliced files dumped by srom_logger.exe:

Splices start:

securom_backup_53

Splices end:

securom_backup_54

By looking at splices we may see that those are allocated on 0x10000 boundary, but there is one small gap between all those splices located here:

securom_backup_55

Look closer and you will see that we are missing memory region between 45D0000 and 4960000. So we will have to dump that region too.

So far we have 2 regions which have to be dumped:

1480000 – 37F0000 and 45D0000 – 4960000, dump them, and use CFF explorer by Daniel Pistelli to add those regions to dumped_securom.exe. For me it looks like this:

securom_backup_56

One more thing left to go over, and that’s to add splices, with sfixer.exe . Before you run sfixer.exe you should save splices.bin to, for example, splices_save.bin as sfixer.exe will modify this file, and if something goes wrong you won’t have this file, also you should know that sfixer.exe assumes that you are fixing file “final.exe” so copy updated dumped_securom.exe to “final.exe”. Note also that this step is NOT required,

you could just dump memory from heap1 to highmem from MessageBoxA but your dump would be + ~20mb. In this way, with splices fixing we are reducing dump size + we are making nicer dump.

After running sfixer.asm we may check some of our virtual memory redirections from log_redirection.exe (produced by srom_logger.asm):

redirection at 0x00410BC1 to 0x03B20001   <--- from log_redirections.txt 

securom_backup_57

And fixed splice:

securom_backup_58

I’ll also show you two splices which weren’t fixed by my tool correctly, and which I have fixed by hand (from protected game in memory):

securom_backup_59
From my dump:

securom_backup_60

And when fixed by hand it should look like:

securom_backup_61

And second splice:

securom_backup_62
And when fixed:

securom_backup_63

As you may see in this picture, splice is fixed, but before fixing this call was causing crash as whole splice was rebased to the new address.

Theoretically speaking sfixer.asm can be rewritten to follow execution flow and rebuild those procedures in better way. We may see 3 patterns used as call:

push   <ret_splice_address> push   <proc_address> ret 

push   <ret_splice_address> jmp    __proc_address 

push   <ret_splice_address> push   dword ptr[API_pointer_from_IAT> ret 

And those are only patterns which are fixed by sfixer.asm, of course, better engine could be written, but, this is good enough 

Voila, dumping is done now. All we have to do is to get rid of anti-dump tricks present in the SecuROM, and we will have dumped and fixed game.

5. Anti-Dump fixing

This is part where your head might start hurting a little bit, so my advice is to grab pen and paper and write your observation. Well at least that’s how I do with all protectors.

First anti-dump that you will see is when you enter into SecuROM vm interpreter. I don’t want to trouble you mutch so here is address to be hit first:

.bla:00CBEC00 sub_CBEC00      proc near                
.bla:00CBEC00                 jmp     ds:dword_125D9EC 
.bla:00CBEC00 sub_CBEC00      endp 

Depending on your dump address to which this jmp is leading could be different, but for me it is at 3320000:

seg000:03320345                 mov     eax, large fs:18h 
seg000:0332034B                 inc     ebp 
seg000:0332034C                 db      3Eh 
seg000:0332034C                 mov     eax, [eax+30h] 
seg000:03320350                 db      3Eh 
seg000:03320350                 mov     eax, [eax+8] 
seg000:03320354                 db      3Eh 
seg000:03320354                 mov     edx, [eax+3Ch] 
seg000:03320358                 dec     ebp 
seg000:0332035A                 db      3Eh 
seg000:0332035A                   mov     edx, [edx+eax+50h] 

Here it takes OptionalHeader.SizeOfImage if you dump your target it will have different image size from the one that SecuROM expects to be there. So you will have to fix it, it is, 87A000, well simply dump target from memory with LordPE and you will see correct values.

Ok you need to patch it… simply patch mov edx, [edx+eax+50h] with mod edx, 87A000, and first anti-dump is defeated. Next anti dump is CPUID trick  which makes dump only CPU specific eg. It won’t work on other CPUs if you don’t fix it:

seg000:03320305                 mov     eax, 1 
seg000:0332030A                 push    ebx 
seg000:0332030B                 add     ebp, eax 
seg000:0332030D                 cpuid 
seg000:0332030F                   and     eax, 0FFFFFFDFh 

This is relatively simple to fix, what you will do is to search trough dumped vm regions for certain byte patern: mov eax. 1. When you find it, now use lde to check if cpuid is present in next 5 instructions, if you find jmp __ follow it. When you find cpuid you know what to fix. Use again lde from cpuid offset to find and eax, 0FFFFFFDFh and patch it with mov eax, value which is returned on your CPU.

This search’n’replace is easy to write, so you may exercise a little bit.

There are 2 more anti-dumps in VM interpreter. GetCurrentProcessId and OpenEventA/CloseHandle. Trick here is to patch GetCurrentProcessId call to return PID of your dump, and OpenEventA/CloseHandle to return 1 for CloseHandle:

seg000:028400F1                 mov     edx, [edx] 
seg000:028400F3                 xor     edx, 0CC5C4764h 
seg000:028400F9                 call    edx  <-- GetCurrentProcessId 
seg000:028400FB                 and     ebp, edx 
seg000:028400FD                 mov     edx, eax 
seg000:028400FF                 sub     edx, ecx 
seg000:02840101                 btc     ebp, eax 
seg000:02840104                 pop     ecx 

So it should look something like this when patched:

securom_backup_64

OpenEventA/CloseHandle:

seg000:028600DB                 mov     edx, [edx] 
seg000:028600DD                 xor     edx, 0BA6258DBh 
seg000:028600E3                 call    sub_286010A 
seg000:028600E8 a01a3ee67056205 db '01A3EE67056205C94339FAF75B158E1CD',0 
seg000:0286010A 
seg000:0286010A sub_286010A     proc near                
seg000:0286010A 
seg000:0286010A                 btc     ebp, eax 
seg000:0286010D                 push    0 
seg000:02860112                 mov     ebp, 8A9FAD20h 
seg000:02860117                 push    2 
seg000:0286011C                 call    edx  <--- OpenEvent 
seg000:0286011E                 inc     ebp 
seg000:02860120                 push    eax 
seg000:02860121                 xadd    ebp, ebp 
seg000:02860124                 mov     edx, [ebx+28h] 
seg000:02860127                 add     edx, 14h 
seg000:0286012D                 xchg    ebp, ebp 
seg000:0286012F                 mov     edx, [edx] 
seg000:02860131                 xor     edx, 0AC86A5A7h 
seg000:02860137                 xor     ebp, 0C9C0E01Dh 
seg000:0286013D                 call    edx  <--- CloseHandle 

You should patch this part in a smart way, first you have to patch push 2/call edx with add esp, 8 to eliminate 2nd and 3rd arguments passed to OpenEvent, now you will have to patch push eax (event handle with nop) and xor edx, 0AC86A5A7h with mov eax,1 and nop call edx (CloseHandle).

Good, good, VM handlers are now anti-dump patched.

Let’s proceed to another anti-dump in code of SecuROM:

securom_backup_65

Hmmm what is what here? Lets start by going to each one of them and seeing what is going on:

1st:

securom_backup_66

This is GetCurrentProcessId check, which you will have to patch with mov eax, dump_pid

2nd:

securom_backup_67

Check for OS version via GetVersion, so you patch it with mov eax, version_of_your_os

3rd check:

securom_backup_68

Well pretty much self explanatory, patch it with value which cpuid returns on your machine

4th check:

securom_backup_69

Patch this as mov eax, 1, and nop out call to ResetEvent

5th check:

securom_backup_70

Hey, there is my computer name, nice. So SecuROM here checks for length of your computer name. Check line 0xFA678F and 2 lines after it is set to 0x10, which is maximum size of buffer passed to GetComputerNameA. Then at line 0xFA67BE it takes length of computer name returned by call to GetComputerName, so your patch here would be to nop out EnterCriticalSection and LeaveCriticalSection, also nop out call to GetComputerNameA and nop passing arguments to it, and assemble there mov [12F5A2C], len_of_your_computer_name , and nop je at 0xFA67AB. Easy…

6th check:

securom_backup_71

And buffer:

securom_backup_72

This buffer will simply return it’s address, so above check you should patch with: mov eax, 4650000

7th check:

securom_backup_73

That’s my user name over there: Weeeeeee. Well at line 0xFA6843 it takes 4 chars from name and adds them to EAX, this is very simple to patch so you can do it on your own. Not much stuff going on.

In simple words, if you run dump as different user, it will crash at some point.

And last and 8th check:

securom_backup_74

Very simple anti-dump check, it loads address of advapi32.dll in edx, and address of kernel32.dll to eax, now it plays a little bit with PE header. Of course on different windows version kernel32.dll and advapi32.dll probably will have different values there. It could happen that during MS updates those two, or at least one of them is updated, and in such PE will be screwed, and wrong value will be returned. One simple way to bypass this anti-dump is to run this procedure and get value returned in eax then simly patch this routine with mov eax, that_value/retn 4

Only 2 more anti-dumps left to go over, only 2 more.

Next anti-dump is related to EntryPoint stored in PE header in memory and is located here:

securom_backup_75

adc edi, [400150] is actually adding OptionalHeader.AddressOfEntryPoint to edi. Your dump will have different EntryPoint, but in this real target, SecuROM excepts it to be: 4d5b98h . I have located at least 1 more check, but no need to look for all of them, it is more then enough to add extra code to the dump which will overwrite OptionalHead.AddressOfEntryPoint with this value, and also will update OptionalHeader.SizeOfImage with value which SecuROM wants in its VM interpreter, so we will be injection this code into SecuROM:

loader:                 pusha                         call    __delta __delta:                pop     ebp                         sub     ebp, offset __delta                                                  call    getkernelbase                         xchg    eax, ebx                         gethash <VirtualProtect>                         call    getprocaddress, ebx, hash                                                  push    esp                         mov     ecx, esp                         call    eax, 400000h, 1000h, PAGE_READWRITE, ecx                         add     esp, 4                                                  mov     esi, 400000h                         add     esi, [esi+3ch]                         mov     [esi.pe_addressofentrypoint], 4d5b98h                         mov     [esi.pe_sizeofimage], 87a000h 

                        mov     eax, [ebp+old_entry_point]                         mov     [esp.Pushad_eax], eax                         popa                       jmp     eax 

And now, do you remember that TLS stuff I mentioned earlier? Well here it goes:

securom_backup_76

It calls TlsGetValue(0x0F), so you will have to patch it with value you got like this:

securom_backup_77

And so it has been done, now run your dumped/patched exe, and you will have your game up and running.

That’s all folks…

6. Conclusion

Well as you may see it is relatively simple to fix SecuROM, still, going for the virgin file would be nice, but it requires more free time as you will have to reverse SecuROM VM to fix it properly.

7. References

[1] Ultimate Hooking Engine, deroko of ARTeam,
(Link to Deroko’s Page)

8. Greetings

I wish to thank to all my mates in ARTeam for sharing their knowledge, to 29a for one of the best e-zines, unpack.cn crew (fly, shoooo, heXer, softworm, okododo), AnonymouS tut contributor, he know who he is, and of course, you for reading this document.

С вером у Бога, deroko of ARTeam

Deep Inside SecuRom by AnonymouS

1. The funny side of things

The authors of SecuRom cracks me up:

securom_backup_78

“Time to drink .securom”

And in the redirector proc:

“nobody move, nobody gets hurt”
“Masses Against the Classes”

“yates is still ere.something kinda Ooooh”

“Are there no honor among thieves Mr. Yates ??”

2. Code morphing

This PUSH 10 and JMP look a bit strange:

securom_backup_79

Follow jump and you will end up here:

securom_backup_80

As one can all this does is that it pushes a value onto stack. Anyway, this is not important to us. The dump works fine without patching this ;)

3. Basic API redirection

Here is where we first meet a redirection of a call to API:

securom_backup_81

Let’s trace into 05693644h

securom_backup_82

As one can see, this is actually a call to GetProcAddress.

If we load our dump into LordPE and take a look at the Directory Table (ImportTable):

securom_backup_83
Running through the ImportTable (kernel32.dll) we noticed the following API’s gets redirected:

GetProcAddress (5670A38)
LoadLibraryA (5670A3C)
ExitProcess (5670AD8)
TerminateProcess (5670AE0)

We need to fix these API’s. One can do this either by hand or by using ImpRec or ReVirgin. I prefer nothing it automatically coding a unpacker !!!

4. Code splicing

The code splicing in SecuRom usually looks like this:

securom_backup_84

Once we trace into 05EB4000h we will enter the wonderful world of SecuRom redirection. I will spare you the heartbreak for going through all the calculation loops, but what it basically does is redirection you to the code splicing code. Anyway, here is the beginning:

securom_backup_85

It will eventually end up here:

securom_backup_86
securom_backup_87
As one might see this is part of the code splicing. However we can fix this by forcing the jump at 095A859h to jump to 05E6EEE9h. Now here is something interesting… Look above at line 05E6EF70h… What is this ?? Let’s trace… A new chapter begins…

5. Advanced API redirections

Like with the code splicing the advanced API redirection is trigged by a run through the calculation loops. Most often around 10 times. No big deal… After a little tracing we end up here:

securom_backup_88

This is a call to GetStartupInfoA !!

As one can see it’s not advanced at all. I only chose to call it this because of the calculation/obfuscation loops.

Often calls to API looks very similar to calls to another SecuRom trick…. The Virtual Machine (VM)… Let’s take a look at this feature…

6. Virtual Machine

securom_backup_89

:D

Archive Closing Notes

All materials included here can be found on the index.

(c) ARTeam, 2007/2008.
Reformatted for Archive use by LFATeam, 2018