依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

【好消息,好消息,好消息】VIP会员可以发表文章赚积分啦 !
查看: 30|回复: 0

一个编写传奇封外挂(反外挂)系统的完成过程 - 游戏客户端编写加壳免误报篇

[复制链接] 主动推送

2636

主题

2645

帖子

3377

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3377
发表于 昨天 19:42 | 显示全部楼层 |阅读模式
一个编写传奇封外挂(反外挂)系统的完成过程 - 游戏客户端编写加壳免误报篇
什么是免杀、免误报。现在的杀毒软件因为鉴别能力的原因和偷懒干脆一棍子打死等因素造成我们大多正常程序都被误认为是病毒木马,一下载到用户电脑就直接报毒甚至直接删除。现在的杀毒软件普遍存在这种偷懒行为,以某六零程序最为严重,只要你的程序调用了内存、进程、网络和文件相关方面的API,达到一定数量就会报毒,就算没有调用,只要在导入表里面存在也会报毒。当然也包括一些代码撞衫病毒库的,代码量越大撞衫的几率就越大。有时候VS默认生成的hellow word都会报毒。至于加了知名加密壳的程序也是必报的。

     报毒会对我们的产品造成极大的困扰,你不可能告诉每一个用户说这是误报需要手动加入白名单,用户也不一定完全都信任我们。虽然对于误报程序,杀毒软件方面也提供了途径上传给他们分析后好加入他们的白名单系统,但是毕竟很麻烦。我早些年也申请过,没通过,记得是有公司规模资质要求,还需要有什么电信增值许可(具体名字忘了)之类的,类似于文网文之类的证书,总之很麻烦。更何况上传也不止一家,每次更新都得来一遍,有多少家杀软你就得上传多少家去,有的可能会根据你的软件情况收费,某六零好像就有收费的,价格还不便宜。

   免杀,免误报现在网络上已经形成一套知识体系了,有兴趣的朋友可以搜索了解。免误杀基本原理是通过修改代码的特征,使撞衫病毒木马的代码改变特征。对于“危险API”的调用可以改成隐式调用方式,使API不出现在导入表中。隐式调用就是 LoadLibray + GetProcAddress ,要注意这里的调用最好直勾勾地调用、也不要有初始化的字符串,否则同样报毒。

   本文介绍的免杀免误报方式是通过编写自定义壳的方式来实现的。本壳是自己设计的,关于壳的知识可以购买《windows pe权威指南》 和看雪学院的 《加密和解密》书籍深入学习。

   本项目的壳是将整个欲处理的游戏程序全部加密的方式处理的、并非普通壳只加密代码段,下面是介绍本项目壳的开发过程:

第一步、跳转  : 产生一个异常使程序跳转到壳代码。这里不能使用 jmp跳转、入口点使用jmp 某六零必报毒。所以不能使用jmp 。

  1. 00B8F0DC | 68 09D51C01              | push 游戏.11CD509                               |
  2. 00B8F0E1 | 33C0                     | xor eax,eax                                        |
  3. 00B8F0E3 | 64:FF30                  | push dword ptr fs:[eax]                            |
  4. 00B8F0E6 | 64:8920                  | mov dword ptr fs:[eax],esp                         |
  5. 00B8F0E9 | 3E:8900                  | mov dword ptr ds:[eax],eax                         |    |
复制代码
第二步、反沙箱:因为杀毒软件沙箱一般硬件配置比较低、所以运用这个特点、限制运行系统硬件的CPU,内存,以及鼠标键盘外设等硬件条件来阻止程序在沙箱运行。因为正常用户都是鼠标双击或者键盘回车来启动程序的,所以几秒内一定有鼠标键盘事件。还可以设计进程数量限制、SetErrorMode模式等反沙箱方案,下面只贴出部分演示代码,一旦代码全贴出来很快就会变得无效。想了解更多关于反沙箱知识请百度搜索学些。这些知识也是动态变化的,一旦被贴到网上,很快就会失效。最好自己多揣摩、猜测去测试、发现。其中值得一提的是:多运用多线程技术对反沙箱方面比较好使、因为动沙箱检测多线程方面本身难度就比较大。
  1.    //通过PPEB 获取CPU数量 ,限制最少2CPU
  2.     DWORD* pdword = &ppack_info->anti_cpu_count;
  3.     _asm
  4.     {
  5.         mov eax,0x12
  6.         mov ebx,0x20
  7.         mov ecx,0x60
  8.         mov eax,dword ptr fs:[eax + 0x06]
  9.         add eax,0x10
  10.         mov eax,dword ptr ds:[eax + ebx ]
  11.         add eax,0x04
  12.         mov eax,dword ptr ds:[eax + ecx ]
  13.         push eax
  14.         mov eax,dword ptr [pdword]
  15.         pop dword ptr[eax]
  16.     }
  17.     if (*pdword < 0x02)
  18.     {
  19.         ((void(*)(PACK_INFO*))(ppack_info->dwFN_doexit_normal - ppack_info->dwFN_offset))(ppack_info);
  20.         return;
  21.     }

  22. //物理内存容量 ,最少1G内存
  23.     MEMORYSTATUSEX memInfo;
  24.     memInfo.dwLength = sizeof(MEMORYSTATUSEX);
  25.     ((type_GlobalMemoryStatusEx)((DWORD)ppack_info->fnGlobalMemoryStatusEx - ppack_info->dwFN_offset))(&memInfo);
  26.     //*pdword = memInfo.ullTotalPhys / 1024 / 1024;
  27.     DWORD dwHi = *((DWORD*)&memInfo.ullTotalPhys + 1);//取高32位
  28.     DWORD dwLo = *((DWORD*)&memInfo.ullTotalPhys );   //低32位

  29.     if (dwHi == 0 && dwLo < 999 * 1024*1024) //小于 999M
  30.     {
  31.         ((void(*)(PACK_INFO*))(ppack_info->dwFN_doexit_normal - ppack_info->dwFN_offset))(ppack_info);
  32.         return;
  33.     }


  34. //开机时间 > 1 分钟
  35.     ppack_info->fnGetTickCount = (type_GetTickCount)((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::GetProcAddress, ppack_info->hmodKernel32, (DWORD)ppack_info->name_GetTickCount_str1, ppack_info->name_GetTickCount_fixidx, (DWORD)ppack_info->name_GetTickCount_str2);
  36.     if (((type_GetTickCount)((DWORD)ppack_info->fnGetTickCount - ppack_info->dwFN_offset))() < 1 * 60 * 1000)
  37.     {
  38.         ((void(*)(PACK_INFO*))(ppack_info->dwFN_doexit_normal - ppack_info->dwFN_offset))(ppack_info);
  39.         return;
  40.     }


  41. //校验:必须存在鼠标
  42.     type_GetSystemMetrics fnGetSystemMetrics = (type_GetSystemMetrics)((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::GetProcAddress, ppack_info->hmodUser32, (DWORD)ppack_info->name_GetSystemMetrics_str1, ppack_info->name_GetSystemMetrics_fixidx, (DWORD)ppack_info->name_GetSystemMetrics_str2);
  43.     if (!((type_GetSystemMetrics)((DWORD)fnGetSystemMetrics - ppack_info->dwFN_offset))(SM_MOUSEPRESENT))
  44.     {
  45.         ((void(*)(PACK_INFO*))(ppack_info->dwFN_doexit_normal - ppack_info->dwFN_offset))(ppack_info);
  46.         return;
  47.     }

  48. //校验:GetLastInputInfo 上次输入时间间隔 < 8s
  49.     type_GetLastInputInfo  fnGetLastInputInfo = (type_GetLastInputInfo)((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::GetProcAddress, ppack_info->hmodUser32, (DWORD)ppack_info->name_GetLastInputInfo_str1, ppack_info->name_GetLastInputInfo_fixidx, (DWORD)ppack_info->name_GetLastInputInfo_str2);
  50.     LASTINPUTINFO* ppi = (LASTINPUTINFO*)ppack_info->mov_ToAddr;
  51.     ppi->cbSize = sizeof(LASTINPUTINFO);
  52.     ((type_GetLastInputInfo)((DWORD)fnGetLastInputInfo - ppack_info->dwFN_offset))(ppi);
  53.     if (((type_GetTickCount)((DWORD)ppack_info->fnGetTickCount - ppack_info->dwFN_offset))() - ppi->dwTime > 8 * 1000)
  54.     {
  55.         ((void(*)(PACK_INFO*))(ppack_info->dwFN_doexit_normal - ppack_info->dwFN_offset))(ppack_info);
  56.         return;
  57.     }
复制代码
第三步、动态获取API,第二步中部分API也是本步骤获取的。本壳的导入表里面已经存在LoadLibraryA和GetProcAddress两个函数,其它函数全靠这里动态加载导入。要注意导入函数时不能直勾勾地调用、也不能直接写DLL名称和导入函数,得做一些加密混淆处理。
  1. ppack_info->fnLoadlibraryA = (type_LoadLibraryA)(*(DWORD*)ppack_info->fnLoadlibraryA + ppack_info->dwFN_offset);
  2.     ppack_info->fnGetProcAddress = (type_GetProcAddress)(*(DWORD*)ppack_info->fnGetProcAddress + ppack_info->dwFN_offset);
  3.     ppack_info->hmodKernel32 = ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::LoadLibraryA, (DWORD)ppack_info->name_Kernel32_str1, ppack_info->name_Kernel32_fixidx, (DWORD)ppack_info->name_Kernel32_str2, NULL);
  4.     ppack_info->fnSleep = (type_Sleep)((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::GetProcAddress, ppack_info->hmodKernel32, (DWORD)ppack_info->name_Sleep_str1, ppack_info->name_Sleep_fixidx, (DWORD)ppack_info->name_Sleep_str2);
  5.     ppack_info->fnGlobalMemoryStatusEx = (type_GlobalMemoryStatusEx)((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::GetProcAddress, ppack_info->hmodKernel32, (DWORD)ppack_info->name_GlobalMemoryStatusEx_str1, ppack_info->name_GlobalMemoryStatusEx_fixidx, (DWORD)ppack_info->name_GlobalMemoryStatusEx_str2);
  6.     ppack_info->fnExitProcess = (type_ExitProcess)((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::GetProcAddress, ppack_info->hmodKernel32, (DWORD)ppack_info->name_ExitProcess_str1, ppack_info->name_ExitProcess_fixidx, (DWORD)ppack_info->name_ExitProcess_str2);
  7.     ppack_info->hmodUser32 = ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info, EComFunType::LoadLibraryA, (DWORD)ppack_info->name_user32_str1, ppack_info->name_user32_fixidx, (DWORD)ppack_info->name_user32_str2, NULL);
复制代码
第四步、解密:解密原始的PE文件数据,解密算法可以自行设计即可,一般就是亦或、加减、位移这些简单的、运算速度快的算法组合起来就可以了。

第五步、修复IAT: 关于修复IAT的知识网上比较多,可以具体搜索来看,加壳基本都会涉及到修复IAT ,下面贴出实例代码,这些关于PE文件格式的代码如果正常编写一般都会匹配成病毒,木马,所以加了干扰变形调用。

  1. void fixiat(PACK_INFO* ppack_info)
  2. {
  3.     int i;
  4.     DWORD dwoffset = ppack_info->unuse_word1 ;
  5.     IMAGE_DOS_HEADER* pdoshdr = (IMAGE_DOS_HEADER*)(ppack_info->dwShell_BaseAddress + dwoffset);
  6.     ppack_info->unuse_word1++;
  7.     IMAGE_NT_HEADERS* pnthdr = (IMAGE_NT_HEADERS*)(((char*)pdoshdr - dwoffset) + ((IMAGE_DOS_HEADER*)((char*)pdoshdr - dwoffset))->e_lfanew + dwoffset);
  8.     ppack_info->unuse_byte1 -= ppack_info->unuse_word1 % 0xff;
  9.     IMAGE_IMPORT_DESCRIPTOR* pImportDes = (IMAGE_IMPORT_DESCRIPTOR*)(ppack_info->dwShell_BaseAddress + ((IMAGE_NT_HEADERS*)((char*)pnthdr - dwoffset))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + dwoffset);
  10.     ppack_info->unuse_word1 ^= ppack_info->unuse_word1;
  11.     while ( ((IMAGE_IMPORT_DESCRIPTOR*)((char*)pImportDes - dwoffset))->FirstThunk)
  12.     {
  13.         char* pDllName = (char*)(ppack_info->dwShell_BaseAddress + ((IMAGE_IMPORT_DESCRIPTOR*)((char*)pImportDes - dwoffset))->Name);
  14.         DWORD hMod =  ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info
  15.             , EComFunType::LoadLibraryA
  16.             , (DWORD)pDllName
  17.             , 0xff
  18.             , NULL
  19.             , NULL) - ppack_info->dwFN_offset;
  20.         //抹去dll名称
  21.         for (i = 0; i < 0x64 && pDllName[i] != 0; i++)
  22.         {
  23.             pDllName[i] = 0xcc;
  24.         }

  25.         IMAGE_THUNK_DATA* pThunkINT = ((IMAGE_IMPORT_DESCRIPTOR*)((char*)pImportDes - dwoffset))->OriginalFirstThunk == NULL ?
  26.             NULL : (IMAGE_THUNK_DATA*)(ppack_info->dwShell_BaseAddress + ((IMAGE_IMPORT_DESCRIPTOR*)((char*)pImportDes - dwoffset))->OriginalFirstThunk);
  27.         ppack_info->unuse_byte1 += 0x02;
  28.         IMAGE_THUNK_DATA* pThunkIAT = (IMAGE_THUNK_DATA*)(ppack_info->dwShell_BaseAddress + ((IMAGE_IMPORT_DESCRIPTOR*)((char*)pImportDes - dwoffset))->FirstThunk);
  29.         ppack_info->unuse_dword1 += ppack_info->unuse_word1;
  30.         if (pThunkINT == NULL)
  31.         {
  32.             while (pThunkIAT->u1.Function)
  33.             {
  34.                 if (pThunkIAT->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
  35.                 {
  36.                     DWORD dwHit = pThunkIAT->u1.Ordinal & 0xFFFF;
  37.                     ppack_info->unuse_byte1++;
  38.                     pThunkIAT->u1.Function = ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info
  39.                         , EComFunType::GetProcAddress
  40.                         , hMod + ppack_info->dwFN_offset
  41.                         , (DWORD)(char*)(pThunkIAT->u1.Ordinal & 0xFFFF)
  42.                         , 0xff
  43.                         , NULL) - ppack_info->dwFN_offset;
  44.                 }
  45.                 else
  46.                 {
  47.                     IMAGE_IMPORT_BY_NAME* ImportName = (IMAGE_IMPORT_BY_NAME*)(ppack_info->dwShell_BaseAddress + pThunkIAT->u1.AddressOfData);
  48.                     ppack_info->unuse_dword1 ^= ppack_info->unuse_byte1 + 0xcc;
  49.                     pThunkIAT->u1.Function = ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info
  50.                         , EComFunType::GetProcAddress
  51.                         , hMod + ppack_info->dwFN_offset
  52.                         , (DWORD)(char*)ImportName->Name
  53.                         , 0xff
  54.                         , NULL) - ppack_info->dwFN_offset;
  55.                     //抹去函数名
  56.                     for (i = 0; i < 0x64 && ImportName->Name[i] != 0; i++)
  57.                     {
  58.                         ImportName->Name[i] = 0xcc;
  59.                     }
  60.                 }
  61.                 pThunkIAT++;
  62.             }
  63.         }
  64.         else
  65.         {
  66.             while (pThunkINT->u1.Function)
  67.             {
  68.                 if (pThunkINT->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
  69.                 {
  70.                     DWORD dwHit = pThunkINT->u1.Ordinal & 0xFFFF;
  71.                     ppack_info->unuse_byte1++;
  72.                     pThunkIAT->u1.Function = ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info
  73.                         , EComFunType::GetProcAddress
  74.                         , hMod + ppack_info->dwFN_offset
  75.                         , (DWORD)(char*)(pThunkINT->u1.Ordinal & 0xFFFF)
  76.                         , 0xff
  77.                         , NULL) - ppack_info->dwFN_offset;
  78.                 }
  79.                 else
  80.                 {
  81.                     IMAGE_IMPORT_BY_NAME* ImportName = (IMAGE_IMPORT_BY_NAME*)(ppack_info->dwShell_BaseAddress + pThunkINT->u1.AddressOfData);
  82.                     ppack_info->unuse_dword1 ^= ppack_info->unuse_byte1 + 0xcc;
  83.                     pThunkIAT->u1.Function = ((DWORD(*)(PACK_INFO*, EComFunType, DWORD, DWORD, DWORD, DWORD))(ppack_info->dwFN_commfunction - ppack_info->dwFN_offset))(ppack_info
  84.                         , EComFunType::GetProcAddress
  85.                         , hMod + ppack_info->dwFN_offset
  86.                         , (DWORD)(char*)ImportName->Name
  87.                         , 0xff
  88.                         , NULL) - ppack_info->dwFN_offset;
  89.                     //抹去函数名
  90.                     for (i = 0; i < 0x64 && ImportName->Name[i] != 0; i++)
  91.                     {
  92.                         ImportName->Name[i] = 0xcc;
  93.                     }
  94.                 }
  95.                 pThunkIAT++;
  96.                 pThunkINT++;
  97.             }
  98.         }
  99.         pImportDes++;
  100.     }

  101.     ((void(*)(DWORD, PACK_INFO*))(ppack_info->dwFN_call_fnAddr - ppack_info->dwFN_offset))(ppack_info->dwFN_fixreloc, ppack_info);
  102. }
复制代码
第六步、修复重定向: EXE程序一般不存在重定向数据,并且本项目的壳取消了EXE的随机基址,不需要修复。

第七步、修复tls : 严格来说这个不是第七步,而是在生成加壳后程序的时候生成的,有些程序有tls,而本项目的壳是属于完全加密壳,需要手动生成tls节和相关数据,以及调用 tls回调函数。tls相关知识点在网上也很多,请自行搜索学习。

第八步、跳转回解密以后的 oep



完成

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员6折;永久VIP4折
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|小黑屋|依星资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2025-2-5 17:47

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

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