首页
社区
课程
招聘
[原创]分页机制 打造自己的变速齿轮
发表于: 2011-11-4 02:03 28744

[原创]分页机制 打造自己的变速齿轮

2011-11-4 02:03
28744
传统变速齿轮通过HOOK GetTickCount时间函数 之后修改返回值获得效果。

这样做的好处是方便,不好是容易被一些反的游戏检测出来.或者游戏自己仿写一个GetTickCount也不难.

因为分析GetTickCount的动作。其实是访问了进程内存地址$7FFE0000。

进程内存地址$7FFE0000上是一个不断更新的数值。其更新是由系统来完成。系统中$FFDF0000地址正是跟进程上映射到相同的物理地址。

那么我们可以在系统中再创建一个空间,作为一个假的时间记录空间,然后由系统更新$FFDF0000的时候顺便更新这个假的记录空间,当然这个是按照我们的意思来更新(即时间变快),然后将这要改变速度的目标进程的内存地址$7FFE0000映射改为我们这个假的空间。那么无论是 GetTickCount函数获得的都是只按我们意愿改变的时间。

原理说完了。来看下代码:

首先。我们在驱动入口时,此时位于系统上下文,我们应该做些什么。

1.获得系统更新时间函数的地址:
 RTlInitAnsiString(@g_KeUpdateSystemTime_Name_Ansi,'KeUpdateSystemTime');
 RtlAnsiStringToUnicodeString(@g_KeUpdateSystemTime_Name_Unicode,@g_KeUpdateSystemTime_Name_Ansi,true);   //字符
 g_KeUpdateSystemTime_Address:=MmGetSystemRoutineAddress(@g_KeUpdateSystemTime_Name_Unicode);
 g_KeUpdateSystemTime_Address_C:=cardinal(g_KeUpdateSystemTime_Address);
 RtlFreeUnicodeString(@g_KeUpdateSystemTime_Name_Unicode);


2.因为我们要HOOK这个函数(为的是系统更新时间时一并替我们更新假的空间上数据),所以要看下这个函数的版本是否适合(我用的是XP SP3),这个叫硬编码吧?我使用了....没办法..技术的确不高...
 if g_KeUpdateSystemTime_Address<>nil then
 begin
  g_pmdlSystemCall:=MmCreateMdl(nil,g_KeUpdateSystemTime_Address,$6D);
  if g_pmdlSystemCall<>nil then
  begin
   MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
   g_pmdlSystemCall^.MdlFlags:=g_pmdlSystemCall^.MdlFlags or MDL_MAPPED_TO_SYSTEM_VA;
   MappedSystemCallTable:=MmMapLockedPages(g_pmdlSystemCall,KernelMode);
   Byte_KeUpdateSystemTime:=g_KeUpdateSystemTime_Address;
   if Byte_KeUpdateSystemTime^=$B9 then
   begin
    DbgPrint('版本匹配成功1!'#13#10);
    Cardinal_KeUpdateSystemTime:=PTR(g_KeUpdateSystemTime_Address_C+1);
    if Cardinal_KeUpdateSystemTime^=$FFDF0000 then
    begin
     DbgPrint('版本匹配成功2!'#13#10);
     SysBBOK:=true;
    end;
   end;
  end;
 end;


3.改写时间更新函数的代码,一个跳转跳到我们的自己的代码的地址上去。。
  HST1Next:=PTR(g_KeUpdateSystemTime_Address_C+5);
  HST1To:=cardinal(@HST1)-g_KeUpdateSystemTime_Address_C-5;
  Cardinal_KeUpdateSystemTime:=PTR(g_KeUpdateSystemTime_Address_C+1);
  Byte_KeUpdateSystemTime:=g_KeUpdateSystemTime_Address;
  Cardinal_KeUpdateSystemTime^:=HST1To;
  Byte_KeUpdateSystemTime^:=$E9;


4.我们自己的代码,看看做了什么。
procedure HST1;
asm
 push eax
 push ebx
 push ecx
 push edx
 call HST1EX    //调用顺便更新假空间的函数
 pop edx
 pop ecx
 pop ebx
 pop eax
 mov ecx,$FFDF0000   //恢复我们改写的内容,在系统更新时间函数上我们的跳转覆盖了这个指令
 jmp HST1Next           //跳回去下一个指令
end;


5.而HST1EX(调用顺便更新假空间的函数)所做的:
procedure HST1EX;
label L;
begin
 if HST1_OPEN=1 then
 begin
  HST1_GT_Temp1:=PTR($FFDF0004);
  HST1_GT1_INGG:=HST1_GT_Temp1^;
  HST1_GT_Temp1:=PTR($FFDF0000);                   //取系统上真实的时间.
  HST1_GT1:=HST1_GT_Temp1^;
  HST1_GT_Temp2:=PTR($FFDF0008);
  HST1_GT2:=HST1_GT_Temp2^;
  HST1_GT_Temp3:=PTR($FFDF0014);
  HST1_GT3:=HST1_GT_Temp3^;
  
  if HST1_GT_JiaShu=0 then
  begin
   HST1_GT1_Save:=HST1_GT1;
   HST1_GT2_Save:=HST1_GT2;
   HST1_GT3_Save:=HST1_GT3;
  end;

//加快的时间=真实系统时间+(真实系统时间-原记录系统时间)*要改变的速度倍数
  HST1_GT1_ING:=HST1_GT1+(HST1_GT1-HST1_GT1_Save)*(HST1_GT_JiaShu);

//  HST1_GT2_ING:=HST1_GT2+(HST1_GT2-HST1_GT2_Save)*(HST1_GT_JiaShu);
  HST1_GT_CC1:=HST1_GT_JiaShu;
  HST1_GT_CC2:=HST1_GT2-HST1_GT2_Save;
  //HST1_GT2_ING:=HST1_GT_CC1*HST1_GT_CC2;
  llmul(HST1_GT_CC1,HST1_GT_CC2,HST1_GT2_ING);
  HST1_GT2_ING:=HST1_GT2+HST1_GT2_ING;

  HST1_GT_TempC2:=@HST1_GT2_ING;
  inc(HST1_GT_TempC2);
  HST1_GT2_INGG:=HST1_GT_TempC2^;
  
//  HST1_GT3_ING:=HST1_GT3+(HST1_GT3-HST1_GT3_Save)*(HST1_GT_JiaShu);
  HST1_GT_CC1:=HST1_GT_JiaShu;
  HST1_GT_CC2:=HST1_GT3-HST1_GT3_Save;
  //HST1_GT3_ING:=HST1_GT_CC1*HST1_GT_CC2;
  llmul(HST1_GT_CC1,HST1_GT_CC2,HST1_GT3_ING);
  HST1_GT3_ING:=HST1_GT3+HST1_GT3_ING;

  HST1_GT_TempC3:=@HST1_GT3_ING;
  inc(HST1_GT_TempC3);
  HST1_GT3_INGG:=HST1_GT_TempC3^;


  HST1_GT_Temp1:=g_pSharedMemory;
  HST1_GT_Temp1^:=HST1_GT1_ING;
  Inc(HST1_GT_Temp1);
  HST1_GT_Temp1^:=HST1_GT1_INGG;

  HST1_GT_Temp2:=PTR(cardinal(g_pSharedMemory)+$8);
  HST1_GT_Temp2^:=HST1_GT2_ING;

  HST1_GT_Temp1:=PTR(cardinal(g_pSharedMemory)+$10);
  HST1_GT_Temp1^:=HST1_GT2_INGG;

  HST1_GT_Temp3:=PTR(cardinal(g_pSharedMemory)+$14);
  HST1_GT_Temp3^:=HST1_GT3_ING;

  HST1_GT_Temp1:=PTR(cardinal(g_pSharedMemory)+$1C);
  HST1_GT_Temp1^:=HST1_GT3_INGG;

  g_pSharedMemory_Temp:=PTR(cardinal(g_pSharedMemory)+$20);
  HST1_Memory_Temp:=PTR($FFDF0020);
  HST1_N:=0;
L:
  g_pSharedMemory_Temp^:=HST1_Memory_Temp^;
  inc(g_pSharedMemory_Temp);       //除了更新时间地址,还得把后面空间上的内容一并复制上去.
  inc(HST1_Memory_Temp);
  HST1_N:=HST1_N+1;
  if HST1_N<1016 then
  begin
   goto L;
  end;
 end;
end; 


好了,现在我们拥有了一个和$FFDF0000(一个页面)上面内容一模一样的页面,并且上面的时间记录数据是可以按我们的意思改变的.

然后我们该去改变目标进程了.找到关于内存映射到物理地址的表.

1.首先.我们要知道 分页机制开启 CR0中最高位PG位控制分页管理机制

    asm
     mov eax,cr0
     mov GetMyCR0,eax
    end;


2.如果GetMyCR0最高位为1,那么我们得知道 开启PAE情况

    asm
     mov eax,cr4
     mov GetMyCR4,eax
    end;


3.如果GetMyCR4第5位上为0.那么就为未开启PAE,否则开启PAE

4.对于两种情况的处理,我们这里还先得获得目标进程的CR3
    if PsLookupProcessByProcessId(PID,GetThePeProcess)=STATUS_SUCCESS then
    begin
     ObfDereferenceObject(GetThePeProcess);
     GetThePeProcessP:=GetThePeProcess;
     GETPIDCR3:=GetThePeProcessP^.Pcb.DirectoryTableBase;
    end;


5.未开启PAE情况
 PDEBA:=uCR3;
 dwPDEIndex:=GetCardinalBIT(VirtualAddr,22,10); //取线性地址的高2位作为选取页目录指针表项的索引
 dwToPTE:=PDEBA+dwPDEIndex*4;   //计算指针表项地址
 //读出地址数据
 CLow:=ReadPhyMem(dwToPTE);

 if GetCardinalBIT(CLow,7,1)=1 then  //使用的是4MB的页
 begin
  exit;
 end;

 CI:=GetCardinalBIT(CLow,12,20);
 CI:=CI shl 12;

 PTEBA:=CI;
 dwPTEIndex:=GetCardinalBIT(VirtualAddr,12,10);
 dwToP:=PTEBA+dwPTEIndex*4;   //计算指针表项地址

 result:=dwToP;


6.开启PAE情况
 PDPTEBA:=uCR3;
 dwPDPTEIndex:=GetCardinalBIT(VirtualAddr,30,2); //取线性地址的高2位作为选取页目录指针表项的索引
 dwToPDE:=PDPTEBA+dwPDPTEIndex*8;   //计算指针表项地址
 //读出地址数据
 CLow:=ReadPhyMem(dwToPDE);
 CHigh:=ReadPhyMem(dwToPDE+4);
 CI:=GetCardinalBIT(CLow,12,20)+GetCardinalBIT(CHigh,0,4) shl 20;
 CI:=CI shl 12;

 PDEBA:=CI;
 dwPDEIndex:=GetCardinalBIT(VirtualAddr,21,9);
 dwToPTE:=PDEBA+dwPDEIndex*8;   //计算指针表项地址

 //读出地址数据
 CLow:=ReadPhyMem(dwToPTE);
 CHigh:=ReadPhyMem(dwToPTE+4);

 if GetCardinalBIT(CLow,7,1)=1 then  //使用的是2MB的页
 begin
  exit;
 end;

 CI:=GetCardinalBIT(CLow,12,20)+GetCardinalBIT(CHigh,0,4) shl 20;
 CI:=CI shl 12;

 PTEBA:=CI;
 dwPTEIndex:=GetCardinalBIT(VirtualAddr,12,9);
 dwToP:=PTEBA+dwPTEIndex*8;   //计算指针表项地址

 result:=dwToP;


得到表记录的地址之后,剩下的事情就是将记录内容(原指向的物理地址),改为我们假空间的物理地址即可..

未开启
    CLow:=ReadPhyMem(Addr);
    CI:=GetCardinalBIT(CLow,12,20);
    CI:=CI shl 12;
    S1:=PNAME+';'+inttostr(PID)+';'+inttostr(Addr)+';'+inttostr(CLow)+';'+''+';'+inttostr(CI)+';';
    CLow:=MYPLYMEM+GetCardinalBIT(CLow,0,12);
    WritePhyMem(Addr,CLow);
    ListBox2.Items.Add(S1);


开启
    CLow:=ReadPhyMem(Addr);
    CHigh:=ReadPhyMem(Addr+4);
    CI:=GetCardinalBIT(CLow,12,20)+(GetCardinalBIT(CHigh,0,4) shl 20);
    CI:=CI shl 12;
    S1:=PNAME+';'+inttostr(PID)+';'+inttostr(Addr)+';'+inttostr(CLow)+';'+inttostr(CHigh)+';'+inttostr(CI)+';';
    CI:=MYPLYMEM;
    CLow:=GetCardinalBIT(CLow,0,12)+(GetCardinalBIT(CI,12,20) shl 12);
    CHigh:=(GetCardinalBIT(CHigh,4,28) shl 4)+GetCardinalBIT(CI,32,5);


最后,因为这个工具是很久之前写的了.写得比较乱,所以现在我自己也不能完全理解了!!只能从其中摘要一点发出来.全部详细请自行分析源代码.

最后上工程源代码:
编译环境:程序:Delphi 6 驱动:Delphi6 + KmdKit4D

[课程]FART 脱壳王!加量不加价!FART作者讲授!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (24)
雪    币: 278
活跃值: (709)
能力值: ( LV15,RANK:520 )
在线值:
发帖
回帖
粉丝
2
顶下,不会Delphi
2011-11-4 03:50
0
雪    币: 768
活跃值: (515)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
3
mark Delphi 写驱
2011-11-4 08:09
0
雪    币: 79
活跃值: (40)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
亲...爱你哦 学习下 谢谢 想学习下 Delphi
2011-11-4 15:44
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这文章应该申精的。。。
2011-11-4 17:56
0
雪    币: 527
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
mark
2011-11-4 19:22
0
雪    币: 485
活跃值: (78)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
7
mark,thx for share it ~~~good article
2011-11-4 21:51
0
雪    币: 402
活跃值: (342)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
8
不错.不习惯d..还是下下来看bin
2011-11-7 11:38
0
雪    币: 538
活跃值: (259)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
好吧 其实我一直想学这个东西.顶楼主好文~~~
2011-11-7 20:59
0
雪    币: 2177
活跃值: (2045)
能力值: (RANK:400 )
在线值:
发帖
回帖
粉丝
10
要是写成C的就更好了。
2011-11-7 21:29
0
雪    币: 71
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
不熟悉delphi

希望改成c的
2011-11-7 22:05
0
雪    币: 494
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
赞同,
楼主思路很好。
2011-11-8 09:22
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
13
貌似从前有个叫“月光宝盒”的东西就是这样干的。
2011-11-8 17:14
0
雪    币: 615
活跃值: (1127)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
500年前!!!!!
2011-11-8 19:40
0
雪    币: 219
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
好东西,看看
2011-11-8 22:03
0
雪    币: 27
活跃值: (90)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
16
看一看 :)
2011-11-9 13:21
0
雪    币: 237
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
markkkkkkkkk
2011-11-11 03:53
0
雪    币: 204
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
呵呵 这东西有系统限制吧
估计就WINXP 能跑 或则32位系统
2011-11-11 11:53
0
雪    币: 965
活跃值: (1184)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
不错,支持下
2011-11-11 20:47
0
雪    币: 47
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
Delphi 牛人
2011-11-15 08:36
0
雪    币: 204
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
TP真的还是好强大,检测了内核态达挂钩了的。
没想到这样的方式也不行 呵呵
2011-11-15 13:48
0
雪    币: 45
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
同志,少文件
Wu_BasicUnit, Wu_ProcessFindUnit
2012-2-3 11:27
0
雪    币: 45
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
procedure Wu_GetLinkOfS(sIn : string; Chr : Char; nIndex : Integer; var sOut : string);
var
  I,II : Integer;
  pIn : PChar;
begin
  sOut := sIn;
  II := 1;
  for I := 1 to Length(sIn) do begin
    if sIn[I] = Chr then begin
      Inc(II);
      if (II = nIndex) then begin
        sOut := Copy(sIn, I + 1, Length(sIn));
        Break;
      end;
    end;
  end;
  II := Pos(Chr, sOut);
  if II > 0 then
    Delete(sOut, II, Length(sOut));
end;
2012-2-3 14:52
0
雪    币: 464
活跃值: (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
好东西,看看
2012-2-4 18:00
0
雪    币: 45
活跃值: (51)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
25
内核中的地址 0xffdf0000 对应的物理内存, 映射到每个进程中的地址是 0x7ffe0000;

实际上这个地址对应的是一个 _KUSER_SHARED_DATA结构:
[CODE]
nt!_KUSER_SHARED_DATA
   +0x000 TickCountLow     : Uint4B
   +0x004 TickCountMultiplier : Uint4B
   +0x008 InterruptTime    : _KSYSTEM_TIME
    ... ...
[\CODE]

+0和+8处的两个结构 对实现变速齿轮有用.
因为三环下: timeGetTime, GetTickCount, QuerPerformanceCount,三个获取时间的函数,
实际上主要的功能是查询+0或者+8地方的值....(PS: 打开OD自己看着三个函数的实现...)
(顺道多嘴, Hook着三个函数就能实现一个三环下的变速齿轮当然,扫雷不能加速, 因为它是用的定时器(timmer)计时~~~)

正题: 内核下,在虚拟机中调试, 看到的是由KeUpdateSystemTime负责更新!_KUSER_SHARED_DATA
中的时间.(具体自己打开你的WinDbg + 看看win源码).....

楼主说的方法类似于Hook KeUpdateSystemTime函数, 让其更新时间的时候 被我们乘机修改??

我试了Hook这个函数更新系统时间的, 加速2倍, 结构受到的效果不是系统时间加速两倍, 而是系统时钟走得断断续续, 加速别的程序什么的也不成....

这个直接的, 仅仅的. 修改0xffdf0000的 方法, 应该是不能实现变速齿轮的....
楼主的变速齿轮, 下过来运行 直接蓝屏~ 应该是硬编码的挂钩地址吧?~

看了一下"守望者变速齿轮"的实现..  
无奈能力有限, 只能看到其Hook了 KeUpdateSystemTime,这个可以完整逆向偷学来....
但是其修改了hal.dll等的系统dll..... 不好搞, 不知道怎么搞...

寻求实现变速齿轮 内核版 的方法ing.....
2012-7-6 20:38
0
游客
登录 | 注册 方可回帖
返回
//