首页
社区
课程
招聘
[原创]某游戏皮肤修改工具的逆向与实现
发表于: 2014-10-7 21:10 28423

[原创]某游戏皮肤修改工具的逆向与实现

2014-10-7 21:10
28423

        某游戏也就是国内目前很火的竞技游戏英雄联盟了,前不久在网上发现到了一个本地修改皮肤的辅助工具,话说作者以前一直以为这种辅助工具是通过修改本地皮肤文件来实现的,想来没什么技术含量,需要的时候找到那个文件修改就行了。这次简单的调试了一下,发现该工具是通过修改游戏内存实现的,于是觉得有意思,就有了本文。(作者技术以及表达能力都很差,请大家见谅,有问题可以短信我或者邮件我)
        工具没加壳,比较简单,本文主要找到其思路,然后自己写代码实现(为了保护工具以及游戏,本文仅提供辅助工具部分反汇编代码和作者自己按照其思路写的文件以及部分源码,不提供该工具名称、文件以及作者自己写的完整源代码,你可以根据此文逆向作者的dll,然后自己实现
        首先分析辅助的文件的作用,辅助修改游戏内存,大部分就两种,ReadProcessMemory等API和注入dll,TX的游戏有TP保护的,所以ReadProcessMemory这种方法肯定行不通。推测辅助是注入dll到游戏的,so在打开工具的情况下打开游戏,使用XT查看游戏相关进程模块,发现游戏进程“League of Legends.exe”(以后称其为游戏进程)注入了Skin.dll。而游戏启动进程” lol.launcher_tencent.exe”(以后称其为启动进程)注入了Mon.dll,同时辅助工具也加载了Mon.dll,修改皮肤时会创建一个Skin.cfg配置文件,里面记录了修改的信息,应该是某个dll会根据里面的信息来修改的。所以我们重点调试Mon.dll和Skin.dll。(逆向过程不是很好表述,所以作者是通过截图+文字来表述的,有些流程是一些简单的字符串处理流程和条件判断流程,为了节省篇幅,都会略过)。
一:Mon.dll的调试
        定位到dll入口处,













部分源码
CreateProcessAHook代码

BOOL  WINAPI CreateProcessAHook(  __in        LPCTSTR lpApplicationName,
                LPTSTR lpCommandLine,
                __in          LPSECURITY_ATTRIBUTES lpProcessAttributes,
                __in          LPSECURITY_ATTRIBUTES lpThreadAttributes,
                __in          BOOL bInheritHandles,
                __in          DWORD dwCreationFlags,
                __in          LPVOID lpEnvironment,
                __in          LPCTSTR lpCurrentDirectory,
                __in          LPSTARTUPINFO lpStartupInfo,
                __out         LPPROCESS_INFORMATION lpProcessInformation)  
{  
                 
      //修改输入参数,调用原函数  
    int ret=OLD_CreateProcessA(lpApplicationName,
                  lpCommandLine,
                  lpProcessAttributes,
                  lpThreadAttributes,
                  bInheritHandles,
                  dwCreationFlags | CREATE_SUSPENDED,
                  lpEnvironment,
                  lpCurrentDirectory,
                  lpStartupInfo,
                  lpProcessInformation);  
    GetModuleFileName(g_hModule,g_szPath,MAX_PATH);
    memset(g_bWriteCode,0,1000);

    CONTEXT  ThreadContext;

    ThreadContext.ContextFlags = CONTEXT_FULL;

    GetThreadContext(lpProcessInformation->hThread,&ThreadContext);
    LPVOID lpAddress = VirtualAllocEx(lpProcessInformation->hProcess,NULL,1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    if (!lpAddress)
    {
      DebugPrintf("VirtualAllocEx Error %d",GetLastError());
      return ret;
    }
    BYTE szBuffer[1000];
    BYTE *bP;
    RtlZeroMemory(szBuffer,1000);


    szBuffer[0] = 0x9C;
    szBuffer[1] = 0x60;
    szBuffer[2] = 0xE8;
    *(DWORD*)(szBuffer+3) = 0x104;
    RtlCopyMemory(szBuffer + 7,g_szPath,MAX_PATH);

    bP = &szBuffer[0x104+7];
    *bP = 0xB8;
    *(DWORD*)(bP+1) = (DWORD)LoadLibraryA; 
    *(bP+5) = 0xFF;
    *(bP+6) = 0xD0;
    *(bP+7) = 0x61;
    *(bP+8) = 0x9d;
    *(bP+9) = 0x68;
    *(DWORD*)(bP+10) = ThreadContext.Eip;
    *(bP+14) = 0xc3;

    DebugPrintf("lpAddress = %x",lpAddress);
    if(!WriteProcessMemory(lpProcessInformation->hProcess, lpAddress, szBuffer, 1000, NULL))
    {
      DebugPrintf("WriteProcessMemory Error,%d",GetLastError());
      return ret;
    }
    FlushInstructionCache(lpProcessInformation->hProcess, lpAddress, 1000);
    ThreadContext.Eip = (DWORD)lpAddress;
    SetThreadContext(lpProcessInformation->hThread, &ThreadContext);
    ResumeThread(lpProcessInformation->hThread);

    return ret;  
}  

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

上传的附件:
收藏
免费 4
支持
分享
最新回复 (60)
雪    币: 114
活跃值: (180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
这个略叼,前排支持楼主~
2014-10-7 21:17
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
3
值得一赞~
2014-10-7 21:27
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark
2014-10-7 21:32
0
雪    币: 116
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
谢谢大牛的赞。
2014-10-7 21:36
0
雪    币: 4687
活跃值: (253)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
这个真心不错,有学习的地方。。。赞  666666
2014-10-7 22:42
0
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
代码好乱 看的我好难受
2014-10-7 23:04
0
雪    币: 10
活跃值: (231)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
值得一赞
2014-10-7 23:37
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
不错。。
2014-10-7 23:58
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
这个不错
2014-10-8 01:34
0
雪    币: 1
活跃值: (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
好N啊。。。。
2014-10-8 03:38
0
雪    币: 66
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
08年我就在用这个注射方法了。。。你居然解决新奇?
2014-10-8 05:55
0
雪    币: 116
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
把思路整理下,哪里不懂就去看哪里的代码就好了。
2014-10-8 07:26
0
雪    币: 116
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
小弟才疏学浅,对我来说新奇。这就是书看少了的下场
2014-10-8 07:27
0
雪    币: 66
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
部分游戏有专门对付这种注射方式的, 主程序启动2次。例如DNF
2014-10-8 21:22
0
雪    币: 238
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
一直想找这方面的教程,楼主帮到了我,感谢
2014-10-8 22:03
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
牛逼啊。。。。。小白不懂这些
2014-10-8 22:12
0
雪    币: 341
活跃值: (143)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
18
牛叉,,,,
2014-10-8 22:13
0
雪    币: 116
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
19
一个小小的猜测:注入后再HOOK CreateProcess是不是不管启动多少次,总能保证自己的dll注入了game?
2014-10-8 23:07
0
雪    币: 1847
活跃值: (1816)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
20
TP是个渣?什么都不拦?
2014-10-9 10:57
0
雪    币: 144
活跃值: (53)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
标记,以后思考
2014-10-9 13:14
0
雪    币: 144
活跃值: (335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
SuspendThread(hThread);
  memset(&Context, 0, 716u);
  Context.ContextFlags = 65543;
  if ( GetThreadContext(hThread, &Context) )
  {
    lpBaseAddress = (Context.Esp - 0x480) & 0xFFFFFFE0;
    v15 = &Buffer;
    if ( str )
    {
      memcpy(&Dst, str, Size);
      v5 = sub_414790(&Buffer, lpBaseAddress + 128);// push addr
      v15 = (char *)sub_414800(v5, loadlibraryaddr, v5 - (_DWORD)&Buffer + lpBaseAddress);// call addr
    }
    v6 = sub_414610(v15, Context.Eax);          // mov eax
    v7 = sub_414640(v6, Context.Ebx);           // mov ebx
    v8 = sub_414670(v7, Context.Ecx);
    v9 = sub_4146A0(v8, Context.Edx);
    v10 = sub_4146D0(v9, Context.Esi);
    v11 = sub_414700(v10, Context.Edi);
    v12 = sub_414730(v11, Context.Ebp);
    v13 = sub_414760(v12, Context.Esp);
    sub_4147C0(v13, Context.Eip, v13 - (_DWORD)&Buffer + lpBaseAddress);// jmp
    Context.Esp = lpBaseAddress - 4;
    Context.Eip = lpBaseAddress;
    if ( VirtualProtectEx(pi, (LPVOID)lpBaseAddress, 1152u, 0x40u, &flOldProtect)
      && WriteProcessMemory(pi, (LPVOID)lpBaseAddress, &Buffer, 0x480u, &NumberOfBytesWritten)
      && FlushInstructionCache(pi, (LPCVOID)lpBaseAddress, 0x480u)
      && SetThreadContext(hThread, &Context) )
昨天碰到个程序,在想这是干啥啊。。
看了楼主的分析明白了。。。
2014-10-9 14:02
0
雪    币: 55
活跃值: (519)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
23
刚好我自己在做。做了一半。看了以后受益匪浅啊。
2014-10-9 15:13
0
雪    币: 55
活跃值: (519)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
24
这种注入方式很早就有了= =我记得我以前个看雪号写这种注入方式教程还加精了。
2014-10-9 15:14
0
雪    币: 116
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
我也纳闷。但是原作者确实是这么做的。估计是TP对这游戏的保护强度不高。
2014-10-9 16:35
0
游客
登录 | 注册 方可回帖
返回
//