吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 56878|回复: 131
收起左侧

[原创] LOLLastHit破解教程

  [复制链接]
crazylin 发表于 2013-7-21 02:46
本帖最后由 crazylin 于 2013-7-22 17:13 编辑

软件名称】: LOLLastHit
【作者邮箱】: crazylin@msn.com
【下载地址】: http://lollasthit.com/
软件语言】: vc++
【使用工具】: OD IDA VS2010
【操作平台】: xp sp3 win7 64
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!


很多人想知道lollasthit(lol英雄联盟辅助)是怎么破解的,我就来写下教程
传送门:这是破解辅助链接:http://www.52pojie.cn/thread-205169-1-1.html
我现在这里讲两种方法:
方法1:用抓包工具分析,很容易这个软件网络验证都是明码发送,而且只要返回一个成功的标志和一个到期时间就验证成功了,所以把正确的包截下来,用本地验证的方式肯定能破解的,如果你们是用易语言而且是用现成模块做本地验证的就不需要看下面的内容了,直接看方法2即可。
原理:hook send函数,使软件链接转向到本地,然后依次返回正确的封包,我汇编比较菜,所以这边介绍一个Hook库,微软的开源平台上有,地址是http://easyhook.codeplex.com/ 优点是他可以hook 64位的程序,这个库本来是供C#hook api用的,但是底层都是用c++和汇编写的,当然c++也是能调用的,支持ring3ring0的,当然我们只要用ring3 下的inline hook 就行。
下面看代码(代码比较繁琐,如果不想用这种方法直接跳到方法2):

[C] 纯文本查看 复制代码
typedef int (WINAPI *PFConnect)(
        __in SOCKET s,
        __in_bcount(namelen) const struct sockaddr FAR * name,
        __in int namelen
        );

int WINAPI MyConnect(
        __in SOCKET s,
        __in_bcount(namelen) const struct sockaddr FAR * name,
        __in int namelen
        );

//定义一些变量
PFConnect                realpfconnect = NULL;
TRACED_HOOK_HANDLE       hHook_Connect = new HOOK_TRACE_INFO();  
ULONG                    Hook_Connect_ACLEntries[1] = {0};

//获取源函数地址
void GetFunctionRealAdderss() 
{ 
        OutputDebugString("PrepareRealApiEntry()\n");  
        // 获取真实函数地址  
        HMODULE  hWS2_32 = LoadLibrary("WS2_32.DLL");  
        if (hWS2_32 == NULL) 
        { 
                OutputDebugString("LoadLibrary(\"WS2_32.DLL\") Error\n");  
                return GetLastError();
        } 
        realpfconnect = (PFConnect)GetProcAddress(hWS2_32, "connect");  
        if (realpfconnect == NULL) 
        { 
                OutputDebugString("GetProcAddress(hWS2_32, \"connect\") Error\n");  
                return GetLastError();  
        } 
        OutputDebugString("GetProcAddress(hWS2_32, \"connect\") OK\n");   
} 
//安装钩子
void InstallHook() 
{
        OutputDebugString("InstallHook()\n");  
        NTSTATUS statue = LhInstallHook(realpfconnect, 
                MyConnect, 
                NULL, 
                hHook_Connect);  
        if(!SUCCEEDED(statue)) 
        { 
                OutputDebugString("LhInstallHook Connect failed\n");  
                return;  
        } 
        OutputDebugString("Hook Connect OK\n");  

        // 一定要调用这个函数,否则注入的钩子无法正常运行。   
        statue = LhSetInclusiveACL(Hook_Connect_ACLEntries, 1, hHook_Connect);  
        if(statue >=0)
                OutputDebugString("Set Connect On\n"); 
}
//卸载钩子
void UnInstallHook() 
{ 
        OutputDebugString("UnInstallHook()\n");  
        LhUninstallAllHooks();  
        LhUninstallHook(hHook_Connect);         
        delete hHook_Connect;
        hHook_Connect = NULL;
        LhWaitForPendingRemovals();  
} 
//关键函数
int WINAPI MyConnect(       
        __in SOCKET s,
        __in_bcount(namelen) const struct sockaddr FAR * name,
        __in int namelen)
{
        //定义IP和端口
        sockaddr_in addr;
        WSAStringToAddress("127.0.0.1:9898",AF_INET, NULL, (LPSOCKADDR)&addr, sizeof(addr));
        //将定义好的IP和端口拷贝到原地址
        memcpy((void*)name,&addr,namelen);
        //然后连接
        return connect(s,name,namelen);
}

//定义TCP服务器,当然这里谢了一个很简单的,能处理软件的封包就可以了,大家用易语言有本地验证模块的话可以略过,我不懂易语言…所以这里用c++写了,网络协议都是基于socket的,TCP协议是基于socket的 ,http协议是基于TCP的
int StartTcpServer()
{
        WSADATA                  wsd;             //WSADATA变量  
        SOCKET                   sServer;        //服务器套接字  
        SOCKADDR_IN              addrServ;      //服务器地址  
        int                      retVal;
        //初始化套结字动态库  
        if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) 
        { 
                OutputDebugString("WSAStartup failed!");
                return 1;  
        } 
        //创建套接字  
        sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);      
        if(INVALID_SOCKET == sServer) 
        { 
                OutputDebugString("socket failed!");
                WSACleanup();//释放套接字资源;  
                return  -1;  
        } 

        //服务器套接字地址   
        addrServ.sin_family = AF_INET;  
        addrServ.sin_port = htons(9898);  
        addrServ.sin_addr.s_addr = INADDR_ANY;        
        //绑定套接字  
        retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));  
        if(SOCKET_ERROR == retVal) 
        {   
                OutputDebugString("bind failed!");
                closesocket(sServer);   //关闭套接字  
                WSACleanup();           //释放套接字资源;  
                return -1;  
        } 

        //开始监听   
        retVal = listen(sServer, 10);  
        if(SOCKET_ERROR == retVal) 
        { 
                OutputDebugString("listen failed!");        
                closesocket(sServer);   //关闭套接字  
                WSACleanup();           //释放套接字资源;  
                return -1;  
        } 
        //为防开主线程卡住,这里另开一个线程开接收客户端
        DWORD lpThreadId;
        HANDLE hThread = CreateThread(
                NULL,
                NULL,
                thTcpServer,
                (LPVOID)sServer,
                NULL,
                &lpThreadId
                );
        CloseHandle(hThread);
}
//接收客户端线程
DWORD WINAPI thTcpServer(LPVOID lparam)
{
        SOCKET          sClient;        //客户端套接字    
        int             retVal;         //返回值 
        SOCKET          sServer = (SOCKET)lparam;

        while(true)
        { 
                //接受客户端请求  
                sockaddr_in addrClient;  
                int addrClientlen = sizeof(addrClient);  
                sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen);  
                if(INVALID_SOCKET == sClient) 
                { 
                        OutputDebugString("accept failed!");        
                        closesocket(sServer);   //关闭套接字  
                        WSACleanup();           //释放套接字资源;  
                        return -1;  
                } 
                //当然这里你可以建个队列什么的把客户端的队列保存起来,因为http端连接,一次发送就收完以后就断开了,所以这里我们不保存
                DWORD lpThreadId;
                HANDLE hThread = CreateThread(
                        NULL,
                        NULL,
                        thProcessDdata,
                        (LPVOID)sClient,
                        NULL,
                        &lpThreadId
                        );
                CloseHandle(hThread);
        }
        return 0; 
}
//处理数据线程
DWORD WINAPI thProcessDdata(LPVOID lparam)
{
        const int       BUF_SIZE = 4096;  
        SOCKET          sClient = (SOCKET)lparam;//客户端套接字  
        char            buf[BUF_SIZE];    //接收数据缓冲区  
        int             retVal;            //返回值 
        while(true)
        { 
                if(sClient!=NULL)
                {
                        //接收客户端数据  
                        ZeroMemory(buf, BUF_SIZE);  
                        retVal = recv(sClient, buf, BUF_SIZE, 0);  
                        if (SOCKET_ERROR == retVal) 
                        { 
                                OutputDebugString("recv failed!");       
                                closesocket(sClient);   //关闭套接字       
                                WSACleanup();           //释放套接字资源;  
                                return -1;  
                        }
                        //这里处理数据
                        //buf是什么封包,你就把对应的封包发回去
                        //send(sClient,封包,封包长度, 0); 
                }
        }
}

方法2:先查壳,UPX的壳,怎么脱我就不说了,各位搜索一下论坛应该就能找到,我们打开软件帐号密码随便输,点LOGIN(登录),出现INVALID VIP ACCOUNT OR PASSWORD(无效的账户或密码),那好我们就直接搜索字符串,我个人比较喜欢先用IDA分析,有插件可以直接生成C源码,因为不懂汇编嘛。打开IDA搜索字符串
如下图:
1.png
第一个在函数sub_403370里面,当然是点开第一个进去了

2.png
大家仔细看INVALID VIP ACCOUNT OR PASSWORD 附近有很多验证成功和失败的不同字符串,我们发现“"%s IS VALID UNTIL %s"(XX是有效的直到XX时候) 这个应该就是验证成功的字符串,我们按F5 看看sun_403370这个函数生成的C代码
3.png
打开OD,载入软件,ctrl+G 输入00403370 回车,来到这里,然后在这个地址下断点,F9运行程序,随便输入帐号密码点登录,然后断下来了,看下图
4.png
我们看到EDI 点数据窗口跟随,看左下角,看到是完整的登录返回封包的数据,但根据经验判断,这个应该是主程序接收tcp数据的缓冲区,我们再找其他地址(数据窗口跟随),最后发现ECI地址上的数据比较可疑
5.png
看上图,我们可以确定这些是存放验证信息的地址,然后我们要做的就是修改这个内存中的数据,怎么修改?
先讲第一种方法,先找一个没有数据的代码段,我找了一个0x004A6913然后0x00403370处修改代码jmp 0x004A6913(使用了五个字节),0x004A6913处修改如图所示,修改的数据是“4\n9999/11/11 11:11:11\n0”再补回原0x00403370处的五个字节,然后跳回(0x00403370+0x5
8.png
7.png
修改完保存,运行软件,随便输入帐号密码,点登录果断登录成功,界面显示XXX is vailID until 9999/11/11 11:11:11
别高兴太早,这软件还有一个定时检测线程,如果检测不通过,会显示LOGIN TO YOUR VIP ACCOUNT TO ACTIVATE THE TOOL,我们超找一下字符串就好了,出来三个,我们没个都下断点,然后登录软件,一会儿,其中一处就断下来了,我们过去看到上面有哥jnz,改成jmp就可以了地址是0x004032EA,然后保存,软件就破解成功了。

但是还有方法2,我不会写汇编,上面写的也是突发奇想写出来的- -。,我来介绍下用hook的方式来解决这个问题。

为了代码简单点,我直接写了,已经验证是可以的
[C] 纯文本查看 复制代码
typedef void  (_fastcall *PFSub_3370)(DWORD ecx, DWORD edx,DWORD a1, DWORD a2, DWORD a3);
void _fastcall MySub_3370(DWORD ecx, DWORD edx,DWORD a1, DWORD a2, DWORD a3);

PFSub_3370 realSub_3370 = NULL;   
TRACED_HOOK_HANDLE      hHook_Sub_3370 = new HOOK_TRACE_INFO();  
NTSTATUS                  statue;  
ULONG                      Hook_Sub_3370_ACLEntries[1] = {0};  

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                                         )
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
                {
                        hModule = GetModuleHandle(NULL);
                        realSub_3370 = (PFSub_3370)((DWORD)hModule+0x3370);

                        statue = LhInstallHook(realSub_3370,  MySub_3370,  NULL, hHook_Sub_3370);  
                        if(!SUCCEEDED(statue))  
                        {  
                                OutputDebugString("LhInstallHook Sub_3370 failed\n");   
                        }  
                        OutputDebugString("Hook Sub_3370 OK\n"); 
                        LhSetExclusiveACL(Hook_Sub_3370_ACLEntries, 1, hHook_Sub_3370);  
                        
                        //X掉检测
                        DWORD checkAddr =  ((DWORD)hModule + 0x32EA);
                        unsigned char checkAddrData[2] ={0xEB,0x7E};
                        VirtualProtect((LPVOID)checkAddr,0x1000,PAGE_EXECUTE_READWRITE,NULL);
                        WriteProcessMemory((void*)-1, (LPVOID)checkAddr, checkAddrData, sizeof(checkAddrData), NULL); 

                }
                break;
        case DLL_THREAD_ATTACH:
                break;
        case DLL_THREAD_DETACH:
                break;
        case DLL_PROCESS_DETACH:

                LhUninstallAllHooks();    
                LhUninstallHook(hHook_Sub_3370);  
                delete hHook_Sub_3370;  
                hHook_Sub_3370 = NULL;  
                LhWaitForPendingRemovals();  
                break;
        }
        return TRUE;
}
void _fastcall MySub_3370(DWORD ecx, DWORD edx,DWORD a1, DWORD a2, DWORD a3)
{
        char* tmpData = "4\n2999/09/09 09:09:09\n0";
        memcpy((void*)(ecx+0xB8),tmpData,strlen(tmpData));
        return (*realSub_3370)(ecx,edx,a1,a2,a3);
}


看了@半斤八两的初级教程 “27.Patch机器码” 其实这个就是inline hook的原理 活学活用 我也来实现下
[C] 纯文本查看 复制代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

DWORD g_dwSrcAddr = 0;
DWORD g_dwJmpAddr = 0;
DWORD g_dwSrcAddrNext = 0;
//32位程序远jmp+地址是5个字节  64是12个字节
unsigned char szJmp[5] = {0xE9};
char* tmpData = "4\n2999/09/09 09:09:09\n0";

void __declspec (naked)  PatchData()
{
        _asm
        {
                //不会汇编,只会这样写 - -。
                mov byte ptr ds:[ecx+0xB8],0x34
                mov byte ptr ds:[ecx+0xB9],0xA
                mov byte ptr ds:[ecx+0xBA],0x32
                mov byte ptr ds:[ecx+0xBB],0x39
                mov byte ptr ds:[ecx+0xBC],0x39
                mov byte ptr ds:[ecx+0xBD],0x39
                mov byte ptr ds:[ecx+0xBE],0x2F
                mov byte ptr ds:[ecx+0xBF],0x31
                mov byte ptr ds:[ecx+0xC0],0x31
                mov byte ptr ds:[ecx+0xC1],0x2F
                mov byte ptr ds:[ecx+0xC2],0x31
                mov byte ptr ds:[ecx+0xC3],0x31
                mov byte ptr ds:[ecx+0xC4],0x20
                mov byte ptr ds:[ecx+0xC5],0x31
                mov byte ptr ds:[ecx+0xC6],0x31
                mov byte ptr ds:[ecx+0xC7],0x3A
                mov byte ptr ds:[ecx+0xC8],0x31
                mov byte ptr ds:[ecx+0xC9],0x31
                mov byte ptr ds:[ecx+0xCA],0x3A
                mov byte ptr ds:[ecx+0xCB],0x31
                mov byte ptr ds:[ecx+0xCC],0x31
                mov byte ptr ds:[ecx+0xCD],0xA
                mov byte ptr ds:[ecx+0xCE],0x30
                //还原5个字节
                push ebp
                mov ebp,esp
                push -0x1
                //跳回
                jmp g_dwSrcAddrNext
        }
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                                         )
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
                {
                        //获取基址
                        hModule = GetModuleHandle(NULL);
                        //源地址
                        g_dwSrcAddr = (DWORD)hModule + 0x3370;
                        //目标地址
                        g_dwJmpAddr = (DWORD)PatchData - g_dwSrcAddr - 5;
                        //跳回地址
                        g_dwSrcAddrNext = g_dwSrcAddr + 5;

                        //组合jmp地址 例 jmp 0x0041000
                        memcpy(&szJmp[1],&g_dwJmpAddr,4);
                        DWORD dwOldProtect;
                        VirtualProtect((void*)g_dwSrcAddr,0x1000,PAGE_EXECUTE_READWRITE,&dwOldProtect);
                        //把jmp跳转赋值到源地址上面
                        memcpy((void*)g_dwSrcAddr,szJmp,sizeof(szJmp));

                        //======================
                        //====X掉检测============
                        //======================
                        DWORD checkAddr =  ((DWORD)hModule + 0x32EA);
                        unsigned char checkAddrData[2] ={0xEB,0x7E};
                        VirtualProtect((LPVOID)checkAddr,0x1000,PAGE_EXECUTE_READWRITE,NULL);
                        WriteProcessMemory((void*)-1, (LPVOID)checkAddr, checkAddrData, sizeof(checkAddrData), NULL); 
                }
                break;
        case DLL_THREAD_ATTACH:
                break;
        case DLL_THREAD_DETACH:
                break;
        case DLL_PROCESS_DETACH:
                break;
        }
        return TRUE;
}

点评

谢谢撸主分享学习心得~  发表于 2013-7-21 07:44
欢迎到动画视频区来做客 很好~  发表于 2013-7-21 07:43

免费评分

参与人数 14热心值 +14 收起 理由
wxw1145897898 + 1 热心回复!
sunflover + 1 给个精华不为过吧??
小六升臣 + 1 牛逼的一塌糊涂,完全看不懂
ukonw3477 + 1 欢迎分析讨论交流,[吾爱破解论坛]有你更精.
邦德 + 1 欢迎分析讨论交流,[吾爱破解论坛]有你更精.
gossipboyuu + 1 感谢发布原创作品,[吾爱破解论坛]因你更精.
天山雪鹰 + 1 很牛B,我完全看不懂
Chief + 1 欢迎分析讨论交流,[吾爱破解论坛]有你更精.
19nuclear91 + 1 我很赞同!
ebookread + 1 支持很好的教程
bestow + 1 欢迎分析讨论交流,[吾爱破解论坛]有你更精.
qiusuo + 1 我很赞同!
qq934679515 + 1 完全看不懂- - 好高深! 为楼主顶下!
佩慈pest_OHyeah + 1 支持大神 学习了

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

261044691 发表于 2013-7-21 02:49
谢谢楼主分享
 楼主| crazylin 发表于 2013-7-21 02:46
本帖最后由 crazylin 于 2013-7-21 02:58 编辑

占楼占楼占楼占楼占楼占楼占楼占楼 - -占沙发
1354669803 发表于 2013-7-21 02:50
qq8803128 发表于 2013-8-3 14:37
crazylin 发表于 2013-8-3 12:33
修改输入表,让他自导加载你的dll

我用C程序修改的导入表啊  就是修改不成功
佩慈pest_OHyeah 发表于 2013-7-21 03:01
我也学学~~~~~自己可以破解就好了
isaacchen 发表于 2013-7-21 03:04
技术贴支持一下啊。。
耶蘇丶上帝 发表于 2013-7-21 03:04
前排必须顶, 后台挂机的在P一个那就完美了  {:1_902:}
佩慈pest_OHyeah 发表于 2013-7-21 03:07
耶蘇丶上帝 发表于 2013-7-21 03:04
前排必须顶, 后台挂机的在P一个那就完美了

要求太多  
鱼子酱 发表于 2013-7-21 04:21
hook 是什么?
vip_zhizun 发表于 2013-7-21 04:25 来自手机
看不懂。。。。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 11:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表