首页
社区
课程
招聘
[转帖]Activemark破解方法
发表于: 2005-12-2 10:23 6451

[转帖]Activemark破解方法

2005-12-2 10:23
6451
为了防止非常拷贝,越来越多的游戏公司开始使用Trymedia的Activemark技术来保护自己的产品。幻想游戏中大热的“养鱼大亨”和“神秘视线”就是用这一技术来限制试玩的时间的。

    “这套系统利用一个CD激活码与购买者当前系统的“唯一硬件设置指纹”进行配对,如果这个激活码移植到其他电脑上,指纹也就无法与之实现匹配,那么游戏就会变成演示模式。另外,这个认证过程需要通过互联网或电话进行,可能是需要从Trymedia读取与激活码匹配的指纹码,另外每个激活码可搭配多个指纹的做法也允许用户在多台机器上安装同一套游戏――只要游戏开发商同意。当然,每一个备份都需要有一个唯一的指纹和之前的激活码相匹配。”因此,普通的“时光倒流”软件已无用武之地。

    好在破解高手早已出招,这是本人在国外BLOG上看到的一篇破解技术文件,顺手翻译了一下。虽然文章不是原创,但翻译也算是一种创作吧,呵呵。

    如果哪位达人有兴趣,可以去http://www.addict3d.org/index.php?page=viewarticle&type=security&ID=314浏览原文。

    好了,正文开始:

三十分钟解除Activemark保护

AM=Activemark

所需软件 :
PTRW/W9x, SoftIce, C/C++ compiler, basic debugging skills.

这个方法相当麻烦,而且本人不善表达,如果您不熟悉S-ice等,我看还是免了 :)

背景:

AM对SOFTICE的防护十分简单,只是尝试打开诸如"\\.\SICE", "\\.\NTICE"等等文件,检查是否成功。因此只要用Yoda的HOKO(本人也不知道这是什么东西,译注),你就可以随心所欲使用SoftIce了。
我还用了PTRW 2000 / WinMe,因为这样可以正确脱壳。我在NT/2K下无法用S-ICE的插件脱壳(也许我很懒:-b)。

1). 找到入口点:
* 在NT/2K下, 运行HOKO(use CreateFileA hook and ret -1 if "\\.\NTICE" on CreateFileA)
* 运行由AM保护的游戏,等1-3秒,按press ctrl-d,然后在内存中搜索以下内容:
(如果找不到,按g再等几秒,然后再搜索,肯定能找到,相信我)。

L0 lea edi, [esi + ...]
L1 mov eax, [edi]
L2 or eax, eax
L3 jnz XXX

例如:s 400000 L -1 8B, 07, 09, C0, 74

OK,记下上面的指令,像lea edi, [esi + ...]
这将是我们的新入口。

现在启动进入w9x,在PTRW里打开游戏文件,bpx设定L0,然后运行.
我们可以得到一个断点。

(要说明的是,在此处脱壳脱得再好也没有用,这是因为:
a) - 游戏已经在内存中加载了&LoadLibraryA和&GetProcAddress;
b) - 还应当跳过另外2个检查(2 JMPs);
c) - 这时游戏正在读取自身,而脱壳的程序不同与原始的EXE文件,于是会产生另外的错误。)

下面就教你一手如何避免这些问题。

根据c). 我们在游戏L0处加载一个小小的DLL - AM.dll,来将LoadLibraryA和GetProcAddress覆盖(加载时),其位置十分容易找到:
向下滚动代码,你可以找到一个对[esi + ...]的调用,记下下面几行的地址,我们称之为LLA。GPA(GetProcAddress)就在LLA后面。同时记下ESI和EDI中的值,而EIP应该是“L1”(例如,LEA EDI, ... 被执行)
(ESI总是401000, EDI是401000 + 别的值)

接着,我们长话短说。向下搜索代码,你会发现一大堆的0,跳过这些,在L6...
L4 POPAD
L5 JMP ep
L6 db 0, 0, 0, 0,... (有很多很多,不会错过的:)

那么,我们要在L6跳转,调用loadlibrary,然后跳回来,再脱壳。

在 L0,改成:
NOP 90
JMP L6 ; (E9 XX XX XX XX)

在 L6:
CALL $+7 ; (E8 07 00 00 00)
db 'am.dll', 0 ; (7 bytes)
mov edx, @LLA ; 这就是前面记下的LoadLibraryA地址
call edx ; 堆栈里已经加载了'am.dll'
; 返回
pushad
mov esi, 401000 ; (BE 00 10 40 00) (前面记下的ESI的值)
mov edi, ... ; (BF xx xx xx xx) (前面记下的EDI的值)
JMP L1

好了,现在可以搞定问题b). 去掉后面的AM检查。

在内存中搜索以下地址
AS1 = "ActiveMark Client engine could not find a valid volume."
AS2 = "Unable to start ActiveMark Client engine due to an internal error."

然后在内存中搜索指令: "PUSH AS1"和"PUSH AS2", (他们只出现一次)
然后向前查看。有时只有简单的JNZ或JZ指令,而有时则要花上些功夫,不过总之,你只要避免(用简单的JMP)到那里(这一段本人也看不明白,这位仁兄的表达实在…,译注)
(不会超过5分钟的debug).

OK,现在一切就绪,只要"pedump dump.exe",然后运行。
游戏应该不会崩溃,只要没做错。

然后,重新回到NT/W2K,在dump.exe中搜索"KERNEL32.DLL" (注意大小写)
我们将找到一个PE入口段.  

修改这个入口...

---------------------------------------------
好,现在我们还缺一个DLL, "am.dll"

这个DLL的目的是检查这个游戏是否试图打开自身,如果是,则向游戏提交原始的EXE文件 :-).

这也要用到Yoda的HOKO. (超棒的工具,可惜要花钱的)

以下的am.dll是可配置的,也就是说am_hooks.dll将用4x2字节存贮游戏的LoadLibraryA和GetProcAddress的地址。干脆利落:

这样,将原始的xxxx.exe文件改名为xxxx.ex_,将dumped.exe复制为xxxx.exe,
编译并将am.dll复制到游戏文件夹下,修改dumped.exe的入口,编辑am_hooks.bin
输入LoadLibraryA和GetProcAddress的地址,然后运行游戏,
好了,再也没有AM了。

如果什么地方不对,你只能自己搞定了

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (3)
雪    币: 136
活跃值: (429)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
2
am.dll的原代码:

===============================================

// 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;
}
2005-12-2 10:23
0
雪    币: 898
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
3
可惜拿不到Activemark主程序
Patch Activemark应该没有太大难度
2005-12-2 10:29
0
雪    币: 61
活跃值: (160)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
4
学习。。。
2005-12-2 10:54
0
游客
登录 | 注册 方可回帖
返回
//