Hacking Diablo II之外挂实战教程:去除D2JSP试用版显示的Trial Version信息

前几天一个网友给我发消息请我帮他个忙。他的问题是,他正在使用的D2JSP是免费试用版,试用版在运行时会在游戏的所有游戏画面中央显示一行很大的“Trial Version”字样(见下图中的红圈),很烦人,他想去掉这行字。我想正好用此做个教程解释前面介绍过的hack工作原理,于是答应帮他看看。


我已经很久没有开BOT了,最后一次使用D2JSP还是2003年1.09d时期的事。根据我以前的理解,D2JSP和其他hacks一样,由两部分组成,d2jsp.exe只是一个loader,负责把d2jsp.dll加载到游戏进程。而d2jsp.dll才是D2JSP的运行引擎-真正发挥作用的东西。因此“Trial Version”字符串肯定在d2jsp.dll中。最开始的想法是“Trial Version”可能会以一个常量字符串的形式保存在d2jsp.dll中,因此最容易想到的做法是用UltraEdit打开d2jsp.dll,寻找“Trial Version”,找到的话把它改为空字符串就OK了。然而打开后发现d2jsp.dll是加了壳的,这才想起自从D2 1.10以来D2JSP开始收费,采取了一些措施防止别人破解,加壳就是其中之一(当然还是被人破了)。在d2jsp.dll文件的前面几行看到了aspack字串,看来加的是aspack壳。脱壳我很不在行,但是我知道有一些自动脱壳工具,针对aspack的脱壳工具也肯定有,不妨试试,于是到网上抓了个脱壳软件-AspackDie。当然试验的结果是失败了。又想,不管怎样,d2jsp.dll运行的时候它自己得脱壳吧,那我可以在它自己脱壳后在进程内存中找吧。于是加载D2JSP后用WinHex打开游戏进程,查找Trial Version,然而还是没找到。看来可能用了即时脱壳技术,或者干脆Trial Version根本不是常量字符串,比如我知道如果在C/C++中这样写szVersion就不是常量字符串:

 

char  szVersion[]  =  { ' T ' ' r ' ' i ' ' a ' ' l ' '   ' ' V ' ' e ' ' r ' ' s ' ' i ' ' o ' ' n ' ' ' };

然后怎么办,继续研究脱壳?不,其实我早就有了更好、更通用的解决方法,但是需要编程。现在看来简单的思路行不通,还是写程序算了。换个角度来看,不管字符串是什么,总得调用某个函数来输出显示吧,看上图中的字样,显然是调用了Diablo II的内部函数显示的,因此如果能截获这个函数过滤掉"Trial Version"字符串就搞定了。而且这种方法不用修改D2JSP程序,对所有D2JSP版本都起作用,因此更通用。问题是它调用了D2X的哪个函数呢?我想起我的d2hackmap中也用到了类似的字符串显示的功能,它们用的有可能是同一个!于是把d2hackmap的源代码翻出来看了看,发现了d2win.dll中导出序号为10064(Diablo II中所有dll导出的函数都只有序号没有名字)的函数做此用途,其函数声明为:

 

void  __fastcall D2WIN_DrawText(wchar_t  * str,  int  xpos,  int  ypos, DWORD color, DWORD unknown);

 然后用VC++ attach到游戏进程进行调试,想在D2WIN_DrawText设断点监控,结果发现调试器刚attach上去D2JSP就会崩溃。看来它做了反调试处理,这也在意料之中。还是直接写个DLL注入到游戏进程截获D2WIN_DrawText来观测得了,字符串可以用Win32 API OutputDebugString输出然后用DbgView观测:

 

void  __fastcall MyD2WIN_DrawText(wchar_t  * str,  int  xpos,  int  ypos, DWORD color, DWORD unknown)

{
    
if (str) OutputDebugStringW(str);
}

结果发现“Trial Version”果然是通过D2WIN_DrawText输出的,见下图。


现在只要改变MyD2WIN_DrawText()的实现,判断输出字符串如果为"Trial Version"则跳过真正的D2WIN_DrawText()代码就搞定了,代码见后面的详细源代码分析。改变后的显示结果如下图,画面上方的d2jsp v1.2.0字样还在,Trial Version字样已经没了:


至此,给D2JSP打补丁的程序d2jsppatch.dll已经做好了,然而还有一个问题,就是d2jsppatch.dll如何加载呢?用D2JSP开的BOT一般是无人职守自动运行的,因此d2jsppatch.dll最好能够随着d2jsp.dll自动加载,否则每次都需要人工加载那就太麻烦了。那怎么做到随d2jsp.dll自动加载呢?一个办法是把d2jsp.dll改名为d2jsp2.dll,d2jsppatch.dll改名为d2jsp.dll,这样d2jsp.exe每次加载d2jsp.dll时其实加载的是d2jsppatch.dll。d2jsppatch.dll加载起来后,在它的启动代码里再手工加载d2jsp2.dll(即原来的d2jsp.dll),这样就达到了自动加载d2jsppatch.dll的目的。这种方法的缺点是它改变了D2JSP原有模块之间的关系,可能会导致一些兼容性问题,比如d2jsp.exe可能会校验d2jsp.dll检查它是否被人改过,或者某个依赖于d2jsp.dll工作的程序可能会用LoadLibrary("d2jsp.dll")/GetProcAddress()获取d2jsp.dll的到处函数,这时就会失败。

伴随d2jsp.dll自动加载的另外一种做法是把d2jsppatch.dll静态绑定到d2jsp.dll或者d2jsp.dll会用到的某个DLL。由于d2jsp.dll加了壳,DLL文件的IAT(Import Address Table,即导入表)已经被破坏,最好是从d2jsp.dll会用到的DLL下手。我们知道D2JSP支持jscript脚本程序,它肯定会加载jscript脚本引擎js32.dll,所以我们可以把d2jsppatch.dll静态绑定到js32.dll上去。熟悉Windows编程且喜欢玩API hooking的朋友可能知道微软的Detours Library,其中附带了一个程序setdll.exe可以把一个DLL静态绑定到其他EXE或者DLL。在命令行运行:

 

setdll . exe -d :d2jsppatch . dll js32 . dll

就把d2jsppatch.dll绑定到了js32.dll,如下图。

以下是对d2jsppatch.dll源代码的详细分析,给感兴趣的朋友参考。d2jsppatch.dll虽然是针对D2JSP的,但是它包含了一个hack的所有基本组成部分,而且功能很简单,用它来了解hack的工作原理是很合适的。d2maphack、d2hackmap等hacks的工作原理和它完全一样,不同的只是实现的功能。

1,d2jsppatch.dll的加载和卸载。DLL入口函数DllMain中一般用来做安装(InstallPatch)、卸载(RemovePatch)旁路点(detour patch)。安装、卸载工作可能导致程序崩溃(见以前的文章),放在DllMain中进行最合适,因为Windows保证在DllMain中的代码执行时进程内的其他线程不会运行,见:http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx

 

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
    
if (DLL_PROCESS_ATTACH == dwReason)
    
{
        DisableThreadLibraryCalls((HMODULE)hModule);
        
return InstallPatch();
    }

    
else if(DLL_PROCESS_DETACH == dwReason)
    
{
        RemovePatch();
    }

    
return TRUE;
}

 2,旁路点的安装和卸载。安装旁路点一般就是往旁路点处插入一个跳转(call或jmp,5字节)指令,让程序运行到此处时跳转到自己的patch代码。要注意程序代码段的页面属性一般都是只读的,安装前先要把页面属性改成可读写,装完后再恢复原来的页面属性。卸载旁路点就是把patch点原先的代码写回去,因此在安装时要做一下备份。

 

void  WriteLocalBYTES( void *  pAddress,  void   * buf,  int  len)
{
    DWORD oldprot 
= 0, dummy;
    VirtualProtect(pAddress, len, PAGE_EXECUTE_READWRITE, 
&oldprot);
    WriteProcessMemory(GetCurrentProcess(), pAddress, buf, len, 
&dummy);
    VirtualProtect(pAddress, len, oldprot, 
&dummy);
}


void  PatchCALL(DWORD pOldCode, DWORD pNewCode, DWORD len)
{
    BYTE buf1[
5];
    buf1[
0= 0xe8// inst call
    *(DWORD *)(buf1+1= pNewCode-(pOldCode+5);
    WriteLocalBYTES((
void*)pOldCode, buf1, len);
}


FARPROC g_pDrawText;
BYTE g_oldcode[
5 ];
BOOL InstallPatch()
{
    HMODULE hD2Win 
= GetModuleHandle("d2win.dll");
    
if (!hD2Win) hD2Win = LoadLibrary("d2win.dll");
    
if (hD2Win)
    
{
        g_pDrawText 
= GetProcAddress(hD2Win, (LPCSTR)10064);
        
if (g_pDrawText)
        
{
            memcpy(g_oldcode, g_pDrawText, 
5);
            PatchCALL((DWORD)g_pDrawText, (DWORD)D2WinDrawTextPatch_ASM, 
5);
            
return TRUE;
        }

    }

    
return FALSE;
}


void  RemovePatch()
{
    
if (g_pDrawText)
        WriteLocalBYTES(g_pDrawText, g_oldcode, 
5);
}

3,旁路点的patch代码。patch代码根据所要实现的功能的不同而不同,需要具体情况具体分析。一般来说逻辑稍微复杂些的patch代码会由两步分组成,一是用汇编写的入口代码,对寄存器和栈指针进行精细控制以免破坏原先的数据;二是用C/C++写的逻辑实现代码。汇编入口代码调用C/C++的逻辑实现代码。在此例中,D2WinDrawTextPatch_ASM为纯(嵌入)汇编函数,第一行代码运行前的寄存器内容和栈布局如注释所示,D2WinDrawTextPatch有5个参数,由于是__fastcall,因此前两个参数在ecx和edx中传递(VC的规定,C++ Builder可能有所不同),剩下三个从栈中传入。由于C函数D2WinDrawTextPatch可能破坏ecx和edx里的内容,因此调用前必须保存它们的值,然后调用D2WinDrawTextPatch做真正的字符串过滤,注意D2WinDrawTextPatch也声明为__fastcall,因此参数str为ecx。根据str是否为"Trial Version",D2WinDrawTextPatch返回TRUE或FALSE(返回值存放在eax中)。D2WinDrawTextPatch_ASM在D2WinDrawTextPatch返回后先恢复ecx和edx的原先内容,接着根据eax的值判断是否应该执行真正的D2WinDrawText函数来显示字符串。标号draw_text后面的代码流程将跳到真正的D2WinDrawText去执行,由于旁路点把它的前两个指令(共5个字节)改掉了(见下图,上半部分为patch前的代码,下半部分为patch后的代码),在跳转之前必须先执行这两条指令。
BOOL __fastcall D2WinDrawTextPatch(wchar_t  * str)
{
    
return str && (*str == L'T' && wcscmp(str, L"Trial Version"== 0);
}


/*
* [esp+0x10] = arg 5 - unknown
* [esp+0x0c] = arg 4 - col
* [esp+0x08] = arg 3 - ypos
* [esp+0x04] = return address of caller of D2WIN_DrawText, e.g., from D2JSP
* [esp+0x00] = return address of D2WIN_DrawText
* edx = arg 2, xpos
* ecx = arg 1, text string
*/

void  __declspec(naked) D2WinDrawTextPatch_ASM()
{
    __asm
    
{
        push edx; 
// arg 2 - xpos
        push ecx; // arg 1 - str
        call D2WinDrawTextPatch;
        pop ecx;
        pop edx;
        test eax, eax;
        jz draw_text;
        pop eax; 
// discard return address of caller of D2WIN_DrawText
        ret 0x0c// discard three arguments
draw_text:
        pop eax; 
// return address of D2WIN_DrawText

        
// original code, overwritten by detour patch
        push ebx;
        mov ebx, dword ptr [esp
+0x10];

        jmp eax;
    }

}


4,给静态绑定用的空导出函数。在一般的hack中这一部分是不需要的。setdll.exe在做静态绑定时需要把d2jsppatch.dll的一个导出函数(随便一个,有一个就行)插入js32.dll的IAT中。

 

extern   " C "  __declspec( dllexport ) VOID FakedInterface()
{
}

 5,做为一个行为良好、成熟的hack,在安装旁路点前还应该检测自己用到的旁路点有没有已经被其他hack改掉了,否则就会导致其他hack不能正常工作甚至整个游戏崩溃。如果别人已经改掉了,自己要么额外处理要么只好主动退出。那么如果要patch很多点(像maphack、hackmap有上百个),怎么检查这些点是否被改了呢?一个快速而且有效的做法是对这些点的内容预先计算一个校验和,运行时每次安装旁路点前再校验一遍然后和预先计算的值比较。d2jsppatch.dll的功能很简单,没有做这部分处理,具体做法请参考d2hackmap 2.24的源代码,也可以到这里下载:http://newd2event.net/index.php?id=hacks/Sting_Hackmap

附:完整的d2jsppatch.dll源代码,只有短短的100行不到。

#define  WIN32_LEAN_AND_MEAN
#include 
< windows.h >

//  helper stuff
void  WriteLocalBYTES( void *  pAddress,  void   * buf,  int  len)
{
    DWORD oldprot 
= 0, dummy;
    VirtualProtect(pAddress, len, PAGE_EXECUTE_READWRITE, 
&oldprot);
    WriteProcessMemory(GetCurrentProcess(), pAddress, buf, len, 
&dummy);
    VirtualProtect(pAddress, len, oldprot, 
&dummy);
}


void  PatchCALL(DWORD pOldCode, DWORD pNewCode, DWORD len)
{
    BYTE buf1[
5];
    buf1[
0= 0xe8// inst call
    *(DWORD *)(buf1+1= pNewCode-(pOldCode+5);
    WriteLocalBYTES((
void*)pOldCode, buf1, len);
}


//  patch stuff
// BOOL __fastcall D2DrawTextPatch(wchar_t *str, int xpos, int ypos, DWORD col, DWORD unknown)
BOOL __fastcall D2WinDrawTextPatch(wchar_t  * str)
{
    
return str && (*str == L'T' && wcscmp(str, L"Trial Version"== 0);
}


/*
* [esp+0x10] = arg 5 - unknown
* [esp+0x0c] = arg 4 - col
* [esp+0x08] = arg 3 - ypos
* [esp+0x04] = return address of caller of D2WIN_DrawText, e.g., from D2JSP
* [esp+0x00] = return address of D2WIN_DrawText
* edx = arg 2, xpos
* ecx = arg 1, text string
*/

void  __declspec(naked) D2WinDrawTextPatch_ASM()
{
    __asm
    
{
        push edx; 
// arg 2 - xpos
        push ecx; // arg 1 - str
        call D2WinDrawTextPatch;
        pop ecx;
        pop edx;
        test eax, eax;
        jz draw_text;
        pop eax; 
// discard return address of caller of D2WIN_DrawText
        ret 0x0c// discard three arguments
draw_text:
        pop eax; 
// return address of D2WIN_DrawText

        
// original code, overwritten by detour patch
        push ebx;
        mov ebx, [esp
+0x10];

        jmp eax;
    }

}


FARPROC g_pDrawText;
BYTE g_oldcode[
5 ];
BOOL InstallPatch()
{
    HMODULE hD2Win 
= GetModuleHandle("d2win.dll");
    
if (!hD2Win) hD2Win = LoadLibrary("d2win.dll");
    
if (hD2Win)
    
{
        g_pDrawText 
= GetProcAddress(hD2Win, (LPCSTR)10064);
        
if (g_pDrawText)
        
{
            memcpy(g_oldcode, g_pDrawText, 
5);
            PatchCALL((DWORD)g_pDrawText, (DWORD)D2WinDrawTextPatch_ASM, 
5);
            
return TRUE;
        }

    }

    
return FALSE;
}


void  RemovePatch()
{
    
if (g_pDrawText)
        WriteLocalBYTES(g_pDrawText, g_oldcode, 
5);
}


BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
    
if (DLL_PROCESS_ATTACH == dwReason)
    
{
        DisableThreadLibraryCalls((HMODULE)hModule);
        
return InstallPatch();
    }

    
else if(DLL_PROCESS_DETACH == dwReason)
    
{
        RemovePatch();
    }

    
return TRUE;
}


//  faked exported function used to bind with js32.dll
extern   " C "  __declspec( dllexport ) VOID FakedInterface()
{
}


 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Google Hacking 是指使用 Google 搜索引擎的高级搜索语法,来寻找互联网上的敏感信息和漏洞的技术。以下是一些常用的 Google Hacking 语法: 1. site:限制搜索结果为某个网站或域名下的页面; 2. filetype:搜索特定类型的文件,如 PDF、DOC、XLS 等; 3. intitle:搜索包含指定关键词在标题中的页面; 4. inurl:搜索包含指定关键词在 URL 中的页面; 5. intext:搜索包含指定关键词在页面正文中的页面; 6. cache:查看 Google 搜索引擎缓存的网页内容; 7. link:搜索某个网站的链接; 8. related:搜索与某个网站相关的其他网站; 9. info:查看某个网站的基本信息,包括 IP 地址、Whois 信息等。 需要注意的是,Google Hacking 语法可以帮助用户快速定位需要的信息,但同时也可能暴露网站的敏感信息,因此需要谨慎使用。 ### 回答2: Google Hacking是指利用Google搜索引擎的高级搜索语法来查找目标网站和相关信息的一种技术。以下是常用的Google Hacking语法: 1. intitle: 可以搜索出标题中含有指定关键词的页面,例如:intitle:”登录” 2. inurl: 可以搜索出网址中含有指定关键词的页面,例如:inurl:”admin” 3. filetype: 可以搜索出指定文件类型的文件,例如:filetype:pdf 4. site: 可以搜索出指定网站域名的页面,例如:site:”example.com” 5. intext: 可以搜索出页面中含有指定关键词的页面,例如:intext:”用户名” 6. link: 可以搜索出指向指定网站的链接,例如:link:”example.com” 7. cache: 可以搜索出Google搜索引擎缓存的页面,例如:cache:example.com 8. related: 可以搜索出与指定网站相关的网站,例如:related:example.com 9. info: 可以搜索出指定网站的相关信息,例如:info:example.com 除了以上的语法,Google Hacking还可以使用一些高级语法,例如使用算术符号来搜索数字范围、使用“?”号来代替任何单个字符等等。Google Hacking的语法非常灵活,只要善于利用,就能快速找到目标网站和相关信息。但同时需要注意,这种技术也容易被黑客利用来找到漏洞并进行攻击,因此作为站长或网民,我们也需要加强网站安全意识和防护措施,避免被攻击和侵犯。 ### 回答3: Google Hacking是利用Google搜索引擎的强大搜索功能,来寻找并获取有价值的信息的方法。在进行Google Hacking时,常用的语法包括以下几种: 1. 搜索特定的文件格式:filetype:pdf / filetype:doc / filetype:xls 等等。这种语法可用于查找特定类型的文档或文件,例如搜索PDF文件时,输入filetype:pdf,会返回所有的PDF文件。 2. 搜索特定站点:site:www.example.com。使用这种语法可以搜索特定的网站,想要查询某个网站所有的页面时,只需要输入site:www.example.com。 3. 搜索精确的短语:” ”或者intitle:。用英文双引号来进行精确匹配,或者使用intitle:键入关键字,然后在该网页的标题中搜索该关键字。 4. 搜索定义的关键词:define:几个关键词。在搜索框中输入define:,然后加上要搜索的单词,就可以搜索该词的定义信息。 5. 查看页面的缓存:cache:url。该语法可以查看Google缓存的页面,在Google结果页面中选择“快照”按钮或者在搜索框中输入cache:url。 6. 搜索相关的网站:related:www.example.com。通过使用related:加上网站名称,可以找到与该网站相关的其他网站。 7. 搜索某个时间段:daterange:startdate-enddate。该语法用于搜索网页发布的时间,只需要在搜索框中键入daterange:startdate-enddate来确定日期范围即可。 8. 使用OR和AND:OR和AND都可以用于组合关键词,如dogs OR cats。 在实践中,人们可以将以上语法进行组合使用,以进行更精准、高效的搜索。但是需要注意:Google Hacking是一种合法的技术,但是在获取信息时一定要遵守相关的法律规定,不得侵犯他人的隐私。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值