ArthaXerXes 01/01/2001 22:32
----------------------------

How it works :
--------------

 start:                                  ; DATA XREF: stxt371:029DC206o
029DC1FD                                         ; stxt371:029DC211w ...
029DC1FD                 push    ebp
029DC1FE
029DC1FE loc_29DC1FE:                            ; DATA XREF: stxt371:029DC21Bw
029DC1FE                                         ; stxt371:029DC25Fw
029DC1FE                 mov     ebp, esp
029DC200                 pusha
029DC201                 mov     eax, offset loc_29DC27B
029DC206                 sub     eax, offset start
029DC20B                 add     eax, dword ptr ds:loc_29DC27B+1
029DC211                 mov     dword ptr ds:start, 0E9h
029DC21B                 mov     dword ptr ds:loc_29DC1FE, eax <-- mmm, patching entry point ?
029DC220                 push    offset aUser32_dll ; "USER32.dll"
029DC225                 push    offset aKernel32_dll ; "KERNEL32.dll"
029DC22A                 push    offset loc_29DC009
029DC22F                 push    offset GetModuleHandleA
029DC234                 mov     al, ds:byte_29DC021
029DC239                 cmp     al, 1
029DC23B                 jz      short loc_29DC244
029DC23D                 mov     eax, 0
029DC242                 jmp     short loc_29DC247
029DC244 ; ---------------------------------------------------------------------------
029DC244
029DC244 loc_29DC244:                            ; CODE XREF: stxt371:029DC23Bj
029DC244                 mov     eax, [ebp+8]
029DC247
029DC247 loc_29DC247:                            ; CODE XREF: stxt371:029DC242j
029DC247                 push    eax
029DC248                 call    sub_29DC280
029DC24D                 add     esp, 14h
029DC250                 cmp     eax, 0 <-- 0 == everything went fine, 1 == error
029DC253                 jz      short loc_29DC271 <-- we exit if an error occured, we resume if everything is fine
029DC255                 mov     dword ptr ds:start, 0C2h
029DC25F                 mov     dword ptr ds:loc_29DC1FE, 0Ch
029DC269                 push    eax
029DC26A                 mov     eax, ds:ExitProcess
029DC26F                 call    eax ; ExitProcess
029DC271
029DC271 loc_29DC271:                            ; CODE XREF: stxt371:029DC253j
029DC271                 popa
029DC272                 pop     ebp
029DC273                 jmp     short loc_29DC27B


Note that of course if something went wrong, the executable will be ciphered hence unusable, so it
is not just about patching the check at 29dc253.

preventing the file creation :
------------------------------

how it works

 - GetModuleFileName() to get the name of the file name
 - CreateFile() to open the filename,
 - then, the stuff is copied to %TEMP%

but, the directory changes each time, no problem !

the directory stuff :
---------------------

CreateDirectory() && RemoveDirectory() are your friends

1/ you will quickly find if you put a bpx on CreateDirectory() that around the function 029DD18A
at pos 029DD1EA a call to 029DD2E1 is done.

First it gets the name of the temp directory, then it generates a name of directory that does not
exists, it seems to always start with ~ef, after are 4 figures.

what you must do to this function

 - it must still get the name of %TEMP%
 - but the name of the directory must always be the same, if the directory already exists it is not
 an error, we just do nothing
 - the function must return 1

 it is not that hard.

 what must be modified is function 029DD3DD called by function 029DD2E1 at 029DD3BF. You will see
 it generates the name of the directory until it does not exist. what we must do, always generate the
 same name, and not account existence.

 generating the same name :

@29dd4e0


c7 07 61 78 78 00 mov dword ptr [edi], 00787861
c7 02 61 78 78 00 mov dword ptr [edx], 00787861
            eb 04 jmp 029DD4F2

s/8BF78BC18BFAC1E902F3A58BC883E103F3A46A00
p/c70761787800c70261787800eb04

this hack will ensure that the files are used from %TEMP%\axx, but the directory created is
not %TEMP%\axx yet.

this is because the full name of the directory is in [ebp-012c], and this is what you must set for
CreateDirectory to work.

if we look at
029DD4F4 8D 8D D4 FE FF FF                    lea     ecx, [ebp+new_directory] (ebp-12c)
029DD4FA 51                                   push    ecx
029DD4FB 8B 55 E8                             mov     edx, [ebp+var_18]

029DD4FE FF 52 44                             call    dword ptr [edx+44h] ; CreateDirectory
029DD501 85 C0                                test    eax, eax
029DD503 75 39                                jnz     short loc_29DD53E


also we must set the jnz to a jmp (mundane), because the directory creation might fail for several different reasons

s/75398B45
p/eb

at

029DD4A4 8D 4D D8                             lea     ecx, [ebp+var_28]
029DD4A7 51                                   push    ecx
029DD4A8 8D 95 D4 FE FF FF                    lea     edx, [ebp+new_directory]
029DD4AE 52                                   push    edx
029DD4AF 8B 45 08                             mov     eax, [ebp+arg_0]
029DD4B2 50                                   push    eax
029DD4B3 E8 BC 00 00 00                       call    dir_name_stuff  ; seems it does something for the temp dir name
029DD4B8 83 C4 0C                             add     esp, 0Ch

my l33t routine :

8d bd d4 fe ff ff lea edx, [ebp-12c]
            33 c9 xor ecx, ecx
            33 c0 xor eax, eax
            f7 d1 not ecx
            f2 ae repne scasb
c7 07 61 78 78 00 mov dword ptr [edi], 00787861
            eb 11 jmp 29dd4cd

s/8D4Dd8
p/8dbdd4feffff33c933c0f7d1f2aec70761787800eb11

k...

now doing the stuff we wanted to do
-----------------------------------

id est dear friend preventing the file creation, or at least overwrite...
'tis most mundane if you have the whole picture in mind

 loc_29DD203:                            ; CODE XREF: some_file_creation_here+6Fj
029DD203 6A 00                                push    0
029DD205 68 00 00 00 10                       push    10000000h
029DD20A 6A 03                                push    3
029DD20C 6A 00                                push    0
029DD20E 6A 01                                push    1
029DD210 68 00 00 00 80                       push    80000000h
029DD215 8D 8D F4 FE FF FF                    lea     ecx, [ebp+module_file_name]
029DD21B 51                                   push    ecx
029DD21C 8B 55 FC                             mov     edx, [ebp+var_4]
029DD21F FF 52 10                             call    dword ptr [edx+10h] ; CreateFile(exe.exe)
029DD21F                                                              ; sounds like we are opening the current exe...
029DD21F                                                              ; why would we open the file, except for reading stuff into it, hum ? :-)
029DD222 89 85 F0 FE FF FF                    mov     [ebp+exe_handle], eax
029DD228 83 BD F0 FE FF FF FF                 cmp     [ebp+exe_handle], 0FFFFFFFFh
029DD22F 75 22                                jnz     short exe_open_ok
029DD231 8D 85 F4 FE FF FF                    lea     eax, [ebp+module_file_name]
029DD237 50                                   push    eax
029DD238 8B 4D FC                             mov     ecx, [ebp+var_4]
029DD23B FF 51 70                             call    dword ptr [ecx+70h]
029DD23E 50                                   push    eax
029DD23F 8B 55 10                             mov     edx, [ebp+imports_table]
029DD242 52                                   push    edx
029DD243 E8 8D 1C 00 00                       call    sub_29DEED5
029DD248 83 C4 0C                             add     esp, 0Ch
029DD24B 66 33 C0                             xor     ax, ax
029DD24E E9 8A 00 00 00                       jmp     a_noise
029DD253                      ; ---------------------------------------------------------------------------
029DD253
029DD253                      exe_open_ok:                            ; CODE XREF: some_file_creation_here+A5j
029DD253 8B 45 1C                             mov     eax, [ebp+ef_file_name] ;  this is what is added to the temp path
029DD256 50                                   push    eax
029DD257 8B 4D 14                             mov     ecx, [ebp+temp_dir]
029DD25A 51                                   push    ecx
029DD25B 6A 00                                push    0
029DD25D 8B 55 10                             mov     edx, [ebp+imports_table]
029DD260 52                                   push    edx
029DD261 8B 85 EC FE FF FF                    mov     eax, [ebp+var_114]
029DD267 50                                   push    eax
029DD268 8B 8D F0 FE FF FF                    mov     ecx, [ebp+exe_handle]
029DD26E 51                                   push    ecx
029DD26F E8 6A 06 00 00                       call    do_the_files_stuff ; this function is obfuscated, which means
029DD26F                                                              ; -> has to be monitored carefully
029DD274 83 C4 18                             add     esp, 18h
029DD277 66 89 45 F8                          mov     word ptr [ebp+ret_val], a

ah, I should have used Imhotep before loading this fat file into IDA...

well, what now ? in do_the_files_stuff the creation of the file is performed.

because you are very clever, you put a bpx on CreateFile()  :

HANDLE CreateFile(
  LPCTSTR lpFileName,                         // file name
  DWORD dwDesiredAccess,                      // access mode
  DWORD dwShareMode,                          // share mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
  DWORD dwCreationDisposition,                // how to create
  DWORD dwFlagsAndAttributes,                 // file attributes
  HANDLE hTemplateFile                        // handle to template file
);

dwFlagsAndAttributes


029DDE8F 6A 00                                push    0               ; handle to template file
029DDE91 68 00 00 00 10                       push    10000000h       ; file attributes
029DDE96 6A 02                                push    2               ; how to create
029DDE98 6A 00                                push    0               ; SD
029DDE9A 6A 00                                push    0               ; share mode
029DDE9C 68 00 00 00 C0                       push    0C0000000h      ; access mode
029DDEA1 8D 85 F0 FE FF FF                    lea     eax, [ebp+var_110]
029DDEA7 50                                   push    eax             ; file name
029DDEA8 8B 4D F8                             mov     ecx, [ebp+var_8]
029DDEAB FF 51 10                             call    dword ptr [ecx+10h]

file_attributes == FILE_FLAG_RANDOM_ACCESS (Indicates that the file is accessed randomly. The system can use this as a hint to optimize file caching.)

access_mode == GENERIC_READ | GENERIC_WRITE (read/write access)

how_to_create == CREATE_ALWAYS, Creates a new file. If the file exists, the function overwrites the file and clears the existing attributes.

An idiot would remove the CreateFile() call, but you are not an idiot, instead you will change the how_to_create to CREATE_NEW (1), which will do this :

Creates a new file. The function fails if the specified file already exists.

Then we change the check

029DDEAE 89 45 FC                             mov     [ebp+var_4], eax
029DDEB1 83 7D FC FF                          cmp     [ebp+var_4], 0FFFFFFFFh
029DDEB5 75 43                                jnz     short loc_29DDEFA


s/026A006A0068000000C08D85f0
p/01

but if you run now, you will have an error "The file exists."

We need to hack the function so that the existence of the file is not treated as an error. Hard ? Not that much,


029DDEA8 8B 4D F8                             mov     ecx, [ebp+var_8]
029DDEAB FF 51 10                             call    dword ptr [ecx+10h] ; CreateFile
029DDEAE 89 45 FC                             mov     [ebp+var_4], eax
029DDEB1 83 7D FC FF                          cmp     [ebp+var_4], 0FFFFFFFFh
029DDEB5 75 43                                jnz     short loc_29DDEFA
029DDEB7 8B 55 F8                             mov     edx, [ebp+var_8]
029DDEBA FF 52 70                             call    dword ptr [edx+70h] ; GetLastError
029DDEBD 89 85 E8 FE FF FF                    mov     [ebp+var_118], eax
029DDEC3 83 BD E8 FE FF FF 20                 cmp     [ebp+var_118], 20h
029DDECA 75 0F                                jnz     short loc_29DDEDB
029DDECC 8B 45 0C                             mov     eax, [ebp+arg_4]
029DDECF 83 78 14 01                          cmp     dword ptr [eax+14h], 1
029DDED3 75 06                                jnz     short loc_29DDEDB
029DDED5 66 B8 01 00                          mov     ax, 1
029DDED9 EB 56                                jmp     short loc_29DDF31


we simply have to change 20h to 50h (50h == file exists) and the way the jump is done

cmp [ebp+var_118], 50h
je 29dded5

this way if the file exists, the error will be ignored.

s/20750f
p/507409

it works ! the program runs with the right cd... (but the files are still deleted)

clean up
--------

bpx RemoveDirectory

you will break once in exe, it tries to RemoveDirectory(%TEMP%) (probably to check existence)
and then you will break in ~efxxxx.tmp, it removes the %TEMP%\~stuff, that is what interests us,
is not it ?

but if the directory is not empty, it will not be removed hence making things easier for us
(The RemoveDirectory function deletes an existing empty directory).

clever test :

create directory axx

put a file in it,
run the program, a message box "unload debugger and try again" should appear
click
the directory is removed

-> the routine removes all the files in the directory and then remove the directory
-> -> we must change that routine to a no-op.

different ways to understand the protection, but know that the clean up takes place in the
%TEMP%\~efxxxx.tmp file

bpx on DeleteFile(), FindFirstFile() or RemoveDirectory() work well.

you will find a look that searches for the files of the directory and removes them one by one, you cannot see it
from IDA because :

00401809 loc_401809:                             ; CODE XREF: clean_up_dir+10Aj
00401809                 push    ebp
0040180A                 call    eax <-- ahah
0040180C                 add     esp, 10h
0040180F                 mov     ebx, eax
00401811
00401811 loc_401811:                             ; CODE XREF: clean_up_dir+F6j
00401811                                         ; clean_up_dir+115j
00401811                 lea     edx, [esp+35Ch+var_348]
00401815                 push    edx
00401816                 push    esi
00401817                 call    ds:FindNextFileA
0040181D                 test    eax, eax
0040181F                 jz      short loc_401826
00401821                 cmp     ebx, 0FFFFFFFFh
00401824                 jnz     short loc_4017C6
00401826
00401826 loc_401826:                             ; CODE XREF: clean_up_dir+13Fj
00401826                 push    esi
00401827                 call    ds:FindClose

eax contains a routine that deletes the file, no big deal.

do not edit this function (a delete) directly. We must only kill it here :

004011B1                 push    0
004011B3                 push    offset sub_401200
004011B8                 push    0
004011BA                 push    offset a_       ; "*.*"
004011BF                 push    ebx
004011C0                 call    delete
004011C5                 add     esp, 14h
004011C8                 push    ebx
004011C9                 call    ds:RemoveDirectoryA

when it is called for removing all the files of the directory.

noticed that the ~efe2.tmp seems to be used like a mutex or something ? if you can open -> we should delete,
otherwise the protection is still running.

so we should be carefull :

0040119F                 call    ds:CreateFileA
004011A5                 cmp     eax, 0FFFFFFFFh
004011A8                 jz      short loc_4011CF
004011AA                 push    eax
004011AB                 call    ds:CloseHandle
004011B1                 push    0
004011B3                 push    offset sub_401200
004011B8                 push    0
004011BA                 push    offset a_       ; "*.*"
004011BF                 push    ebx
004011C0                 call    delete
004011C5                 add     esp, 14h
004011C8                 push    ebx
004011C9                 call    ds:RemoveDirectoryA
004011CF
004011CF loc_4011CF:                             ; CODE XREF: sub_4010E0+C8j
004011CF                 mov     ecx, [esp+114h+arg_0]
004011D6                 push    ebp
004011D7                 push    ecx
004011D8                 call    sub_401AF0
004011DD                 mov     ebp, eax
004011DF                 add     esp, 8
004011E2                 test    ebp, ebp
004011E4                 jnz     loc_401103
004011EA                 pop     ebx
004011EB                 pop     esi
004011EC                 pop     edi


we have to jump over the code starting at 4011b1 and ending at 4011cf (am I dreaming or this is a spinlock ??? what a bunch of lamers...), so yes, the code to put at 4011b1 is jmp 4011cf. :-)

s/6A00680012
p/eb1c

note : ~efxxxx.tmp should be overwritten, but our above hack also prevented this (since the same function is used for all the files, which is logical, they are not THAT stupid).

now you should have your SD2 program that "delivers its cargo" in %TEMP%\axx (note that you have to create that directory by hand the first time), and will not overwrite the existing files (you can therfore hexedit them all).

the next step -> bypassing the debugger detection, see other notes.


Why do I think Macrovision is a bunch of lamers ?
-------------------------------------------------

 - they seem to know nothing about FILE_FLAG_DELETE_ON_CLOSE (CreateFile() flag), instead of calling DeleteFile() they call CloseHandle() to delete the file, much more l33t and much more clean coding.
 - they suck because they just added the dll at the end of the executables instead of adding sections that would contain
 the protection, a little bit like a packer. This is probably due to the way they access the CD, which is a third reason why I think they suck.
 - ok they know several ways to detect SoftIce so what ? Now I have all the time I need to remove these checks one by one. All these debug checks are a waste of time.
 - a file for a signal ???? damn ! Never heard of mutexes or semaphores ?

You know why I think that protections will never be awesome ? Because people who are able to make such protections generally have a more interesting job than making useless protections, a job which consists in showing off, drinking cappuccino, producing marvellous C++, complaining about Direct3D and listening to Einsturzende Neubauten (all simultaneously).

Your comments apprecied.

THE SIGNATURE
-------------

The CD contains a 20 Mb signature, but the protection auto-disables itself if it detects the CD will not be able to
read the signature so we do not really give a ... about it, do we ? :-)
