我最讨厌别人拿我的东西改改就去买,我的都是免费发布的,如果谁想拿去买就自觉离开,前段时间,发了个给大家乐和的东西,就有人改了后报价了,我很厌烦,这次真不想发了,再说,也许有人要吧! 卖几个玛雅之石吧!现在穷啊!
主题开始:和以前一样,先说原理,9C经过1.00以后的版本,对客户端实行了2次校验,这次比前一次更TMD的不是玩意!上一次是程序校验很简单的被高手发现并绕开,这次我看就不大容易了,我就不说第一次怎么跳了,我就只说二次校验. 先从进游戏来看,一进游戏没穿衣服掉线为第一次校验无法通过,当穿衣服进游戏掉线为第二次校验无法通过,根据协议为: 这里以1.02.22版本为基础: //C30D7C2C5258D1B4513A810F35 //DEC:C30D03038A00925CFD 最后面的三个字节为服务端发给客户的校验码,也就是加密种子 //数据包是从这里发出去的,但是这个SEND是不能跳掉的,几乎好多C3都是从这里发出去的. 004FD8A8|.6A00|push0;/Flags=0 004FD8AA|.8D543C38|leaedx,dwordptr[esp+edi+38];| 004FD8AE|.51|pushecx;|DataSize 004FD8AF|.52|pushedx;|Data 004FD8B0|.50|pusheax;|Socket 004FD8B1|.FF1520657700|calldwordptr[<&ws2_32.send>];\send //从文件的上面我找到了相关的函数地址在: 004FD5B8|>\66:FF84243C0>incwordptr[esp+13C] 004FD5C0|>8B8424541900>moveax,dwordptr[esp+1954] 004FD5C7|.66:8B4804movcx,wordptr[eax+4] 004FD5CB|.51pushecx 004FD5CC|.E8A83DFDFFcall004D1379;加密是从这个函数进行的 004FD5D1|.8B8C24400100>movecx,dwordptr[esp+140] 004FD5D8|.83C404addesp,4 004FD5DB|.81E1FFFF0000andecx,0FFFF 004FD5E1|.8D5104leaedx,dwordptr[ecx+4] 004FD5E4|.81FA00080000cmpedx,800 //对主函数控制流程进行了分析: //--------------------------------------------流程分析------------------------------------------------ 004D137955pushebp 004D137A8BECmovebp,esp 004D137C6AFFpush-1 004D137E68ACB87600push0076B8AC 004D138364:A100000000moveax,dwordptrfs:[0] 004D138950pusheax 004D138A64:89250000000>movdwordptrfs:[0],esp 004D139181EC30090000subesp,930 004D139753pushebx;5D 004D139856pushesi;C1060300D765把C3当C1来处理 004D139957pushedi;03 004D139A66:8B4508movax,wordptr[ebp+8];ax=65d7 004D139E50pusheax 004D139FE8A8FEFFFFcall004D124C //此处的CALL是需要对服务端发来的数据进行处理,处理代码为下面: //---------------------------------------------------------------------------------- 004D124C55pushebp 004D124D8BECmovebp,esp 004D124F51pushecx 004D12508B4508moveax,dwordptr[ebp+8] 004D125325FFFF0000andeax,0FFFF 004D12583579B40000xoreax,0B479 004D125D66:8945FCmovwordptr[ebp-4],ax 004D12618B45FCmoveax,dwordptr[ebp-4] 004D126425FFFF0000andeax,0FFFF 004D1269C1F80Asareax,0A 004D126CC1E004shleax,4 004D126F8B4DFCmovecx,dwordptr[ebp-4] 004D127281E1FFFF0000andecx,0FFFF 004D127883E10Fandecx,0F 004D127B0BC1oreax,ecx 004D127D8BE5movesp,ebp 004D127F5Dpopebp 004D1280C3retn //---------------------------------------------------------------------------------- //获取当前MAIN模块句柄,一般为00400000 //---------------------------------------------------------------------- 004D13A483C404addesp,4 004D13A766:894508movwordptr[ebp+8],ax;ax=034e 004D13ABFF15D8617700calldwordptr[7761D8];getmodulehandle //获取路径,从路径中把命令行取掉,变成纯路径 //---------------------------------------------------------------------------- 004D13BD51pushecx 004D13BE8D95E8FEFFFFleaedx,dwordptr[ebp-118] 004D13C452pushedx 004D13C5E8D6FCFFFFcall004D10A0;killcommandlineforpath 004D13D9.6880000000push80;|Attributes=NORMAL 004D13DE.6A03push3;|Mode=OPEN_EXISTING 004D13E0.6A00push0;|pSecurity=NULL 004D13E2.6A01push1;|ShareMode=FILE_SHARE_READ 004D13E4.6800000040push40000000;|Access=GENERIC_WRITE 004D13E9.8D85E8FEFFFFleaeax,dwordptr[ebp-118];| 004D13EF.50pusheax;|FileName 004D13F0.FF1578617700calldwordptr[<&kernel32.CreateFile>;\CreateFileA 004D13F6.8985D8FEFFFFmovdwordptr[ebp-128],eax //--------------------------------------------------------------------------------------------------------------- ;以写模式打开,如果打开成功就写,这个说明,如果要是当前打开的不是正在运行的程序,那么它就可以以写模式打开,这样同样校验通不过,如果是程序打开自己,并对自己写操作,那是不经处理,无论如何也不会成功的. //------------------------------------------------------------------------------------------------------------------ 004D13FC.83BDD8FEFFFF>cmpdwordptr[ebp-128],-1 004D1403.746Ejeshort004D1473 004D1405.8D8DCCF6FFFFleaecx,dwordptr[ebp-934];下面是处理函数 004D140B.E85024F3FFcall00403860 004D1410.C745FC00000>movdwordptr[ebp-4],0 004D1417.68F1000000push0F1;/Arg2=000000F1 004D141C.68C1000000push0C1;|Arg1=000000C1 004D1421.8D8DCCF6FFFFleaecx,dwordptr[ebp-934];| 004D1427.E89424F3FFcall004038C0;\UnpackMa.004038C0 004D142C.6A00push0;/Arg1=00000000 004D142E.6A03push3;|/Arg1=00000003 004D1430.6A03push3;||/Arg1=00000003 004D1432.8D8DCCF6FFFFleaecx,dwordptr[ebp-934];||| 004D1438.E8C32CF3FFcall00404100;||\UnpackMa.00404100 004D143D.8BC8movecx,eax;|| 004D143F.E8BC2CF3FFcall00404100;|\UnpackMa.00404100 004D1444.8BC8movecx,eax;| 004D1446.E8B52CF3FFcall00404100;\UnpackMa.00404100 004D144B.6A00push0;/Arg2=00000000 004D144D.6A01push1;|Arg1=00000001 004D144F.8D8DCCF6FFFFleaecx,dwordptr[ebp-934];| 004D1455.E87626F3FFcall00403AD0;\UnpackMa.00403AD0 004D145A.C745FCFFFFF>movdwordptr[ebp-4],-1 004D1461.8D8DCCF6FFFFleaecx,dwordptr[ebp-934] 004D1467.E81424F3FFcall00403880 004D146C.33C0xoreax,eax 004D146E.E9D5000000jmp004D1548 004D1473>6A00push0;/hTemplateFile=NULL 004D1475.6880000000push80;|Attributes=NORMAL 004D147A.6A03push3;|Mode=OPEN_EXISTING 004D147C.6A00push0;|pSecurity=NULL 004D147E.6A01push1;|ShareMode=FILE_SHARE_READ 004D1480.6800000080push80000000;|Access=GENERIC_READ 004D1485.8D8DE8FEFFFFleaecx,dwordptr[ebp-118];| 004D148B.51pushecx;|FileName 004D148C.FF1578617700calldwordptr[<&kernel32.CreateFile>;\CreateFileA 004D1492.8985D8FEFFFFmovdwordptr[ebp-128],eax;如果写模式打开失败,就以读模式打开,由于是前面对自己进行写操作,80%都会是在这里进行的,现在就用读模式打开 004D1498.83BDD8FEFFFF>cmpdwordptr[ebp-128],-1 004D149F.7507jnzshort004D14A8 004D14A1.33C0xoreax,eax 004D14A3.E9A0000000jmp004D1548;打开失败就返回, 004D14A8>6A00push0;/pFileSizeHigh=NULL 004D14AA.8B95D8FEFFFFmovedx,dwordptr[ebp-128];| 004D14B0.52pushedx;|hFile 004D14B1.FF15DC617700calldwordptr[<&kernel32.GetFileSiz>;\GetFileSize 004D14B7.8985E4FEFFFFmovdwordptr[ebp-11C],eax;打开成功获取文件大小 004D14BD.8B85E4FEFFFFmoveax,dwordptr[ebp-11C] 004D14C3.50pusheax;将大小值保存 004D14C4.E819442800call007558E2;分配内存 004D14C9.83C404addesp,4 004D14CC.8985C8F6FFFFmovdwordptr[ebp-938],eax;EAX=分配地址 004D14D2.8B8DC8F6FFFFmovecx,dwordptr[ebp-938] 004D14D8.898DDCFEFFFFmovdwordptr[ebp-124],ecx 004D14DE.6A00push0;/pOverlapped=NULL 004D14E0.8D55F0leaedx,dwordptr[ebp-10];| 004D14E3.52pushedx;|pBytesRead 004D14E4.8B85E4FEFFFFmoveax,dwordptr[ebp-11C];| 004D14EA.50pusheax;|BytesToRead 004D14EB.8B8DDCFEFFFFmovecx,dwordptr[ebp-124];| 004D14F1.51pushecx;|Buffer 004D14F2.8B95D8FEFFFFmovedx,dwordptr[ebp-128];| 004D14F8.52pushedx;|hFile 004D14F9.FF1580617700calldwordptr[<&kernel32.ReadFile>];\ReadFile读取整个原始文件 004D14FF.8B85D8FEFFFFmoveax,dwordptr[ebp-128] 004D1505.50pusheax;/hObject 004D1506.FF156C617700calldwordptr[<&kernel32.CloseHandl>;\CloseHandle关闭文件 004D150C.66:8B4D08movcx,wordptr[ebp+8] 004D1510.51pushecx;/Arg3;最开始协议加密中的034E 004D1511.8B95E4FEFFFFmovedx,dwordptr[ebp-11C];| 004D1517.52pushedx;|Arg2;文件长度 004D1518.8B85DCFEFFFFmoveax,dwordptr[ebp-124];| 004D151E.50pusheax;|Arg1;文件地址 004D151F.E85DFDFFFFcall004D1281;\UnpackMa.004D1281处理读取数据 //---------------------------------------------------------------------------------------------------- 这里需要注意的是,我这里发现不是对部分做加密运算,而是把整个程序做加密运算,最后为一个DWORD值,这个值只要代码被修改就会变,服务端那边就通不过,主动断开.我想除非不对原程序做任何的更改,否则,根本就通不过校验,如果,不做任何修改,那么就不能脱壳,如果不脱壳,那我们就等于什么也没做,如果校验一部分代码也好办,现在是校验全部代码,我真没想到9C就这么整人,连个余地都没给....... 只要这里加密的数字和服务端那里的对不上就断开,这个就是第二次校验为什么会穿衣服断线的原因. 我想菜鸟们听了我的解释也明白了吧! //---------------------------------------------------------------------------------------------------- 004D1524.83C40Caddesp,0C 004D1527.8945ECmovdwordptr[ebp-14],eax 004D152A.8B8DDCFEFFFFmovecx,dwordptr[ebp-124] 004D1530.898DC4F6FFFFmovdwordptr[ebp-93C],ecx 004D1536.8B95C4F6FFFFmovedx,dwordptr[ebp-93C] 004D153C.52pushedx 004D153D.E84E742700call00748990;释放堆载....:( 004D1542.83C404addesp,4 004D1545.8B45ECmoveax,dwordptr[ebp-14] 004D1548>8B4DF4movecx,dwordptr[ebp-C] 004D154B.64:890D00000>movdwordptrfs:[0],ecx 004D1552.5Fpopedi 004D1553.5Epopesi 004D1554.5Bpopebx 004D1555.8BE5movesp,ebp 004D1557.5Dpopebp 004D1558.C3retn