Quote:
A 10-30 minutes method to remove the activemark protection from a game is presented here:
AM=Activemark
tools required :
PTRW/W9x, SoftIce, C/C++ compiler, basic debugging skills.
Now this method is very cumbersome, my english is bad and if your not familiar with S-ice and such
you can skip all this :)
Background:
AM's Softice detection is quite simple. It tries to open a file like "\\.\SICE", "\\.\NTICE", etc and exits if success. So simply use Yoda's HOKO and you can play with SoftIce as you like.
I needed PTRW 2000 / WinMe because it makes a correct dump, which I wasn't able (I didnt' try hard :) to make
under NT/2k with Sice - addins.
1). Method of finding our entrypoint:
* under nt/2k, launch hoko (use CreateFileA hook and ret -1 if "\\.\NTICE" on CreateFileA)
* launch the AM protected game, wait 1-3 seconds, press ctrl-d, then search for the following pattern in memory :
if you cant find it, g and wait another second, then ctrl-d again. It is there, believe me.
L0 lea edi, [esi + ...]
L1 mov eax, [edi]
L2 or eax, eax
L3 jnz XXX
i.e. s 400000 L -1 8B, 07, 09, C0, 74
OK. note the above instruction, is something like lea edi, [esi + ...]
because this will be our new entry point.
now boot in w9x, load the .exe in PTRW, bpx at L0, and go.
we will receive a break due to our bpx @ L0
(Here I should tell you that even you make the perfect dump at this point, it won't work because:
a) - the .exe already loaded &LoadLibraryA and &GetProcAddress somewhere in memory, making our crack OS-dependant);
B) - you need to skip 2 more checks (2 JMPs);
c) - the game is reading itself, so because our dump is different than the original exe, another error will occur.
you will learn to avoid all these problems in a sec.
for the point c). we will be loading at L0 a little DLL, am.dll, which will overwrite LoadLibraryA and GetProcAddress (at loadtime) in the game (their locations are found very easy :
scroll down the code, you will see a call to [esi + ...] just a few lines below, notice the address on a
paper, I call them LLA. The GPA (GetProcAddress) is just after the LLA. Also note the values of the ESI and EDI registers, as when the EIP will be "L1". (i.e LEA EDI, ... is executed)
(ESI is always 401000, EDI is 401000 + some_value)
so, we will write a little stub. Search down the code, you will notice that we have plenty of space (0s) just
after this kind of jump, at L6...
L4 POPAD
L5 JMP ep
L6 db 0, 0, 0, 0,... (lots of them, cant miss'em :)
so, we'll jump at L6, make a call to loadlibrary, then jump back, then dump the exe.
at L0: overwrite with :
NOP 90
JMP L6 ; (E9 XX XX XX XX)
at L6:
CALL $+7 ; (E8 07 00 00 00)
db 'am.dll', 0 ; (7 bytes)
mov edx, @LLA ; address of LoadLibraryA you've noted before
call edx ; the stack is already with 'am.dll' on it
; return to host
pushad
mov esi, 401000 ; (BE 00 10 40 00) (prev. noted value)
mov edi, ... ; (BF xx xx xx xx) (prev. noted value)
JMP L1
ok, now is time to fix the point B). i.e. get rid of the subsequent AM checks.
search in memory for the address of the following
AS1 = "ActiveMark Client engine could not find a valid volume."
AS2 = "Unable to start ActiveMark Client engine due to an internal error."
ok, now search in memory for instructions : "PUSH AS1" and "PUSH AS2", (they appear only once)
and look just before. Sometimes there is a simple JNZ or JZ instruction, sometimes it takes a
little bit of effort but this is it : you just have to avoid (with a simple JMP) getting here.
(shouldnt' take you more than 5 minutes of debugging).
ok, now everything is set, just "pedump dump.exe", and go
the game should not crash, if we did it right.
Now, boot again in nt/w2k, make a quick tool that will scan dump.exe for "KERNEL32.DLL" (case sensitive)
where we find a PE import section. (a routine is presented below)
and fix the imports just before it...
---------------------------------------------
Now, all we need is our injected DLL, "am.dll"
the scope of this DLL is to check if the game tries to open itself, and present him with the
original exe if so :).
For this you could also use Yoda's HOKO. (great tool, too bad its for money)
This am.dll presented here is configurable, meaning am_hooks.dll will have 4x2 bytes containing the
addresses of LoadLibraryA and GetProcAddress in the game. Quick and DIRTY :
With this, move the original game xxxx.exe into xxxx.ex_, copy the dumped.exe as xxxx.exe,
compile & copy the am.dll into the game dir, fix the imports on the dumped.exe, edit am_hooks.bin
and enter the addresses of LoadLibraryA and GetProcAddress, and there you go, launch the exe
and it will go. No more AM.
If something goes wrong, you will have to figure out for yourself
---------------------------------------------------------------
// am.cpp : Defines the entry point for the DLL application.
//
#include
typedef HANDLE WINAPI _LoadLibraryA_t
(
LPCTSTR lpLibraryName
);
typedef HANDLE WINAPI _GetProcAddress_t
(
HMODULE hModule,
LPCTSTR lpFunctionName
);
typedef HANDLE WINAPI _CreateFile_t(
LPSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
static char g_szGame[MAX_PATH + 1];
static long g_szGameLen = 0;
static char* g_szHooksPointersFile = "am_hooks.bin";
DWORD g_pfnCreateFile_ORIG = 0;
DWORD g_pfnLoadLibraryA_ORIG = 0;
DWORD g_pfnGetProcAddress_ORIG = 0;
DWORD g_bLoadingKernel32 = FALSE;
HANDLE WINAPI xCreateFile(LPSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
HANDLE WINAPI xLLA(LPCTSTR lpLibraryName);
HANDLE WINAPI xGPA(HMODULE hModule, LPCTSTR lpFunctionName);
void FixPointers()
{
DWORD dwDummy;
DWORD dwLLA = 0;
DWORD dwGPA = 0;
HANDLE hFile = CreateFile(g_szHooksPointersFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE != hFile)
{
ReadFile(hFile, &dwLLA, 4, &dwDummy, NULL);
ReadFile(hFile, &dwGPA, 4, &dwDummy, NULL);
CloseHandle(hFile);
*((DWORD*)dwLLA) = (DWORD)xLLA;
*((DWORD*)dwGPA) = (DWORD)xGPA;
}
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// initialize the pointers
g_pfnCreateFile_ORIG = (DWORD)CreateFileA;
g_pfnLoadLibraryA_ORIG = (DWORD)LoadLibraryA;
g_pfnGetProcAddress_ORIG = (DWORD)GetProcAddress;
g_szGame[0] = '\0';
// Get self name
g_szGameLen = GetModuleFileName(GetModuleHandle(NULL), g_szGame, MAX_PATH);
// mark pointers in the game
FixPointers();
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
HANDLE WINAPI xLLA(LPCTSTR lpLibraryName)
{
long k, nLen;
for (k = nLen = 0; !IsBadReadPtr(&lpLibraryName[k], 1) && lpLibraryName[k] != '\0'; k++)
nLen++;
if (nLen == 12)
{
if (lpLibraryName[0] | 0x20 == 'k' &&
lpLibraryName[1] | 0x20 == 'e' &&
lpLibraryName[2] | 0x20 == 'r' &&
lpLibraryName[3] | 0x20 == 'n' &&
lpLibraryName[4] | 0x20 == 'e' &&
lpLibraryName[5] | 0x20 == 'l' &&
lpLibraryName[6] | 0x20 == '3' &&
lpLibraryName[7] | 0x20 == '2' &&
lpLibraryName[8] | 0x20 == '.' &&
lpLibraryName[9] | 0x20 == 'd' &&
lpLibraryName[10] | 0x20 == 'l' &&
lpLibraryName[11] | 0x20 == 'l')
{
g_bLoadingKernel32 = 1;
}
else
{
g_bLoadingKernel32 = 0;
}
}
_LoadLibraryA_t* pfnMyLoadLibraryA = (_LoadLibraryA_t*)g_pfnLoadLibraryA_ORIG;
return (*pfnMyLoadLibraryA)(lpLibraryName);
}
HANDLE WINAPI xGPA(HMODULE hModule, LPCTSTR lpFunctionName)
{
if (g_bLoadingKernel32)
{
long k, nLen;
for (k = nLen = 0; !IsBadReadPtr(&lpFunctionName[k], 1) && lpFunctionName[k] != '\0'; k++)
nLen++;
if (11 == nLen)
{
if ((lpFunctionName[0] | 0x20) == 'c' &&
(lpFunctionName[1] | 0x20) == 'r' &&
(lpFunctionName[2] | 0x20) == 'e' &&
(lpFunctionName[3] | 0x20) == 'a' &&
(lpFunctionName[4] | 0x20) == 't' &&
(lpFunctionName[5] | 0x20) == 'e' &&
(lpFunctionName[6] | 0x20) == 'f' &&
(lpFunctionName[7] | 0x20) == 'i' &&
(lpFunctionName[8] | 0x20) == 'l' &&
(lpFunctionName[9] | 0x20) == 'e' &&
(lpFunctionName[10] | 0x20) == 'a')
{
return xCreateFile;
}
}
}
_GetProcAddress_t* pfnMyGetProcAddress = (_GetProcAddress_t*)g_pfnGetProcAddress_ORIG;
return (*pfnMyGetProcAddress)(hModule, lpFunctionName);
}
HANDLE WINAPI xCreateFile(LPSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
if (IsBadReadPtr(lpFileName, 1))
return INVALID_HANDLE_VALUE;
long k, nLen;
for (k = nLen = 0; lpFileName[k] != '\0'; k++)
nLen++;
if (g_szGameLen == nLen)
{
for (k = 0; k < nLen; k++)
{
if ((lpFileName[k] | 0x20) != (g_szGame[k] | 0x20))
break;
}
if (k == nLen)
{
lpFileName[k -1] = '_';
}
}
_CreateFile_t* pfnMyCreateFile = (_CreateFile_t*)g_pfnCreateFile_ORIG;
return (*pfnMyCreateFile)(lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
}
---------------------------------------------------------------
and the "optimised", DIRTY too, routine for fixing imports :
bool FixImports(char* pszFileName)
{
CString strOrigGame = CString(pszFileName);
char* szFileName = (LPSTR)(LPCSTR)strOrigGame;
HANDLE hFile = CreateFile(szFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
return false;
}
DWORD dwDummy;
DWORD dwSize = GetFileSize(hFile, &dwDummy);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwSize, "__KRNL32OFFS_SCAN2");
if (!hMap)
{
printf("CreateFileMapping failed\n");
}
DWORD* pMapMem = (DWORD*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
ULONG _bFound = 0;
ULONG _nOffset = 0;
if (pMapMem)
{
__asm
{
cld
mov _bFound, 0
mov ecx, dwSize
shr ecx, 2
mov edi, pMapMem
_loop:
mov eax, 0x4e52454b // 'KERN'
repnz scasd
cmp ecx, 0
jnz _found1
jmp _notfound
_found1: cmp [edi], 0x32334c45 // 'EL32'
jz _found2
jmp _notfound
_found2: cmp [edi + 4], 0x4c4c442e // '.DLL'
jnz _notfound
inc ecx
shl ecx, 2
mov eax, dwSize
and eax, 0xfffffffc
sub eax, ecx
mov _nOffset, eax
jmp _done
_notfound:
cmp ecx, 8
ja _loop
_done:
}
}
else
{
return false;
}
UnmapViewOfFile(pMapMem);
DWORD dwAddressOffset = _nOffset - 0x70;
CloseHandle(hMap);
CloseHandle(hFile);
char buff[512];
char libbuff[1024];
GetSystemDirectory(buff, 512);
DWORD a[24];
HINSTANCE h;
memset(a, 0, 24 * sizeof(DWORD));
a[0] = (DWORD)LoadLibrary;
a[1] = (DWORD)GetProcAddress;
a[2] = (DWORD)ExitProcess;
a[4] = (DWORD)RegCloseKey;
strcpy(libbuff, buff);
strcat(libbuff, "\\comdlg32.dll");
h = LoadLibrary(libbuff);
if (h)
{
a[6] = (DWORD)GetProcAddress(h, "PrintDlgA");;
FreeLibrary(h);
}
strcpy(libbuff, buff);
strcat(libbuff, "\\crypt32.dll");
h = LoadLibrary(libbuff);
if (h)
{
a[8] = (DWORD)GetProcAddress(h, "CertOpenStore");;
FreeLibrary(h);
}
a[10] = (DWORD)::DPtoLP;
strcpy(libbuff, buff);
strcat(libbuff, "\\netapi32.dll");
h = LoadLibrary(libbuff);
if (h)
{
a[12] = (DWORD)GetProcAddress(h, "Netbios");
FreeLibrary(h);
}
a[14] = (DWORD)CoInitialize;
a[16] = (DWORD)ExtractIconA;
a[18] = (DWORD)::GetDC;
strcpy(libbuff, buff);
strcat(libbuff, "\\wininet.dll");
h = LoadLibrary(libbuff);
if (h)
{
a[20] = (DWORD)GetProcAddress(h, "InternetOpenA");;
FreeLibrary(h);
}
strcpy(libbuff, buff);
strcat(libbuff, "\\winmm.dll");
h = LoadLibrary(libbuff);
if (h)
{
a[22] = (DWORD)GetProcAddress(h, "joyGetPos");;
FreeLibrary(h);
}
CFile f;
if (f.Open(strOrigGame, CFile::modeReadWrite))
{
f.Seek(dwAddressOffset, CFile::begin);
f.Write(a, 24 * sizeof(DWORD));
f.Close();
}
else
{
return false;
}
return true;
}