I know this probably isn't clean enough to put into wine yet, but it is a start, so I am at least attempting to get it put in, also, if someone wants to download it for me for backup purposes, as I seem to frequently delete the new copy of my patches rather than the old one ;) -Dustin Note that these are created against 05/22/2002 tree and may/may not apply cleanly to older/newer trees. Also note that you need to apply safedisc-safe.diff before safedisc-dmca.diff in order to get the functions in the correct order... __________________________________________________ Do You Yahoo!? LAUNCH - Your Yahoo! Music Experience http://launch.yahoo.com
--- memory/emulate.c Wed Dec 31 18:00:00 1969 +++ memory/emulate.c Thu May 23 20:40:41 2002 @@ -0,0 +1,53 @@ +/* + * Emulation of privileged memory + * + * Copyright 2002 Laurent Pinchart + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windef.h" +#include "wingdi.h" +#include "wine/winuser16.h" +#include "module.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(virtual); + +#ifdef __i386__ + +/*********************************************************************** + * MEMORY_SharedUserData + * + * Handles exceptions for the SharedUserData access. + */ +BOOL MEMORY_SharedUserData( LPVOID arg, LPCVOID addr ) +{ + DWORD dwProtection; + BOOL ret; + + TRACE( "MEMORY_SharedUserData\n" ); + + ret = VirtualProtect( (LPVOID)0x7ffe0000, 0x10000, PAGE_READWRITE, &dwProtection ); + if ( ret ) + { + *(LPDWORD)0x7ffe0000 = 0x12345678; + ret = VirtualProtect( (LPVOID)0x7ffe0000, 0x10000, PAGE_READONLY, &dwProtection ); + } + + return ret; +} + +#endif /* __i386__ */ --- dlls/ntdll/Makefile.in Tue May 14 15:55:01 2002 +++ dlls/ntdll/Makefile.in Thu May 23 22:57:27 2002 @@ -32,6 +32,7 @@ $(TOPOBJDIR)/loader/ne/segment.c \ $(TOPOBJDIR)/memory/atom.c \ $(TOPOBJDIR)/memory/codepage.c \ + $(TOPOBJDIR)/memory/emulate.c \ $(TOPOBJDIR)/memory/environ.c \ $(TOPOBJDIR)/memory/global.c \ $(TOPOBJDIR)/memory/heap.c \ --- dlls/ntdll/signal_i386.c Thu May 16 15:32:16 2002 +++ dlls/ntdll/signal_i386.c Thu May 23 22:23:28 2002 @@ -678,7 +678,8 @@ static void do_segv( CONTEXT *context, int trap_code, void *cr2, int err_code ) { EXCEPTION_RECORD rec; - DWORD page_fault_code = EXCEPTION_ACCESS_VIOLATION; + DWORD page_fault_code = EXCEPTION_ACCESS_VIOLATION, + priv_instr_code = EXCEPTION_PRIV_INSTRUCTION; #ifdef CR2_sig /* we want the page-fault case to be fast */ @@ -708,8 +709,8 @@ case T_SEGNPFLT: /* Segment not present exception */ case T_PROTFLT: /* General protection fault */ case T_UNKNOWN: /* Unknown fault code */ - if (INSTR_EmulateInstruction( context )) return; - rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION; + if (INSTR_EmulateInstruction( context, &priv_instr_code )) return; + rec.ExceptionCode = priv_instr_code; break; case T_PAGEFLT: /* Page fault */ #ifdef CR2_sig --- include/miscemu.h Sat Mar 9 18:02:34 2002 +++ include/miscemu.h Thu May 23 21:22:23 2002 @@ -167,7 +167,7 @@ extern UINT DOSMEM_MapLinearToDos(LPVOID); /* linear Wine to DOS */ /* memory/instr.c */ -extern BOOL INSTR_EmulateInstruction( CONTEXT86 *context ); +extern BOOL INSTR_EmulateInstruction( CONTEXT86 *context, DWORD *exception_code ); /* msdos/interrupts.c */ typedef void (WINAPI *INTPROC)(CONTEXT86*); --- include/wine/server_protocol.h Thu Apr 25 17:58:59 2002 +++ include/wine/server_protocol.h Thu May 23 21:22:23 2002 @@ -296,6 +296,7 @@ struct init_process_done_reply { struct reply_header __header; + int suspended; int debugged; }; @@ -3198,6 +3199,6 @@ struct get_window_properties_reply get_window_properties_reply; }; -#define SERVER_PROTOCOL_VERSION 79 +#define SERVER_PROTOCOL_VERSION 80 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ --- memory/instr.c Sat May 11 18:01:12 2002 +++ memory/instr.c Thu May 23 21:22:23 2002 @@ -396,7 +396,7 @@ * * Emulate a privileged instruction. Returns TRUE if emulation successful. */ -BOOL INSTR_EmulateInstruction( CONTEXT86 *context ) +BOOL INSTR_EmulateInstruction( CONTEXT86 *context, DWORD *exception_code ) { int prefix, segprefix, prefixlen, len, repX, long_op, long_addr; SEGPTR gpHandler; @@ -686,7 +686,9 @@ case 0xcd: /* int <XX> */ if (long_op) { - ERR("int xx from 32-bit code is not supported.\n"); + ERR("int 0x%02x from 32-bit code is not supported.\n", instr[1]); + if (instr[1] == 0x01) + *exception_code = EXCEPTION_ACCESS_VIOLATION; break; /* Unable to emulate it */ } else --- scheduler/process.c Tue May 21 14:42:31 2002 +++ scheduler/process.c Thu May 23 21:22:23 2002 @@ -25,6 +25,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <signal.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -42,6 +43,7 @@ #include "wine/server.h" #include "options.h" #include "wine/debug.h" +#include "global.h" WINE_DEFAULT_DEBUG_CHANNEL(process); WINE_DECLARE_DEBUG_CHANNEL(relay); @@ -125,6 +127,9 @@ /* scheduler/pthread.c */ extern void PTHREAD_init_done(void); +/* memory/emulate.c */ +extern BOOL MEMORY_SharedUserData(LPVOID, LPCVOID); + extern BOOL MAIN_MainInit(void); typedef WORD (WINAPI *pUserSignalProc)( UINT, DWORD, DWORD, HMODULE16 ); @@ -306,6 +311,20 @@ SetStdHandle( STD_ERROR_HANDLE, current_startupinfo.hStdError ); } + /* Reserves a PAGE_NOACCESS at 0x7ffe0000. That page is used as shared + * memory between kernel space and user space (see SharedUserData in + * ntddk.h). It looks like it is at 0xffdf0000 on NT5. That is still to + * be checked. + */ + if ( VirtualAlloc( (LPVOID)0x7ffe0000, 0x10000, MEM_RESERVE|MEM_COMMIT, PAGE_NOACCESS ) == (LPCVOID)0x07ffe0000 ) + { + VIRTUAL_SetFaultHandler( (LPCVOID)0x7ffe0000, MEMORY_SharedUserData, 0 ); + } + else + { + WARN( "Unable to map SharedUserData, SafeDisc protection won't be supported\n" ); + } + /* Now we can use the pthreads routines */ PTHREAD_init_done(); @@ -330,7 +349,7 @@ */ static void start_process(void) { - int debugged, console_app; + int suspended, debugged, console_app; LPTHREAD_START_ROUTINE entry; WINE_MODREF *wm; HFILE main_file = main_exe_file; @@ -371,7 +390,8 @@ req->gui = !console_app; wine_server_add_data( req, main_exe_name, strlen(main_exe_name) ); wine_server_call( req ); - debugged = reply->debugged; + suspended = reply->suspended; + debugged = reply->debugged; } SERVER_END_REQ; @@ -403,6 +423,8 @@ PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0 ); /* Call UserSignalProc ( USIG_PROCESS_RUNNING ... ) only for non-GUI win32 apps */ if (console_app) PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0 ); + + if (suspended) raise( SIGSTOP ); if (TRACE_ON(relay)) DPRINTF( "%08lx:Starting process %s (entryproc=%p)\n", --- server/process.c Fri Apr 26 14:05:18 2002 +++ server/process.c Thu May 23 21:22:23 2002 @@ -382,6 +382,14 @@ } +/* signal the parent that a process init is finished */ +void process_init_done( struct process *process ) +{ + set_event( process->init_event ); + release_object( process->init_event ); + process->init_event = NULL; +} + /* get a process from an id (and increment the refcount) */ struct process *get_process_from_id( void *id ) { @@ -896,13 +904,27 @@ if ((process->exe.namelen = get_req_data_size())) process->exe.filename = memdup( get_req_data(), process->exe.namelen ); - generate_startup_debug_events( current->process, req->entry ); - set_event( process->init_event ); - release_object( process->init_event ); - process->init_event = NULL; + generate_startup_debug_events( process, req->entry ); if (req->gui) process->idle_event = create_event( NULL, 0, 1, 0 ); - if (current->suspend + current->process->suspend > 0) stop_thread( current ); - reply->debugged = (current->process->debugger != 0); + + reply->suspended = 0; + reply->debugged = (current->process->debugger != 0); + if (current->suspend + current->process->suspend > 0) + { + /* temporarily clear the init event so that ptrace functions work */ + struct event *init_event = process->init_event; + process->init_event = NULL; + stop_thread( current ); + process->init_event = init_event; + + if (current->attached) + { + reply->suspended = 1; + continue_thread( current ); /* it will suspend itself */ + return; + } + } + process_init_done( process ); } /* open a handle to a process */ --- server/process.h Thu Mar 21 18:21:23 2002 +++ server/process.h Thu May 23 21:22:23 2002 @@ -90,6 +90,7 @@ extern struct thread *create_process( int fd ); extern struct process *get_process_from_id( void *id ); extern struct process *get_process_from_handle( handle_t handle, unsigned int access ); +extern void process_init_done( struct process *process ); extern int process_set_debugger( struct process *process, struct thread *thread ); extern int debugger_detach( struct process* process, struct thread* debugger ); --- server/protocol.def Thu Apr 25 17:58:59 2002 +++ server/protocol.def Thu May 23 21:22:23 2002 @@ -276,6 +276,7 @@ int gui; /* is it a GUI process? */ VARARG(filename,string); /* file name of main exe */ @REPLY + int suspended; /* created suspended? */ int debugged; /* being debugged? */ @END --- server/ptrace.c Sat Mar 9 18:18:36 2002 +++ server/ptrace.c Thu May 23 21:22:23 2002 @@ -79,7 +79,12 @@ switch(sig) { case SIGSTOP: /* continue at once if not suspended */ - if (thread && (thread->process->suspend + thread->suspend)) break; + if (thread && (thread->process->suspend + thread->suspend)) + { + /* check if this was the self-suspend upon process init */ + if (thread->process->init_event) process_init_done( thread->process ); + break; + } /* fall through */ default: /* ignore other signals for now */ if (thread && get_thread_single_step( thread )) --- server/trace.c Fri Apr 26 14:05:18 2002 +++ server/trace.c Thu May 23 21:22:23 2002 @@ -441,6 +441,7 @@ static void dump_init_process_done_reply( const struct init_process_done_reply *req ) { + fprintf( stderr, " suspended=%d,", req->suspended ); fprintf( stderr, " debugged=%d", req->debugged ); }
--- documentation/safedisc.txt Wed Dec 31 18:00:00 1969 +++ documentation/safedisc.txt Thu May 23 20:41:03 2002 @@ -0,0 +1,165 @@ +SafeDisc FAQ + +What is SafeDisc ? +================== + +SafeDisc is a CD copy protection system designed for Microsoft Windows. It is +widely used, especially to protect games. There are currently 2 main versions +of SafeDisc around: SafeDisc 1 and SafeDisc 2. + + +Is my program protected by SafeDisc ? +===================================== + +Look at the root directory of the program CD. The following files are present +on SafeDisc 1 protected programs: + +secdrv.sys +drvmgt.dll +clcd16.dll +clcd32.dll +clokspl.exe +dplayerx.dll (not present on the very first version of SafeDisc) + +In addition to these files, a .icd file contains the encrypted binary, while +the main executable (usually game.exe) is only a wrapper used to load and +decrypt the .icd file. Those two files are sometimes not stored directly in +the root directory of the CD but only in the location of the installed +program on your hard drive. + +For SafeDisc 2, only secdrv.sys and drvmgt.dll can be found on the CD. The +encrypted binary and dplayerx.dll are now stored inside the wrapper (usually +game.exe). + + +How do I run SafeDisc protected programs with Wine ? +==================================================== + +If your version of SafeDisc is supported (see below), your program will run +out-of-the-box (at least for SafeDisc related code :-) if you use NT mode +(--winver nt40, --winver win2k or --winver winxp). Pick the version of NT that +your program is the most likely to support (winxp with a 5 years old program +isn't a good idea). + + +How do I find out which version of SafeDisc my program uses ? +============================================================= + +There is no publicaly available version numbering for SafeDisc. However, it +seems that the version number is stored in the executable as 3 unsigned 32-bit +integers. Using an hexadecimal editor, locate the following byte pattern in +the wrapper (game.exe) + +> 426f475f 202a3930 2e302621 21202059 BoG_ *90.0&!! Y +> 793e0000 y>.. + +There should be 3 unsigned integers right after that, which are respectively +the version, subversion an revision number. + +On some versions of SafeDisc there are 3 null integers following the pattern, +before the version number. You'll then have to look at the 3 unsigned 32-bit +integers right after + +> 426f475f 202a3930 2e302621 21202059 BoG_ *90.0&!! Y +> 793e0000 00000000 00000000 00000000 y>.............. + +Don't forget that the numbers are stored in little endian and in hexadecimal. +To convert numbers stored in little endian format, invert the 4 bytes of the +32 bits integer (e.g. 2d000000 becomes 0000002d which is 45 in decimal). + +So, for instance, a version of 1.35.0 (0x00000001 0x00000023 0x00000000) would +be coded as (in hex) + +> 01 00 00 00 23 00 00 00 00 00 00 00 + +For SafeDisc 1 you can alternatively check the size of the dplayerx.dll file. + +dplayerx.dll size SafeDisc version number +------------------------------------------------------- +156.160 bytes 1.11.0 +165.888 bytes 1.35.0 +172.544 bytes 1.40.4 +173.568 bytes 1.45.0 + +Another very important information is the secdrv.sys version number, which is +unfortunately not easy to determine. For known secdrv.sys versions, you can +check the file size according to the table below. + +secdrv.sys size secdrv.sys version number +--------------------------------------------------------- +14.304 bytes 1.3.0 (SafeDisc 1.11.0) +14.368 bytes 1.3.0 (SafeDisc 1.35.0) +10.848 bytes 1.3.0 (SafeDisc 1.40.4, 1.45.0) +18.768 bytes 2.2.0 (SafeDisc 2.5.30) + +If you have another version of SafeDisc please contribute to these tables. + + +Which version of SafeDisc are currently supported ? +=================================================== + +This hasn't been determined yet. SafeDisc support for Wine has been developped +using a game protected with SafeDisc 1.35.0. SafeDisc 1.40.4 and 1.45.0 have +been reported to work too. All SafeDisc versions which use secdrv.sys 1.3.0 +should work. + +Lower version of secdrv.sys might work too. SafeDisc 2 is not supported yet. + + +How does SafeDisc 1 work ? +========================== + +SafeDisc encrypts the real executable into a .icd file, and uses a wrapper to +decrypt the executable. + +The wrapper contains 3 code sections: `.text', `.txt' and `.txt2'. `.txt' is +encrypted. + +The wrapper starts by decrypting itself, using the checksum of `.text', the +binary content of `.txt2' and some values which depend on debugger detection +tests. If a debugger is loaded, if a software breakpoint is set in the first 8 +bytes of any of the kernel32.dll functions, or if the `.text' and `.txt2' +sections have been modified (this includes setting a software breakpoint in +the code), the `.txt' section won't be decrypted correctly and a crash will +occur. + +On NT, the process of detecting a debugger involves loading the kernel-space +driver secdrv.sys, which I implemented as a user-space code for Wine. On +Windows 95, 98 or Me, it involves executing arbitrary code in ring 0 mode +(kernel mode). This is not supported by Wine (as the underlying OS isn't as +broken as win9x), so that's why you have to use NT mode. + +When the `.txt' section has been decrypted, the wrapper will then check for +the CD key using direct SCSI operations on the CD driver. If the CD key +doesn't match the expected value, a message box pops up to ask you to insert +the original CD in the drive. + +The wrapper then loads and starts clokspl.exe for a still unknown purpose. + +The last stage consists in creating the game.icd in a suspended state. The +suspended process memory is then written with some initialization code, and +SetThreadContext is called to jump to that code. The initialization code will +load dplayerx.dll (which is encrypted in the same way as the wrapper, with 3 +code sections), and will decrypt the main executable. Control is then +transfered to the main executable, and the game starts. + +Have you ever tried to rename game.icd to game.exe, and run it ? It will +crash, because WinMain is encrypted. It needs to be decrypted by the +initialization code, which is found in the wrapper and in dplayerx.dll. + +That's pretty much all I know about SafeDisc. Don't ask me how to remove +SafeDisc from a game. I don't know how to do so. + +If you have more information, especially about debugger detection and +secdrv.sys, please let me know. + + +My version of SafeDisc is not supported. What should I do ? +=========================================================== + +Implement support for it :-) + +For unsupported SafeDisc 1 versions you will probably 'only' need to implement +the secdrv.sys that comes with your program. For SafeDisc 2 things are more +difficult. I'm currently working on that, but please be patient. + --- include/secdrv.h Wed Dec 31 18:00:00 1969 +++ include/secdrv.h Thu May 23 21:51:29 2002 @@ -0,0 +1,54 @@ +/* + * SafeDisc copy protection driver + * + * Copyright 2002 Laurent Pinchart + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_SECDRV_H +#define __WINE_SECDRV_H + +#include "windef.h" + +typedef struct _SECDRV_IOC_IN_BUFFER +{ + DWORD dwVersionMajor, dwVersionMinor, dwVersionPatch; + + DWORD dwCommand; + BYTE bVerificationData[0x400]; + + DWORD cbUserData; + BYTE bUserData[0x100]; +} SECDRV_IOC_IN_BUFFER, *PSECDRV_IOC_IN_BUFFER; + +typedef struct _SECDRV_IOC_OUT_BUFFER +{ + DWORD dwVersionMajor, dwVersionMinor, dwVersionPatch; + + BYTE bVerificationData[0x400]; + + DWORD cbUserData; + BYTE bUserData[0x200]; +} SECDRV_IOC_OUT_BUFFER, *PSECDRV_IOC_OUT_BUFFER; + +#define SECDRV_CMD_INFO_DR (0x0000003c) +#define SECDRV_CMD_INFO_IDT (0x0000003d) +#define SECDRV_CMD_SETUP (0x0000003e) + +extern BOOL SECDRV_DeviceIo_SafeDisc( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, DWORD cbOutBuffer ); + +#endif /* __WINE_SECDRV_H */ --- win32/secdrv.c Wed Dec 31 18:00:00 1969 +++ win32/secdrv.c Thu May 23 22:12:11 2002 @@ -0,0 +1,206 @@ +/* + * SafeDisc copy protection driver + * + * Copyright 2002 Laurent Pinchart + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winerror.h" +#include "file.h" +#include "winioctl.h" +#include "winnt.h" +#include "secdrv.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(vxd); + +static BOOL SECDRV_GetDRInfo( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, LPDWORD lpCbOutBuffer ); +static BOOL SECDRV_GetIdtInfo( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, LPDWORD lpCbOutBuffer ); +static BOOL SECDRV_Setup( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, LPDWORD lpCbOutBuffer ); +static VOID SECDRV_BuildVerificationData( LPVOID lpBuffer ); + +static unsigned int contextDr1 = 0x00000000, contextDr7 = 0x00000400; + +BOOL SECDRV_DeviceIo_SafeDisc( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, DWORD cbOutBuffer ) +{ + PSECDRV_IOC_IN_BUFFER pInBuffer = (PSECDRV_IOC_IN_BUFFER)lpvInBuffer; + PSECDRV_IOC_OUT_BUFFER pOutBuffer = (PSECDRV_IOC_OUT_BUFFER)lpvOutBuffer; + BOOL retv; + + TRACE( "Secdrv %ld.%ld.%ld\n", + pInBuffer->dwVersionMajor, + pInBuffer->dwVersionMinor, + pInBuffer->dwVersionPatch ); + + if ( ! pInBuffer || ! pOutBuffer ) + { + FIXME( "pInBuffer: %p pOutBuffer: %p\n", pInBuffer, pOutBuffer ); + return FALSE; + } + + if ( cbInBuffer != 0x514 ) + { + FIXME( "cbInBuffer: %lx\n", cbInBuffer ); + return FALSE; + } + + if ( cbOutBuffer != 0x610 && cbOutBuffer != 0xc18 ) + { + FIXME( "cbOutBuffer: %lx\n", cbOutBuffer ); + return FALSE; + } + + retv = FALSE; + + switch( pInBuffer->dwCommand ) { + case SECDRV_CMD_INFO_DR: + TRACE( "SECDRV_CMD_INFO_DR\n" ); + retv = SECDRV_GetDRInfo( pInBuffer->bUserData, pInBuffer->cbUserData, + pOutBuffer->bUserData, &pOutBuffer->cbUserData ); + break; + case SECDRV_CMD_INFO_IDT: + TRACE( "SECDRV_CMD_INFO_IDT\n" ); + retv = SECDRV_GetIdtInfo( pInBuffer->bUserData, pInBuffer->cbUserData, + pOutBuffer->bUserData, &pOutBuffer->cbUserData ); + break; + case SECDRV_CMD_SETUP: + TRACE( "SECDRV_CMD_INFO_SETUP\n" ); + retv = SECDRV_Setup( pInBuffer->bUserData, pInBuffer->cbUserData, + pOutBuffer->bUserData, &pOutBuffer->cbUserData ); + break; + default: + FIXME( "unsupported SafeDisc-1 command %lx\n", pInBuffer->dwCommand ); + break; + } + + if ( retv ) + { + pOutBuffer->dwVersionMajor = 3; + pOutBuffer->dwVersionMinor = 5; + pOutBuffer->dwVersionPatch = 0; + SECDRV_BuildVerificationData( pOutBuffer->bVerificationData ); + } + + return retv; +} + +static VOID SECDRV_BuildVerificationData( LPVOID lpBuffer ) +{ + DWORD dwRandom = 0xf367ac7f; + LPDWORD lpDwBuffer = (LPDWORD)lpBuffer; + int i; + + /* lpDwBuffer[0] should be initialized with KeTickCount.LowPart. + * If done, KeTickCount.LowPart should also be accessible from user + * space by a read operation at address 0x7ffe0000. + */ + lpDwBuffer[0] = 0x12345678; + + for ( i = 3; i != 0; --i ) + { + dwRandom = 0x361962e9 - dwRandom * 0x0d5acb1b; + lpDwBuffer[i] = dwRandom; + lpDwBuffer[0] ^= dwRandom; + } +} + +static BOOL SECDRV_GetDRInfo( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, LPDWORD lpCbOutBuffer ) +{ + unsigned int dwDebugRegister; + + if ( cbInBuffer != 0 ) + { + FIXME( "cbInBuffer == %ld when it should be\n + cbInBuffer == 0", cbInBuffer ); + return FALSE; + } + + if ( contextDr1 == 0xbd331200 ) + { + dwDebugRegister = 0x3871dd10; + } + else + { + dwDebugRegister = contextDr7; + } + + *(LPDWORD)lpvOutBuffer = dwDebugRegister & 0x00000500; + *lpCbOutBuffer = 4; + + return TRUE; +} + +static BOOL SECDRV_GetIdtInfo( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, LPDWORD lpCbOutBuffer ) +{ + struct _IDTR { + WORD limit; + DWORD base; + } idtr; + + ULONGLONG *idt; + DWORD dwOffset; + + if ( cbInBuffer != 0 ) + return FALSE; + + asm( "sidtl %0" : "=m"(idtr) ); + idt = (ULONGLONG*)idtr.base; + + /* dwOffset = ( idt[3] & 0x0000ffff ) - ( idt[1] & 0x0000ffff ); */ + dwOffset = 0x1000; + + if ( dwOffset > 0x100 ) + { + *(LPDWORD)lpvOutBuffer = 0x2c8; + } + else + { + *(LPDWORD)lpvOutBuffer = dwOffset; + contextDr1 = 0xbd331200; + } + + *lpCbOutBuffer = 4; + + return TRUE; +} + +static BOOL SECDRV_Setup( LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, LPDWORD lpCbOutBuffer ) +{ + if ( cbInBuffer != 0 ) + return FALSE; + + *(LPDWORD)lpvOutBuffer = 0x5278d11b; + *lpCbOutBuffer = 4; + + return TRUE; +} --- dlls/ntdll/Makefile.in Thu May 23 23:14:32 2002 +++ dlls/ntdll/Makefile.in Thu May 23 21:22:51 2002 @@ -93,6 +93,7 @@ $(TOPOBJDIR)/win32/init.c \ $(TOPOBJDIR)/win32/kernel32.c \ $(TOPOBJDIR)/win32/newfns.c \ + $(TOPOBJDIR)/win32/secdrv.c \ $(TOPOBJDIR)/win32/time.c \ cdrom.c \ critsection.c \ --- win32/device.c Thu May 16 22:31:09 2002 +++ win32/device.c Thu May 23 21:22:51 2002 @@ -40,6 +40,7 @@ #include "msdos.h" #include "miscemu.h" #include "stackframe.h" +#include "secdrv.h" #include "wine/server.h" #include "wine/debug.h" @@ -105,6 +106,12 @@ LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPOVERLAPPED lpOverlapped); + +static BOOL DeviceIo_SECDRV (DWORD dwIoControlCode, + LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, DWORD cbOutBuffer, + LPDWORD lpcbBytesReturned, + LPOVERLAPPED lpOverlapped); /* * VxD names are taken from the Win95 DDK */ @@ -251,6 +258,9 @@ /* WINE additions, ids unknown */ { "MONODEBG.VXD", 0x4242, NULL, DeviceIo_MONODEBG }, + /* SafeDisc copy protection */ + { "SECDRV", 0xef00, NULL, NULL }, + { NULL, 0, NULL, NULL } }; @@ -479,6 +489,12 @@ SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); return FALSE; break; + case CTL_CODE( 0xef00, 0x901, METHOD_NEITHER, FILE_ANY_ACCESS ): + return DeviceIo_SECDRV( dwIoControlCode, + lpvInBuffer, cbInBuffer, + lpvOutBuffer, cbOutBuffer, + lpcbBytesReturned, lpOverlapped ); + break; default: FIXME( "ignored dwIoControlCode=%08lx\n",dwIoControlCode); SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); @@ -1501,6 +1517,31 @@ } return TRUE; } + +/* this is used by SafeDisc copy protection */ +static BOOL DeviceIo_SECDRV(DWORD dwIoControlCode, + LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, DWORD cbOutBuffer, + LPDWORD lpcbBytesReturned, + LPOVERLAPPED lpOverlapped) +{ + switch ((dwIoControlCode >> 2) & 0x0fff) { + case 0x901: /* SafeDisc */ + return SECDRV_DeviceIo_SafeDisc(lpvInBuffer, cbInBuffer, + lpvOutBuffer, cbOutBuffer); + default: + FIXME("(%ld,%p,%ld,%p,%ld,%p,%p): stub\n", + dwIoControlCode, + lpvInBuffer,cbInBuffer, + lpvOutBuffer,cbOutBuffer, + lpcbBytesReturned, + lpOverlapped + ); + break; + } + return FALSE; +} + /* pccard */ static BOOL DeviceIo_PCCARD (DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer,