镜之边缘第四关破解补丁TlsGetValue 导致的Bug解决方法

来源:互联网 发布:php 字符串比较 编辑:程序博客网 时间:2024/06/05 16:01


       年前把镜之边缘下回来玩,可惜玩到第四关的时候会冻结住,人物动不了。按照网上的说法需要打上第四关补丁,不过所谓的第四关补丁同学的电脑可以,我的机器打上了就连程序也打不开了……这几天又非常想玩镜之边缘,终于痛下决心,自己动手,丰衣足食。 
       分别载入可以玩的和不可以玩的版本。可以发现多了这么几条指令:

 

03D60576    FF15 1C28B202   CALL DWORD PTR DS:[<&KERNEL32.GetProcess>; kernel32.GetProcessHeap03D6057C    A3 3C899902     MOV DWORD PTR DS:[299893C],EAX03D60581    A1 60DAA801     MOV EAX,DWORD PTR DS:[<&USER32.SendMessa>03D60586    A3 B105D603     MOV DWORD PTR DS:[3D605B1],EAX03D6058B    C705 60DAA801 9>MOV DWORD PTR DS:[<&USER32.SendMessageW>>03D60595  - E9 D12C5BFD     JMP MirrorsE.0131326B03D6059A    B8 2A010600     MOV EAX,6012A03D6059F    3E:394424 04    CMP DWORD PTR DS:[ESP+4],EAX03D605A4    75 05           JNZ SHORT MirrorsE.03D605AB03D605A6  - E9 A5465FFE     JMP MirrorsE.02354C5003D605AB    FF25 B105D603   JMP DWORD PTR DS:[3D605B1]


 

      可以看到第四关补丁在程序刚开始的地方把SendMessageW的地址给改了。同时写了4个字节的数据。先不管,跑一下发现出错了:

 

024A3238    8951 14         MOV DWORD PTR DS:[ECX+14],EDX


 

      此时的ECX = 8BEC8B55 不出错才怪呢……

 

      往上看到底是什么地方给ECX赋的值找到这里:

 

024A31E2    E8 9071FFFF     CALL MirrorsE.0249A377024A31E7    85C0            TEST EAX,EAX024A31E9    59              POP ECX024A31EA    8947 44         MOV DWORD PTR DS:[EDI+44],EAX024A31ED    B9 B0289202     MOV ECX,MirrorsE.029228B0024A31F2    74 03           JE SHORT MirrorsE.024A31F7024A31F4    8B4F 44         MOV ECX,DWORD PTR DS:[EDI+44]


 

      可见ECX的值是跟0249A377这个地址的函数有关,跟踪下发现执行的是MOV ECX,DWORD PTR DS:[EDI+44],但内部没有关于EDI的操作,那么在向前看看。

 

024A31C8    E8 AAC4FFFF     CALL MirrorsE.0249F677024A31CD    85F6            TEST ESI,ESI024A31CF    8BF8            MOV EDI,EAX



      调用了0249F677,跟进:


0249F677    53              PUSH EBX0249F678    56              PUSH ESI0249F679    FF15 9425B202   CALL DWORD PTR DS:[<&KERNEL32.GetLastErr>; ntdll.RtlGetLastWin32Error0249F67F    FF35 5C93A202   PUSH DWORD PTR DS:[2A2935C]0249F685    8BD8            MOV EBX,EAX0249F687    FF15 A427B202   CALL DWORD PTR DS:[<&KERNEL32.TlsGetValu>; kernel32.TlsGetValue0249F68D    8BF0            MOV ESI,EAX0249F68F    85F6            TEST ESI,ESI0249F691    75 49           JNZ SHORT MirrorsE.0249F6DC0249F693    68 8C000000     PUSH 8C0249F698    6A 01           PUSH 10249F69A    E8 41E5FFFF     CALL MirrorsE.0249DBE00249F69F    8BF0            MOV ESI,EAX0249F6A1    85F6            TEST ESI,ESI0249F6A3    59              POP ECX0249F6A4    59              POP ECX0249F6A5    74 2D           JE SHORT MirrorsE.0249F6D40249F6A7    56              PUSH ESI0249F6A8    FF35 5C93A202   PUSH DWORD PTR DS:[2A2935C]0249F6AE    FF15 5827B202   CALL DWORD PTR DS:[<&KERNEL32.TlsSetValu>; kernel32.TlsSetValue0249F6B4    85C0            TEST EAX,EAX0249F6B6    74 1C           JE SHORT MirrorsE.0249F6D40249F6B8    C746 54 A895A20>MOV DWORD PTR DS:[ESI+54],MirrorsE.02A29>0249F6BF    C746 14 0100000>MOV DWORD PTR DS:[ESI+14],10249F6C6    FF15 A025B202   CALL DWORD PTR DS:[<&KERNEL32.GetCurrent>; kernel32.GetCurrentThreadId0249F6CC    834E 04 FF      OR DWORD PTR DS:[ESI+4],FFFFFFFF0249F6D0    8906            MOV DWORD PTR DS:[ESI],EAX0249F6D2    EB 08           JMP SHORT MirrorsE.0249F6DC0249F6D4    6A 10           PUSH 100249F6D6    E8 4AC0FFFF     CALL MirrorsE.0249B7250249F6DB    59              POP ECX0249F6DC    53              PUSH EBX0249F6DD    FF15 2C28B202   CALL DWORD PTR DS:[<&KERNEL32.SetLastErr>; ntdll.RtlSetLastWin32Error0249F6E3    8BC6            MOV EAX,ESI0249F6E5    5E              POP ESI0249F6E6    5B              POP EBX0249F6E7    C3              RETN


 

      这里就是关键了,可以看到ESI和EAX唯一被修改的地方是CALL DWORD PTR DS:[<&KERNEL32.TlsGetValu>,执行到这一步,EAX变成了7C82209E。也就是FlsGetValue的地址。接下来程序的走法就是就直接跳走,读取非法内存然后挂掉。

      找到问题去同学家跑一下,发现正常的程序执行到这一步TlsGetValue的返回值是0,然后JNZ不跳转。这样的话我们稍微改下,把MOV ESI,EAX改成XOR ESI,ESI就可以达到效果。试试瞧,果然界面弹出来了,哈哈哈……

      可惜执行到一半还是出错了……看看反汇编发现是PhysxCore.dll跑飞了……看下堆栈

      发现返回到07723E01。重启程序跟进看看。发现是CALL EAX,而CALL上面那句还是个CALL,看样子是直接跳到了返回值里面,跟进发现EAX竟然是TlsGetValue的返回值……又是这个阴魂不散的家伙……

      仔细分析刚才我们做的操作,想起来了吗?我们的TlsGetValue的值本来是FlsGetValue,但由于CALL DWORD PTR DS:[<&KERNEL32.TlsSetValu>; kernel32.TlsSetValue而被修改了,所以这个补丁可能没那么简单。

      虽然我们不能直接执行TlsSetValue把值给改了,但我们的目的仅仅是让EDI的值正常,来保障接下来的ECX正常。看看我们分析清楚问题那么就动手吧。看看我们刚才分析到的正确EDI的值:00087E38貌似是会变的,但是之前的CALL 0249DBE0似乎被EA做了手脚,只有在这里才能成功调用,所以就不管会变不会变了,直接赋值给EDI。

      找一块空地址,我找的是0281CD20,先跳过来,由于不知道其他地方会不会调用这个函数所以仅修改EDI为FlsGetValue的情况。

 

0281CD20    FF15 A427B202   CALL DWORD PTR DS:[<&KERNEL32.TlsGetValu>; kernel32.TlsGetValue0281CD26    3D 9E20827C     CMP EAX,kernel32.FlsGetValue0281CD2B    75 05           JNZ SHORT MirrorsE.0281CD320281CD2D    B8 387E0800     MOV EAX,87E380281CD32  ^ E9 5629C8FF     JMP MirrorsE.0249F68D


 

然后修改原来的CALL DWORD PTR DS:[<&KERNEL32.TlsGetValu>改成JMP 0281CD20

保存下,跑跑试试!大功告成!