【文章标题】: rFactor 1.250 破解过程详述
【文章作者】: noword
【作者主页】: bbs.srfc.com.cn
【软件名称】: rFactor
【下载地址】: http://www.rfactor.net/index.php?page=downloads
【加壳方式】: ActiveMark
【保护方式】: ActiveMark + 阴险的暗桩
【使用工具】: FileMon, OllyICE
【操作平台】: Win XP
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【简介】
rFactor是ISI最新的一款模拟赛车游戏,售价34.99欧元。游戏原始引擎最初用于ISI公司99年出版的Sports Car GT (SCGT)模拟赛车游戏,在经过了近8年的不断发展,现在rF采用的是经过ISI全面改进和升级的被称为isiMotor 2.0的开发环境,这是ISI积累了多年赛车游戏经验的技术结晶。rF是这款游戏引擎的首部作品,随后FIA官方游戏GT Legends、 GTR 2也相继采用了这款游戏引擎。相比较ISI过去的游戏引擎,rF增加了真实的动力学,更加真实的操作感,更加逼真的图像,让玩家体验身临其境的感觉。
以前,rFactor一直是一个来自波兰叫Passeli的组织或个人破解的,而在1.150出来之后却迟迟不见动静,倒是有两个假档出现在emule上,于是只好亲自出马,破而解之,造福于天下。
07年1月25日,1.250的补丁出来了,拿到手后,粗看之下同1.150的加密方式没什么不同,但是事后证明这只是表相……
【保护手法】
该游戏使用了ActiveMark(http://www.trymedia.com)的保护方案,可执行文件被其加壳,启动后如果是未付费用户,则跳出付费窗口,且只能进入有一定时间限制的demo模式。
用FileMonitor可发现,rFactor.exe会读写在C:\Documents and Settings\All Users\Application Data\Trymedia下的两个目录 data 和 licenses。
data目录中有4个文件:
{51AE6369-615F-A146-E860-D9D8FF2E73D5}
{F5E9419B-BF55-B4A2-21B9-EA3C4E7E854E}
{C405FC19-D234-E01E-D4FF-F24278204F43}
{439B1256-E959-D772-E973-7835AE2AAE1C}
每次运行后文件中的内容都会变化,应该是记录了游戏运行时间等信息,这样开发商就能限制用户的试玩时间了。
而licensens目录中是空的,程序会试图打开一个名为d27248b8604abd930ed681c3033da219.lcn的文件,密匙应该就在其中。
ps:关于ActiveMark,在http://tutorials.accessroot.com/有好几篇教程,图文并茂,非常详细。
【初步破解】
用OllyICE载入rFactor.exe,停在了入口00EA9003处,按Alt-M:
00400000 00001000 rFactor
00401000 0023E000 rFactor .data
0063F000 000B4000 rFactor .bss
006F3000 003FA000 rFactor .text
00AED000 00009000 rFactor .rsrc
00AF6000 0016E000 rFactor .rdata 第二层
00C64000 00004000 rFactor .idata
00C68000 0000A000 rFactor .rsrc
00C72000 00231000 rFactor .data
00EA3000 00001000 rFactor .idata
00EA4000 00005000 rFactor .rsrc
00EA9000 00018000 rFactor .bss 第一层
00EC1000 00001000 rFactor .text
00EC2000 0000A000 rFactor .rdata
00ECC000 00001000 rFactor .idata
00636B03 |> \68 04010000 push 104 ; /BufSize = 104 (260.)
00636B08 |. BE 60B3AE00 mov esi, 00AEB360 ; |
00636B0D |. 56 push esi ; |PathBuffer => rFactor.00AEB360
00636B0E |. 53 push ebx ; |hModule
00636B0F |. 881D 64B4AE00 mov byte ptr [AEB464], bl ; |
00636B15 |. 90 nop ; \GetModuleFileNameA
00636B16 |. E8 E24E5400 call 00B7B9FD
00636B1B |. A1 68C9AE00 mov eax, dword ptr [AEC968]
地址 线程 命令 ; 寄存器和注释
00B7BA02 主 jmp short 00B7BA07
00B7BA07 主 call 00C40794 ; ESP=0012FF0C
00C40794 主 push -1 ; ESP=0012FF08
00C40796 主 push eax ; ESP=0012FF04
00C40797 主 mov eax, dword ptr fs:[0] ; EAX=0012FFB0
00C4079D 主 push eax ; ESP=0012FF00
00C4079E 主 mov eax, dword ptr [esp+C] ; EAX=00B7BA0C
00C407A2 主 mov dword ptr fs:[0], esp
00C407A9 主 mov dword ptr [esp+C], ebp
00C407AD 主 lea ebp, dword ptr [esp+C] ; EBP=0012FF0C
00C407B1 主 push eax ; ESP=0012FEFC
00C407B2 主 retn ; ESP=0012FF00
00B7BA0C 主 jmp short 00B7BA10
00B7BA10 主 sub esp, 20 ; FL=0, ESP=0012FEE0
00B7BA13 主 jmp short 00B7BA17
00B7BA17 主 mov al, byte ptr [ebp+13] ; EAX=00B7BA00
00B7BA1A 主 push ebx ; ESP=0012FEDC
00B7BA1B 主 push esi ; ESP=0012FED8
00B7BA1C 主 jmp short 00B7BA20
00B7BA20 主 jmp 00B7AD3E
00B7AD3E 主 xor esi, esi ; FL=PZ, ESI=00000000
00B7AD40 主 jmp short 00B7AD44
00B7AD44 主 push edi ; ESP=0012FED4
00B7AD45 主 jmp short 00B7AD49
00B7AD49 主 push esi ; ESP=0012FED0
00B7AD4A 主 jmp 00B7B6B9
00B7B6B9 主 lea ecx, dword ptr [ebp-2C] ; ECX=0012FEE0
00B7B6BC 主 push 00B7A2E8 ; ESP=0012FECC
00B7B6C1 主 retn ; ESP=0012FED0
00B7A2E8 主 mov byte ptr [ebp-2C], al
00B7A2EB 主 push 00B7A8A4 ; ESP=0012FECC
00B7A2F0 主 retn ; ESP=0012FED0
00B7A8A4 主 jmp short 00B7A8A8
00B7A8A8 主 jmp 00B7BBDC
00B7BBDC 主 call 00B66F53 ; ESP=0012FECC
00B66F53 主 cmp byte ptr [esp+4], 0
00B66F58 主 push esi ; ESP=0012FEC8
00B66F59 主 mov esi, ecx ; ESI=0012FEE0
00B66F5B 主 je short 00B66F7F
00B66F7F 主 and dword ptr [esi+4], 0
00B66F83 主 and dword ptr [esi+8], 0
00B66F87 主 and dword ptr [esi+C], 0
00B66F8B 主 pop esi ; ESP=0012FECC, ESI=00000000
00B66F8C 主 retn 4 ; ESP=0012FED4
00B7BBE1 主 jmp short 00B7BBE5
00B7BBE5 主 push 00B7B05E ; ESP=0012FED0
00B7BBEA 主 retn ; ESP=0012FED4
00B7B05E 主 push dword ptr [ebp+10] ; ESP=0012FED0
00B7B061 主 jmp short 00B7B065
00B7B065 主 mov ebx, dword ptr [ebp+C] ; EBX=00AEB360
00B7B068 主 jmp 00B7A3CB
00B7A3CB 主 mov dword ptr [ebp-4], esi
00B7A3CE 主 push ebx ; ESP=0012FECC
00B7A3CF 主 jmp short 00B7A3D3
00B7A3D3 主 jmp dword ptr [B7A3DB]
00B7B804 主 push dword ptr [ebp+8] ; ESP=0012FEC8
00B7B807 主 call dword ptr [kernel32.GetModuleFileNameA] ; EAX=0000001B, ECX=7C93056D, EDX=098A0000, ESP=0012FED4
...
00C423DE 主 call dword ptr [ntdll.RtlAllocateHeap] ; FL=PZ, EAX=01C44698, ECX=7C9306EB, EDX=01220006, ESP=0012FE34
...
00C423DE 主 call dword ptr [ntdll.RtlAllocateHeap] ; FL=PZ, EAX=01C446D0, ECX=7C9306EB, EDX=01220005, ESP=0012FC6C
...
00C423DE 主 call dword ptr [ntdll.RtlAllocateHeap] ; FL=PZ, EAX=01C4A598, ECX=7C9306EB, EDX=01220004, ESP=0012FCA4
...
00C0D080 主 call dword ptr [kernel32.FindFirstFileA] ; FL=PZ, EAX=00152820, ECX=000000D9, EDX=0012FAC0, ESP=0012FD38
...
00C0D0E5 主 call dword ptr [kernel32.FindClose] ; FL=PS, EAX=00000001, ECX=7C80EE67, EDX=003B0000, ESP=0012FD38
...
00C0D080 主 call dword ptr [kernel32.FindFirstFileA]] ; FL=PZ, EAX=00152820, ECX=000000D9, EDX=0012FAC0, ESP=0012FD38
...
00C0D0E5 主 call dword ptr [kernel32.FindClose] ; FL=PS, EAX=00000001, ECX=7C80EE67, EDX=003C0000, ESP=0012FD38
...
00C0D080 主 call dword ptr [kernel32.FindFirstFileA]] ; FL=PZ, EAX=00152820, ECX=000000D9, EDX=0012FAC0, ESP=0012FD38
...
00C0D0E5 主 call dword ptr [kernel32.FindClose] ; FL=PS, EAX=00000001, ECX=7C80EE67, EDX=003D0000, ESP=0012FD38
...
00C422A4 主 call dword ptr [ntdll.RtlFreeHeap] ; FL=PZ, EAX=0278E201, ECX=7C93056D, EDX=01220003, ESP=0012FCE4
...
00C422A4 主 call dword ptr [ntdll.RtlFreeHeap] ; FL=PZ, EAX=01C4A501, ECX=7C93056D, EDX=01230004, ESP=0012FE80
...
00C422A4 主 call dword ptr [ntdll.RtlFreeHeap] ; FL=PZ, EAX=01C44601, ECX=7C93056D, EDX=01240005, ESP=0012FE80
...
00B7AF89 主 jmp 00B7B0EB
00B7B0EB 主 leave ; ESP=0012FF10, EBP=0012FF38
00B7B0EC 主 retn 0C ; ESP=0012FF20
断点位于 rFactor.00636B1B
00636B1B 主 mov eax, dword ptr [AEC968]
RUN 跟踪已关闭
0053BAD0 56 push esi
0053BAD1 8BF1 mov esi, ecx
0053BAD3 807E 70 00 cmp byte ptr [esi+70], 0
0053BAD7 . 75 12 jnz short 0053BAEB
0053BAD9 . 6A 00 push 0
0053BADB . 90 nop
0053BADC . E8 518F6300 call 00B74A32
0053BAE1 . 24 01 and al, 1
0053BAE3 . 83C4 04 add esp, 4
0053BAE6 . 04 01 add al, 1
0053BAE8 . 8846 70 mov byte ptr [esi+70], al
0053BAEB > 807E 70 02 cmp byte ptr [esi+70], 2
0053BAEF . 5E pop esi
0053BAF0 . 0F94C0 sete al
0053BAF3 . C3 retn
0053BAD0 33C0 xor eax, eax
0053BAD2 40 inc eax
0053BAD3 C3 retn
do
{
WaitForDebugEvent (&DBEvent, INFINITE);
GetThreadContext (pi.hThread, &Regs);
ContinueStatus = DBG_CONTINUE;
switch (DBEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
{
switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
{
ContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
}
case EXCEPTION_SINGLE_STEP:
if (Regs.Eip == OEP1) //到达第二层的OEP
{
OEPStatus++;
Regs.Dr0 = 0;
Regs.Dr7 = 0x404;
SetThreadContext (pi.hThread, &Regs);
SuspendOtherThread (pi); //挂起除主线程之外的其他线程
ApplyPatch (pi.hProcess, patch); //写入补丁数据
}
else if (Regs.Eip == OEP2) //到达真正的OEP
{
OEPStatus++;
Regs.Dr1 = Regs.Dr7 = 0;
SetThreadContext (pi.hThread, &Regs);
SuspendOtherThread (pi); //挂起除主线程之外的其他线程
ContinueDebugEvent (DBEvent.dwProcessId,
DBEvent.dwThreadId, ContinueStatus);
STOP = TRUE;
}
else
ContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
case EXCEPTION_BREAKPOINT:
//是否在第一层或第二层
if ((Regs.Eip >= 0xEA9000 && Regs.Eip <= 0xEC1000)
|| (Regs.Eip >= 0xAED000 && Regs.Eip <= 0xC64000))
ContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
//两处int3陷阱,必须将EAX设置为-1
if (Regs.Eip == 0xEAAD18 || Regs.Eip == 0xEB61CF)
{
Regs.Eax = -1;
SetThreadContext(pi.hThread, &Regs);
}
break;
}
break;
}
case LOAD_DLL_DEBUG_EVENT:
//每次载入新的DLL时,都在OEP处下一次硬件断点
if (OEPStatus == 0)
{
Regs.Dr0 = OEP1;
Regs.Dr1 = OEP2;
Regs.Dr7 = 0x505;
SetThreadContext (pi.hThread, &Regs);
}
else if (OEPStatus == 1)
{
Regs.Dr1 = OEP2;
Regs.Dr7 = 0x404;
SetThreadContext (pi.hThread, &Regs);
}
break;
case EXIT_PROCESS_DEBUG_EVENT:
STOP = TRUE;
ExitProcess (-1);
break;
}
if (!STOP)
{
ContinueDebugEvent (DBEvent.dwProcessId, DBEvent.dwThreadId,
ContinueStatus);
}
}
while (!STOP);
void
SuspendOtherThread (PROCESS_INFORMATION pi)
{
THREADENTRY32 te32 = { 0 };
te32.dwSize = sizeof (THREADENTRY32);
HANDLE hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0);
if (Thread32First (hThreadSnap, &te32))
{
do
{
if (te32.th32OwnerProcessID == pi.dwProcessId
&& te32.th32ThreadID != pi.dwThreadId)
{
HANDLE hThread = OpenThread (THREAD_SUSPEND_RESUME, FALSE,
te32.th32ThreadID);
SuspendThread (hThread);
}
}
while (Thread32Next (hThreadSnap, &te32));
}
CloseHandle (hThreadSnap);
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!