无瑕黑心肠 发表于 2016-6-1 00:27

完美破解CS1.6单机版(免CDK) & 带重定位爆破

本帖最后由 无瑕黑心肠 于 2016-6-2 08:05 编辑

前言:

   网上找的CS1.6都是需要CDK的, 于是我就想:"游戏的反破解难道很强大?"网上居然没有免CDK的CS1.6单机版? 于是怀着好奇心默默打开了吾爱破解的OD...


正题:
   首先得搞清楚一点, 游戏的CDK是怎么保存的, 保存在哪? 不难发现网上都有"CS1.6_CDK.reg", 想必都是注册表了. 进去里面看看能发现是在这个路径:"HKEY_CURRENT_USER\Software\Valve\Half-Life\Settings", 好思路有了:
      在注册表操作的API"RegOpenKeyExA"上下断, 然后找到关键跳或关键CALL.
   老规矩, 破解先查一下壳(其实我知道这么做是多余的, 因为游戏注重的是运行效率, 因此游戏如果加了加密壳之类的, 游戏根本没法玩), 查壳结果当然是无壳的C++程序啦!
   拖入OD, F9好几十步后发现读取了这个关键的位置("HKEY_CURRENT_USER\Software\Valve\Half-Life\Settings"):
   
   在堆栈中逐个查找返回到的CALL附近是否有关键跳或关键CALL, 跟了一会儿果然找到很显眼的地方:
   
   下面的 je 是判断注册表路径是否存在的, 存在就继续往下, 往下就是关键跳了:
   
   jnz就是判断CDK是否正确的, 这里直接爆破就成功一半了, 什么? 才一半? 不急, 继续往下看可以看到:
   
   我猜想这应该是CDK分成两部分保存了, 然后验证也是分成两部分验证, 和上面的一样, 它下面的第一个"跳转"是判断注册表路径是否存在的, 它下面的第二个"跳转"就是判断是否为正确CDK的:
   
   而且第二个"跳转"下面有一句"CD KEY INVALID", 也就是"CDK无效"的意思, 思路一下明了了, 至此, 改这个四个个关键跳转就能实现免CDK进入游戏了.
   [原]06612AD2   /74 4C         je short GameUI.06612B20
   [改]06612AD2   /74 00         je short GameUI.06612AD4

   [原]06612AE8   /75 36         jnz short GameUI.06612B20
   [改]06612AE8   /75 00         jnz short GameUI.06612AEA

   [原]06612B07   /74 17         je short GameUI.06612B20
   [改]06612B07   /74 00         je short GameUI.06612B09

   [原]06612B1A   /0F84 9B000000   je GameUI.06612BBB
   [改]06612B1A   /E9 9C000000   jmp GameUI.06612BBB
   重点来了, 仔细看这个区块是属于"GameUI"的, 也就是cs1.6目录下的DLL, 这个DLL带有"动态重定位"(DLL有静态重定位和动态重定位, 详情可以百度一下), 修改后的代码是无法写出到文件里面的, 因为这是虚拟地址, 不是DLL的真实地址, 此时唯一的办法就是, 计算出偏移, 然后用c32asm进行爆破修改, 但是需要修改的地方有四个, 需要进行浓缩一下, 重新找爆破点, 往上翻, 能找到一句可以完美利用的跳转:
   
   这个跳转就是直接跳过验证的, 但必须条件成立, 也就是前面的"test eax,eax"的结果不为0, 意思就是两数做"与(&)"运算, eax一般就是call的返回值了, 一眼望去, 肯定是上面那个call的, 但是先不管那个call, 将这个"test eax,eax"改成"test esp, esp"条件就成立了, 新手朋友们肯定一脸懵逼了, 为什么改esp就可以了? 原理是什么. 其实就是那esp的特性, esp是不可能为0的, 除非程序执行了清0操作"xor esp, esp", 那么它不为0, 执行"test esp, esp"操作后肯定不为0啦, 两个不为0的数做"与(&)"运算, 结果是不为0的.
   说了这个多, 爆破点就定在地址"0x0AAA2A51"了, 问题来了. 刚才说的"动态重定位"呢? 该是时候了, 这时将滚动条拉到这个区块的顶部:
   
   记下地址, 地址是"0x0AA81000", 用"0x0AAA2A51"减去"0x0AA81000"就得到了这个爆破点的偏移"0x21A51", 至此可以关闭OD打开c32asm了, 打开后找到入口地址(通常往下拉, 经过一段空白就到了):
   
   在这个地址头部右键 -> 对应汇编模式编辑:
   
   可以看到汇编模式编辑下的地址是0x10001000:

   
   用这个"0x10001000"加上刚才计算出来的偏移"0x21A51"结果是"0x10022A51", Ctrl + G跳到这个地址, 发现这个就是爆破点的跳转语句了, 不过这个不是要修改的, 要修改的是上一句:
   
   将这个"test eax,eax"改成"test esp, esp"然后保存就爆破了. 怎么样是不是很简单?

总结:
   这一期的帖子主要讲解了:
      1. 程序的多个爆破思路
      2. DLL重定位无法写出问题
      3. 傻瓜式的爆破点定位方法
   小伙伴们是不是又 GET 到新技能了呢? 赶紧收藏一下或做一下笔记吧!

   其实还有一个就是CS1.6安装完成, 注册了CDK后, 电脑重装, CS1.6的CDK又得注册一次. 经过破解后直接免CDK, 还是比较方便的. CS1.6, 都是8090后的回忆呀.
   这个免CDK教程算是十分简单的, 只要OD载入后直接搜索"yek2"这个字符串就能定位到关键跳转语句附近了, 并不需要在注册表的API下断这么麻烦, 但是这个只是破解的一个思路. 我还是希望新手朋友们能从中学到好东西. 感谢所有阅贴的朋友! 破解的道路依旧遥远, 就让我们的经验和时间来证明这一切!

序: 关于上次CS1.6破解在WIN7x64会闪退的深入剖析
http://www.52pojie.cn/forum.php?mod=redirect&goto=findpost&ptid=503166&pid=12690801






无瑕黑心肠 发表于 2016-6-1 22:11

关于上次CS1.6破解在WIN7x64会闪退的深入剖析

本帖最后由 无瑕黑心肠 于 2017-2-15 14:05 编辑

前言:
   上次破解的CS1.6在WIN7 x64下会崩溃的问题, 详情:http://www.52pojie.cn/thread-503166-1-1.html. 今天打开运行一下老是崩溃, 再次载入OD发现原来是破解还不够完美, 只是阻止了弹出填写CDK的窗口, 我还真是小看这CS1.6单机版了. 经过我的深层剖析, 它的校验比较多, 而且还会检测自身的指纹(不清楚MD5还是CRC32之类的). 废话不多说, 此文章比较长, 如果你耐心看完, 也许你能从中 GET 到更多技巧和破解思路!


正题:
   上期的帖子破解CS1.6中的思路是在注册表操作"RegQueryValueExA"上下断, 而且将"test eax,eax"改为"test esp,esp"就能破解了. 事实是如此. 但是如果在"新建游戏" -> 点下"确定"的这一刻, 程序又会再次验证注册表的CDK, 如果CDK不正确, 会有很多坏情况:
   CDK无效:
   坏的CDK:
   还有什么CDK太短, CDK太长, CDK字符非法(CDK不允许0(零)、O(欧)、1、U, 这四个字符)等等...
   先不管这些坏情况, 知道它"新建游戏"会再次检测CDK, 就在第一次CDK检测后再往"RegQueryValueExA"这个API下断, 找到源头后发现是在一个模块文件"mp.dll"里面(文件在"\cstrike\dlls"目录下), 也就是说, 爆破又是存在重定位的, 而且经我的调试, 这个DLL在程序载入后是看不到的, 它是"新建游戏"时才动态加载的. 来看看它检测CDK的地方:
   
   
   计算到这个检测点的"偏移"后立马用C32ASM爆破掉这三个跳转, 因为正确的CDK是走下面的retn的. 为什么要立马爆破掉? 因为这个DLL文件是动态加载的, "新建游戏"的一瞬间这个DLL就被加载而且把代码也跑了, 手慢到了这里下断是没用的, 而且注册表的"RegQueryValueExA"断点不能一直断着, 会影响破解时的流程. 所以马上爆破保存对接下来的调试会比较好.
   爆破掉这里后心想肯定就能完美的运行CS1.6了, 但, 根据我的剧情, 这是不可能的. 因为游戏自身模块"cstrike"还有一个验证CDK的, 于是就找到这个地方:
   
   不跳的话里面就是"受到挑战?"(实在不知道怎么拆解这英文), 如果跳的话就是"无效的身份验证类型", 这岔路看的我云里雾里的. 先把正确的CDK注册, 然后跑到这里会发现它是实现跳转的, 难道这个"无效的身份验证类型"是个幌子? 先不管是不是幌子, 既然正确CDK是这么走那么直接改JMP就能爆破这最后一个校验了.
   但是正当我保存的时候, 意外就是这么突然:
   
   无法定位? 程序一般不带重定位呀! 怎么还无法定位? 这让我疑惑不解, 记住这么爆破点的地址"01D1B419", 重新载入程序跳过来一看:
   
   居然是空的? 我猜这里的代码是不是被程序压缩没解压? 于是在这里下了个内存写入断点, 果不其然:
   
   一开始看到这个汇编代码我也懵了, 因为我也没接触过, 去百度一下它的作用是:"循环从ESI指向的内存区域复制数据到EDI处的内存区域,复制ECX次,每次是一个DWORD", 内存区域复制? 真是个棘手的问题, 我去找了很久找这个ESI指向的内存区域是不是在程序某个地方, 结果发现这个地方是动态的, 也是放弃了. 不过找到一个新的线索, 它写入的地方每次都是不一样的(不可能一个地方内存写入两次一样的东西吧, 这样没啥意义), 因此, 可以自己控制程序的逻辑, 判断它写入的地址是"爆破点"时就把"爆破点"的字节给它改了, 说干就干, 找一段空代码段, 然后将这个内存复制的代码改成"jmp 0140B8CA":
   
   可以发现下面的那个跳转给nop掉了, 没事, 在空代码段补回来就行了. 具体的控制逻辑:
   
   解释一下这几句的汇编代码的意思:
   第一句: 还是那句内存复制的汇编代码
   第二句: 比较当前复制的目的内存区域是不是爆破点的区域
   第三句: 如果当前复制的目的内存区域是爆破点的区域就将"爆破点"的字节进行"爆破"(也就是"je"(74)改"jmp"(EB)), 如果不是就执行之前抹掉的那句jmp, 整个逻辑就这么简单.
   爆破点的爆破字节原本是"682F74C0", 这是它进行内存复制时截取的, 由于数据储存在反过来的, 例如:"12345678", 那么在数据窗口看到的就是就是"78563412", 所以, 这个"682F74C0", 也要倒过来看, 也就是"C0742F68", 是不是很眼熟? 没错, "742F"就是爆破点的汇编字节码:
   
   改成将74改成EB就成"jmp short 01D1B44A"了, 也就是上面的"第三句"的汇编的意思.
   右键 -> 复制到可执行文件 -> 所有修改, 终于保存成功了.
   高兴的还是太早了, 此时打开游戏居然报错了:
   
   程序的自校验来了, 由于时间太晚了, 就不想继续跟自校验了. 有兴趣的朋友可以跟一下.
   办法不是没用, 这期就再教大家内存补丁吧! 打开"KeyMake2.0", 其他 -> 制作内存补丁:
   
   内存补丁里面修改的全是刚才那几句修改的汇编代码, 改完保存再运行CS1.6就真的完全免CDK了, 这次有图有真相:
   
   


附上爆破点和内存补丁以及成品:

DLL爆破点以及偏移C32ASM的地址:
   // 爆破点一(模块:GameUI)
   [原]09C52A4F    85C0            test eax,eax
   [改]09C52A4F    85E4            test esp,esp
   [模块首地址]09C31000    53            push ebx
   [偏移]0x21A4F      (C32ASM: 10022A4F)

   // 爆破点二(模块:mp)
   [原]0E0A5AF0    81EC 18020000   sub esp,0x218
   [改]0E0A5AF0    C3            retn
   [模块首地址]0E031000    53            push ebx
   [偏移]0x74AF0      (C32ASM: 10075AF0)

内存补丁:
   // 原始指令(014027F3)
   F3A5FF2495
   // 修改指令(014027F3)
   E9D2900000
   
   // 原始指令(014027F8)
   08294001
   // 修改指令(014027F8)
   EBF9EBF7
   
   // 原始指令(0140B8CA)
   0000000000000000
   // 修改指令(0140B8CA)
   F3A5817D080010D0
   
   // 原始指令(0140B8D2)
   0000000000000000
   // 修改指令(0140B8D2)
   01750AC70518B4D1
   
   // 原始指令(0140B8DA)
   0000000000000000
   // 修改指令(0140B8DA)
   01C0EB2F683EFF24
   
   // 原始指令(140B8E2)
   0000000000
   // 修改指令(140B8E2)
   9508294001

成品: http://pan.baidu.com/s/1slMwKnb 密码:dsd2


总结:
   废话我也不想叨叨一堆了, 这期里面有很多知识点是非常新手的, 但有些是半知不解的, 在这里我想说的是:"度娘是你很好的老师, 它总有意想不到的答案给你", 遇到不懂的问题多多百度. 这期主要将了一些破解思路、手写汇编控制程序流程、内存补丁的制作. 当然还参杂了其他的知识点等朋友们一一去了解和理解, 很感谢大家的阅贴. 我会一如既往的不断努力. 当然最近偏向的还是破解部分. 我不像大伙的帖子"非零基础者请勿看以下内容, 以免易怒粗口"啥的. 我这里欢迎所有人, 新手经过能学到东西, 当然有高手经过发现帖子有不好的地方和误解的地方还请大方的指出. 我一定会非常感谢.

展鸿 发表于 2016-6-2 00:25

楼主辛苦了,HL引擎是有服务端和客户端两部分的。
客户端启动的时候会验证CDKEY,客户端连接服务器的时候会把CDKEY上传给服务器再次验证。

void CGameUI::Start(struct cl_enginefuncs_s *engineFuncs, int interfaceVersion, void *system)
{
        memcpy(&gEngfuncs, engineFuncs, sizeof(gEngfuncs));

        vgui::VPANEL rootpanel = g_pEngineVGui->GetPanel( vgui::PANEL_GAMEUIDLL );

        scheme()->LoadSchemeFromFile( "Resource/SourceScheme.res", "SourceScheme" );

        char cdkey;

        vgui::system()->GetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Half-Life\\Settings\\ValveKey", cdkey, sizeof(cdkey) - 1);

        if (!strlen(cdkey))
        {
                vgui::system()->GetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Half-Life\\Settings\\yeK1", cdkey, sizeof(cdkey) - 1);

                if (!strlen(cdkey))
                {
                        vgui::system()->GetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Half-Life\\Settings\\yeK2", cdkey, sizeof(cdkey) - 1);

                        if (!strlen(cdkey))
                        {
                                if (!g_pCDKeyEntryDialog)
                                {
                                        g_pCDKeyEntryDialog = new CCDKeyEntryDialog(g_pBasePanel, false);
                                }

                                g_pCDKeyEntryDialog->Activate();
                        }
                }
        }
}

int SV_FinishCertificateCheck( netadr_t *adr, int nAuthProtocol, char *szRawCertificate, char *userinfo )
{
        int nHashCount = 0;
        char *val;

        // Now check auth information
        switch ( nAuthProtocol )
        {
        default:
                if ( stricmp( szRawCertificate, "steam" ) )
                {
                        SV_RejectConnection( adr, "Expecting STEAM authentication USERID ticket!\n" );
                        return 0;
                }
                break;
        case PROTOCOL_HASHEDCDKEY:
                {
                int i;

                if ( Q_strlen( szRawCertificate ) != 32 )
                {
                        SV_RejectConnection( adr, "Invalid CD Key.\n" );
                        return 0;
                }

                val = Info_ValueForKey( userinfo, "*hltv" );

                // Now make sure that this hash isn't "overused"
                for ( i=0; i<svs.maxclients; i++ )
                {
                        if (!svs.clients.active && !svs.clients.spawned && !svs.clients.connected)
                                continue;

                        if ( Q_strnicmp ( szRawCertificate, svs.clients.hashedcdkey, 32 ) )
                                continue;

                        nHashCount++;
                }

                if (nHashCount >= MAX_IDENTICAL_CDKEYS)
                {
                        SV_RejectConnection(adr, "CD Key already in use.\n");
                        return 0;
                }
                }
                break;
        }

        return 1;
}

SomerHalder 发表于 2016-6-1 00:41

膜拜 1.5玩得多虽然N多年没玩了。。。。

红客鄙哥 发表于 2016-6-1 00:30

可以,看到CS1.6又回想起当年。。

szjhpz 发表于 2016-6-1 00:51

感谢楼主的无私分享 好人一生平安

吾爱win 发表于 2016-6-1 01:03

红客鄙哥 发表于 2016-6-1 00:30
可以,看到CS1.6又回想起当年。。

你也在水仓上网

sherry5566 发表于 2016-6-1 01:15

        很喜欢的经典

2015abc 发表于 2016-6-1 01:45

玩过csol,但是再也没有以前玩的那样的感受了,那时候1.3 1.5玩的比较多

txt 发表于 2016-6-1 02:47

厉害,这老经典你也能想起来破{:301_978:}

jojaajj 发表于 2016-6-1 07:04

牛逼啊!!
{:301_1001:}

王美君 发表于 2016-6-1 07:50

经典的游戏
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 完美破解CS1.6单机版(免CDK) & 带重定位爆破