grrr 'Securom, another damn Addendum'

tools: tasm5.0, softICE 3.23 with memory dumper (icedump?), hex editor, procdump, RPPv1.2i..

btw, my target, loader.exe 920,991bytes 27/05/99, compressed with 'Petite'(v1.4??) from the
game 'Outcast' by Appeal/Infogrames www.outcast-game.com (nice target, as 50% of the time
doesnt want to run with the correct cd in anyway.)

hello.. hi r!sc, wassup??

hi, i have been playin with securom.. i loved pedros tutorial on it (http://crknotez.cjb.net),
and this helped me crack a few securom games, but, alas, i think sony have updated securom, so
pedros tutorial dont help much now.. remember how if it knew you had been debugging it, and it
refused to run anymore, until a reboot?? well, this sucks, and there were a few safe
breakpoints you could use before, bpx writeprocessmemory, bpx getdrivetypea, erm, well, it
knows about these now, and refuses to run.. alas, we run into a few problems.. :D

well, are you a reverse engineer or a mouse?? hehe, we aint afraid of no securom..

say i wanna bpx on writeprocessmemory, to find out where it decrypts the data in the programs
code, i get one break, the first one where it decrypts some securom code, then no more, the
program runs in a continous loop, ctrl-alt-del to kill it, then it wont run again.. say i wanna
avoid the code decryption part of it, and break on the getdrivetypea, no no no, if you have any
nice breakpoints set, it just doesnt run, and after clearing them, it still doesnt run.. 

stuff i noticed, cms16.dll, cms32_95.dll & cms32_nt.dll are inside the program, and wrote to
disk when its executed.. probably to stop people tampering with them.. :D if it knows you have
been debugging it, it exits without closing the file handle to cms16.dll. try deleting it, you
get a nice error, 'Cannot delete cms16: The specified file is being used by windows'.

well, theres a clue, maybe.. clear all breakpoints, bpx createfilea.. run ya securom protected
game: first few breaks arnt important.. windows loading the file, then a short pause, where it
gets decompressed, then these are the ones we want, the first opens itself, the second?
cms16.dll? the third, cms16.dll.. wait, take a look at this code..


0137:005A1298  50                  PUSH    EAX                      <-- ptr to x:\xx\cms16.dll
0137:005A1299  FF1534685C00        CALL    [KERNEL32!CreateFileA]
0137:005A129F  8945DC              MOV     [EBP-24],EAX
0137:005A12A2  837DDCFF            CMP     DWORD PTR [EBP-24],-01   <-- its already there, and 
0137:005A12A6  753B                JNZ     005A12E3                   - cant be opened again..
0137:005A12A8  C70580645C0000000000MOV     DWORD PTR [005C6480],00000000
0137:005A12B2  6A00                PUSH    00
0137:005A12B4  6A00                PUSH    00
0137:005A12B6  6A03                PUSH    03
0137:005A12B8  6A00                PUSH    00
0137:005A12BA  6A00                PUSH    00
0137:005A12BC  6800000080          PUSH    80000000
0137:005A12C1  8D8D38FFFFFF        LEA     ECX,[EBP-00C8]
0137:005A12C7  51                  PUSH    ECX                      <-- same ptr to cms16.dll
0137:005A12C8  FF1534685C00        CALL    [KERNEL32!CreateFileA]
0137:005A12CE  8945DC              MOV     [EBP-24],EAX
0137:005A12D1  837DDCFF            CMP     DWORD PTR [EBP-24],-01   <-- oh crap, its still -1
0137:005A12D5  750A                JNZ     005A12E1                   - but forcing this jump
0137:005A12D7  6A00                PUSH    00                         - it will run again :D    
0137:005A12D9  E8022C0000          CALL    005A3EE0
0137:005A12DE  83C404              ADD     ESP,04
0137:005A12E1  EB0A                JMP     005A12ED
0137:005A12E3  C70580645C0001000000MOV     DWORD PTR [005C6480],00000001
0137:005A12ED  8B15C4675C00        MOV     EDX,[005C67C4]


well, see, it tries to create this file, and if it fails, return code FFFFFFFF, it exits.. if
we trick it, make it think it could create this file, just by forcing either of these jumps, it
runs again :D

so not all is lost.. oops, we still cant bpx writeprocessmemory, or bpx getdrivetypea, so
things are trickier, but not impossible..

what i noticed about the older versions of securom, it decrypts 20kb of program code, around
the original entry point, then checks the disk, and if the correct one is in, it decrypts 200h
bytes more of code at the original entry point.. the other code it decrypts wasnt important, as
this was securom code.. theory, trace petite until it has unpacked the program, dump the
memory, bpx on the exit point of the securom code, and when you reach it, dump the memory
again, and just do a file compare, you should find a nice 20kb block of decrypted code in the
second dump.. arrgh! how to bpx on the exit point of the securom code??  heh, dont ph34r, 'tis
easy :D

i expect you to have already fucked up the program, so it wont run anymore, and we have to make
it run by changing one of the jumps after the call to createfilea, 'cms16.dll'.. good.. i used
my dodgy process patcher to make a loader that fixed this for me..

T=10000:
F=loader.exe:
O=securomfix_cc.exe:
P=5A12A6/75/CC:  ; 0137:005A12A6  753B  JNZ  005A12E3
$

changing this to a EB makes it run all the time, but i wanted to break here, so i change it to
a CC, an int 03, then in softice, bpint 03, x. run the loader.. hey, when it breaks, dont
forget to change the CC to an EB..e eip eb.. okay, when it breaks, and you have changed your
int 03 to a jmp, just make your code window nice and big, and scroll, ctrl-page-down.

this is where we could normally break, but bpx getdrivetypea or just bpx 5a25a2, it knows about
them and stops running.. bad, keep scrolling

0137:005A259B  52                  PUSH    EDX
0137:005A259C  FF15E04F5C00        CALL    [KERNEL32!GetDiskFreeSpaceA]
0137:005A25A2  8D8548FCFFFF        LEA     EAX,[EBP-03B8]
0137:005A25A8  50                  PUSH    EAX
0137:005A25A9  FF15004B5C00        CALL    [KERNEL32!GetDriveTypeA]
0137:005A25AF  83F805              CMP     EAX,05

look, another place we could normally break, but, alas, we cant no more.. keep scrolling.. 

0137:005A28E0  8D9548FCFFFF        LEA     EDX,[EBP-03B8]
0137:005A28E6  52                  PUSH    EDX
0137:005A28E7  FF15E04F5C00        CALL    [KERNEL32!GetDiskFreeSpaceA]
0137:005A28ED  8D8548FCFFFF        LEA     EAX,[EBP-03B8]
0137:005A28F3  50                  PUSH    EAX
0137:005A28F4  FF15004B5C00        CALL    [KERNEL32!GetDriveTypeA]
0137:005A28FA  83F805              CMP     EAX,05

Yippee!! this is another place where we would break, many many pages of code have passed before
us, and we know, this is where the securom code ends, and jumps to the proper program :D

0137:005A31A8  B8A1535000          MOV     EAX,005053A1
0137:005A31AD  90                  NOP
0137:005A31AE  90                  NOP
0137:005A31AF  50                  PUSH    EAX
0137:005A31B0  EB03                JMP     005A31B5
0137:005A31B2  58                  POP     EAX
0137:005A31B3  FFE0                JMP     EAX

hmm, good news, we can bpx here 0137:005A31B3  FFE0  JMP  EAX, and all is well.. the program
still works fine :D    yah.. fine!!

heres another lame loader to help us on our way..

T=10000:
F=loader.exe:
O=securom.cc.jmp.eax.exe:
;P=5A12A6/75/EB:  ; heh, my pc crashed for some reason, so this isnt needed yet :)
P=5A31B3/FF/CC:   ; i just wanna break on the securom exit point, the jmp eax.. 
$

nice one r!sc, almost time to be destructive :D


now, using this loader, when softice breaks on the int 03,  we can get our decrypted code, what
about our dodgy calls?? remember older securom? call dword ptr [securom] for every import? and
if you traced over it, the nice securom code replaced [securom] with the actual address of the
import in out IAT somewhere in memory? hehe, well, buggered if this works now :(  look here..

this is my original program entry point, see 005053C7.. thats the api call GetVersion, but it
calls the securom code, which in time, jmp's to GetVersion, trace into one of the calls, then
scroll the code window until a jmp eax.. put a breakpoint on this.. and run it..

0137:005053A1  55                  PUSH    EBP
0137:005053A2  8BEC                MOV     EBP,ESP
0137:005053A4  6AFF                PUSH    FF
0137:005053A6  6810EB5100          PUSH    0051EB10
0137:005053AB  68C0525000          PUSH    005052C0
0137:005053B0  64A100000000        MOV     EAX,FS:[00000000]
0137:005053B6  50                  PUSH    EAX
0137:005053B7  64892500000000      MOV     FS:[00000000],ESP
0137:005053BE  83EC58              SUB     ESP,58
0137:005053C1  53                  PUSH    EBX
0137:005053C2  56                  PUSH    ESI
0137:005053C3  57                  PUSH    EDI
0137:005053C4  8965E8              MOV     [EBP-18],ESP
0137:005053C7  FF1528C25A00        CALL    [005AC228]       <-- call [securom] ...
0137:005053CD  33D2                XOR     EDX,EDX
0137:005053CF  8AD4                MOV     DL,AH
0137:005053D1  891594585900        MOV     [00595894],EDX
0137:005053D7  8BC8                MOV     ECX,EAX
0137:005053D9  81E1FF000000        AND     ECX,000000FF
0137:005053DF  890D90585900        MOV     [00595890],ECX

my 'jmp eax' was at 59F16F

Break due to BPX #0137:0059F16F  (ET=286.97 microseconds)
:?eax
BFF9137C  3220771708 (-1074195588)  "|"
:what eax
The value BFF9137C is (a) KERNEL32!GetVersion  <-- ahh

now, theory, petite unpacks the code, and unpacks a real import address table :D, we just gotta
find the real IAT in memory, search for the correct imports for our calls, and fix the calls to
call our import table instead of the securom code..

:s 400000 l ffffffff 7c 13 f9 bf
Pattern found at 0137:0051B110 (0011B110)
:s
Pattern found at 0137:005C6530 (001C6530)
:s
Pattern found at 0137:005CA3E8 (001CA3E8)
:s
Pattern found at 0137:0095067C (0055067C)

well, we got four choices at the moment, i trace the unpacker code, and stop just before the
securom code runs.. then search again..

:s 400000 l ffffffff 7c 13 f9 bf
Pattern found at 0137:005CA3E8 (001CA3E8)

yippee, it only finds one.. so if i edit this line..

0137:005053C7  FF1528C25A00        CALL    [005AC228]

to

0137:005053C7  FF15E8A35C00        CALL    [KERNEL32!GetVersion]    ; CALL [005CA3E8]

thats one call fixed, only about 300 left to go :)  lets code something..

WHOOPS, i coded something, fixed everything, dumped the memory, copied and pasted it into my
previous dump, and it worked ok.. but.. it didnt work on win95 (i am working with win98)..

further debugging revealed some code like this..

015F:00508FE0  55                  PUSH    EBP
015F:00508FE1  8B2D68B15100        MOV     EBP,[KERNEL32!CloseProfileUserMapping]
015F:00508FE7  56                  PUSH    ESI
015F:00508FE8  57                  PUSH    EDI
015F:00508FE9  33DB                XOR     EBX,EBX
015F:00508FEB  33F6                XOR     ESI,ESI
015F:00508FED  33FF                XOR     EDI,EDI
015F:00508FEF  3BC3                CMP     EAX,EBX
015F:00508FF1  7533                JNZ     00509026
015F:00508FF3  FFD5                CALL    EBP

see this line :

015F:00508FE1  8B2D68B15100      MOV     EBP,[KERNEL32!CloseProfileUserMapping]

its really MOV EBX, DWORD PTR [0051B168], moving an api address from the first IAT we found..
the one thats not their when its unpacked..  securom didnt mess with this, as its not a direct
call to the IAT.. well, i unpacked the executable with procdump.. ran it with loader32, and
checked out the memory at 51B168, it contained 72981200.. obviously, as this is replaced with
the linear address of the api function, this was my 'broken' first thunk.. i searched the
unpacked exe for 72981200, and found two places, one just before all the imported function
names, and the other one i had found before, further studying of the exe with my hex editor, i
located the start of the import table.. the list of my image_import_descriptors, 8 of them,
followed by 14h null bytes.. the terminating descriptor :D yippee!! just use procdumps pe
editor, edit the directory structure, point the import table to the real one.. for this
program, it was 528ed8, - imagebase makes 128ed8.. 

okay, running it with loader32 again, checking out address 51b168, yes, its been written over
with the linear address of the correct api function.. great.. halfway their..


lets code something again.. 

;------------------------------------------------------------------------------

; R!SC's dodgy call fixer for 'newer' securom
; (c) august 27th 1999 risc@notme.com

; tasm32 /mx /m3 /z /q call_fix
; tlink32 -x /Tpe /aa /c call_fix,call_fix

; copy and paste the code into compressed securom executable.. i like the pe header.. 
; break on the jmp eax (in the securom code, jmp orig_entry_point)
; recode the 'jmp eax' in the call [securom] code to jmp ebx
; i3here on, faults on
; copy the code to a empty part of memory.. m 400300 l 60 530000, r eip 530000, run it :0

.486P
.Model Flat

.code
main:
    call    @1  ; please excuse my first attempt at kinda relocatable code
@1:
    pop ebx
    mov esi, ebx
    add ebx, offset here-offset @1  ; return address from jmp [5ac228]
    add esi, offset boring-offset @1
    mov edx, 401000h
    mov ecx, 51b000h-401000h    ; iat begins at 51b000, so code hopefully ends before it
search_loop:
    cmp [edx], 0c22815ffh   ; search pattern for CALL [005AC228]
    jne try_again
    cmp word ptr [edx+4],005ah  ; -
    jne try_again
    
    lea eax, [edx+6]        ; get the address which would be pushed onto the stack
    pushad
    push eax
    ;jmp dword ptr [5ac228h]
    db 0ffh,25h,28h,0c2h,5ah,0  ; jmp blah..

here:                   ; k, we return here from securom code, the api address is in EAX
    mov edx, 51b000h    ; start address of my *real* IAT, first thunk..
search_iat:
    cmp [edx],eax
    jz  got_match
    inc edx
    cmp edx, 51b2a0h
    jne search_iat
    pop eax         ; safty, if it cant find a match for whats in EAX in our IAT
    popad
    int 03          ; match wasnt found, break on the int 03, write down the address in EDX
    jmp try_again   ; and fix it by hand..
got_match:
    mov [esi+4],edx ; save the addr of the import address we got a match for
    pop eax
    popad
    mov eax, [esi+4]    ; retrive the address of the import in our IAT
    mov [edx+2], eax    ; paste it over the 005ac228 in the call [securom]
try_again:
    inc edx
    mov [esi],edx   ; store the last addr EDX was on.. (in case of any problems..)
    dec ecx
    jne search_loop
    int 03
    nop
boring:
end main

;------------------------------------------------------------------------------


right, compile it, hex edit it and hex edit the compressed securom exe, cut and paste the code
from the call fixer into the securom executable somewhere.. i chose file offset 300h, in the pe
header..

run your loader which breaks on the securom exit point. move the code from the pe header to
somewhere else..  m 400300 l 100 530000 , r eip 530000..

put a bpx on JMP [005AC228], and run it. when you get a break, trace into the jmp, and scroll
your code window until you see some code like this..

015F:0059F15E  83C408              ADD     ESP,08
015F:0059F161  61                  POPAD
015F:0059F162  8B45F4              MOV     EAX,[EBP-0C]
015F:0059F165  8BF0                MOV     ESI,EAX
015F:0059F167  8B06                MOV     EAX,[ESI]
015F:0059F169  5F                  POP     EDI
015F:0059F16A  5E                  POP     ESI
015F:0059F16B  5B                  POP     EBX
015F:0059F16C  8BE5                MOV     ESP,EBP
015F:0059F16E  5D                  POP     EBP
015F:0059F16F  FFE0                JMP     EAX

then change the jmp eax, which would jmp to the api call, to jmp ebx.. to jump back to our call
fixing routine

:a 59f16f
0137:0059F16F jmp ebx

bc*, i3here on, faults on, cause we wanna trap any errors, and softice to break if it hits
either of our int 03's..

welp, anyway, run the code, and cross your fingers.. i got a some errors, three calls returned
invalid addresses, so i couldnt find them in my IAT, and two calls caused a crash.. with faults
on, softice caught the crash, and all i had to do was look in memory location at the end of my
code, where i store the address counter, to see what call crashed it..

502bdd crashed it, and after fixing that, 50ce1c crashed it, so when i run it again, i edit
those memory locations, replacing the FF 15 with CC 15, which stopped the code finding the
correct byte pattern, thus stopping the crash, and investigate those calls by hand..

it popped up at this int 03 three times aswell..

    int 03          ; match wasnt found, break on the int 03, write down the address in EDX
    jmp try_again   ; and fix it by hand..

just write down the address in edx, and carry on, we can investigate those calls by hand
aswell..

bad calls were 507c39, 50a324 & 50e0be, so run the program with your break on the securom exit
point, edit the eip to point to one of those dodgy call's, and trace, break on the jmp eax..
and search for the api address in your IAT by hand :D, after all the calls are fixed, dump the
memory, pagein 400000 11b000 c:\callsfixed.dat .. copy and paste this into the file you
unpacked with procdump, and fixed the IAT address in the pe-header..

cross your fingers, run it :D hey, it works :P

thats it!! securom is a bit more fun this time around, but if it wasnt for Pedro and +Xoanon, i
doubt this tutorial would have been possible. In the three or so days this crack took me, i
have learnt quite a lot, and hopefully, you, have learnt something from it aswell..

R!SC 27th August '99

