首页
社区
课程
招聘
[原创]ring3反作弊篇——基于EBP遍历调用栈及模块名
发表于: 2015-7-27 11:54 20860

[原创]ring3反作弊篇——基于EBP遍历调用栈及模块名

2015-7-27 11:54
20860
申请转正贴:【原创】ring3反作弊篇——基于EBP遍历调用栈及模块名

之前自己做的一款老游戏的基于R3入门级的反作弊代码中的片段,仅供学习参考~~

通杀Win XP/7/8,哪位兄弟装了WIN10麻烦测试一下谢谢!

感谢NV、漏网之鱼~~



//
// CallStackList.cpp : 定义控制台应用程序的入口点。
//
// thanks for NV.
//

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>

#include "EasyDetour.h"

#include <TlHelp32.h>
#include <Psapi.h>
#pragma comment(lib,"psapi.lib")

typedef int (WINAPI *fnMessageBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);

fnMessageBoxA    pMessageBoxA = NULL;

DWORD Functiion(DWORD x, DWORD y);


//
// 提取函数
//
BOOL TiQuan()
{
    HANDLE    hToken;
    BOOL    fOk = FALSE;

    if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
    {
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount=1;
        if(!LookupPrivilegeValueA(NULL,"SeDebugPrivilege",&tp.Privileges[0].Luid))
            Sleep(1);

        tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
        if(!AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL))
            Sleep(1);

        fOk = (GetLastError() == ERROR_SUCCESS);
        CloseHandle(hToken);
    }

    return fOk;
}

//
// 获取PE文件大小
//
DWORD GetPEImageSize(HMODULE hModule)
{
    PBYTE pInfo = (PBYTE)hModule;
    PIMAGE_DOS_HEADER pImgDos = (PIMAGE_DOS_HEADER)pInfo;
    PIMAGE_NT_HEADERS pImgNt;
    if(pImgDos->e_magic==IMAGE_DOS_SIGNATURE)
    {
        pImgNt = (PIMAGE_NT_HEADERS)&pInfo[pImgDos->e_lfanew];
        if(pImgNt)
        {
            if(pImgNt->Signature==IMAGE_NT_SIGNATURE)
            {
                return pImgNt->OptionalHeader.SizeOfImage;
            }
        }
    }
    return NULL;
}

//
// Hook MessageBox for test
//
BOOL WINAPI GetCheatModuleByEBP(DWORD nEBP,char *pszPath,int nLen)
{
    TiQuan();
    if(nEBP == 0)
        return FALSE;

    DWORD    nPEB = nEBP;
    BOOL    bFound = FALSE;
    HMODULE hMods[1024] = {0};
    DWORD    cbNeeded = 0;
    char    szModName[MAX_PATH];

    HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
    //IsWow64Process(hProcess, &Wow64Process); //判断是32位还是64位进程
    EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded);

    nPEB = nEBP;
    for (UINT i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
    {
        GetModuleFileNameExA(hProcess, hMods[i], szModName, _countof(szModName));
        if(hMods[i])
        {
            if(nPEB >= (DWORD)hMods[i] && (nPEB <= ((DWORD)hMods[i] + GetPEImageSize(hMods[i]))))
            {
                memset(pszPath,0x00,nLen);
                wsprintfA(pszPath,"%s",szModName);
                bFound = TRUE;
                break;
            }
        }
    }

    CloseHandle(hProcess);
    return bFound;
}

//
// Hook MessageBox for test
//
int WINAPI newMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType)
{
    Functiion(0,0);
    return pMessageBoxA(hWnd,lpText,lpCaption,uType);
}

//
// 回调函数
//
DWORD BackCall(DWORD Address)
{
    char    szDllPath[MAX_PATH] = {0x00};
    if(GetCheatModuleByEBP(Address,szDllPath,MAX_PATH))
        printf("检测地址:0x%08x  %s\n", Address, szDllPath);
    else
        printf("检测地址:0x%08x  未知模块\n", Address, szDllPath);

    //
    // 在这里添加白名单 黑名单匹配的代码 (黑名单返回1 白名单返回0)
    //

    return 0;
}

//
// 检测呼叫者调用连
//
BOOL __declspec(naked)  Check(void)
{
    __asm push ebp;
    __asm mov ebp, esp;
    __asm sub esp, 0x8;

    __asm push edi;
    __asm push ecx;

    //
    // 查询次数
    __asm mov ecx, dword ptr[ebp + 0x8];

    //
    // 设置堆栈指针
    __asm mov edi, dword ptr[ebp];

__Loop:

    //
    // 保存当前堆栈的返回地址 也就是呼叫这个函数的上一层函数的内存空间
    __asm mov eax, dword ptr[edi + 0x4];

    //
    // 调用匹配规则函数
    __asm push eax;
    __asm call dword ptr[ebp + 0xc];
    __asm add esp, 0x4;
    
    //
    // 获取上一个堆栈的指针
    __asm mov edi, dword ptr[edi];

    __asm cmp eax, 0x1;
    __asm je __Out;

    //
    // 如果已经为空了 则直接退出
    
    __asm cmp edi, 0x0;
    __asm je __Out;

    __asm loop __Loop;

__Out:

    __asm pop ecx;
    __asm pop edi;
    __asm add esp, 0x8;
    __asm pop ebp;
    __asm ret;
}



//
// 测试函数
//
DWORD Functiion(DWORD x, DWORD y)
{

    //
    // 设置回调函数
    __asm mov edx, dword ptr[BackCall];
    __asm push edx;

    //
    // 设置最大检测深度
    __asm push 0x50;
    __asm call dword ptr[Check];
    __asm add esp, 0x8;

    return x + y;
}

// 这里是测试函数
DWORD Function(DWORD x,DWORD y)
{
    //x += y;
    __asm mov edx,dword ptr[x];
    __asm add edx,0x10;                // 深度检测10个
    __asm mov dword ptr[x],edx;
    return x;
}

int MsgBox()
{
    return MessageBoxA(NULL,"Hello World by Koma !","Test",MB_OK);
}

int _tmain(int argc, _TCHAR* argv[])
{
    pMessageBoxA = MessageBoxA;
    DetourHook((void**)&pMessageBoxA,newMessageBoxA);
    MsgBox();
    while(getchar() != 'a')
        Sleep(0);
    DetourUnHook((void**)&pMessageBoxA,newMessageBoxA);
    return 0;
}



 //
// 暴力搜索R3断链隐藏模块
//
BOOL WINAPI GetCheatModuleByEBP(const DWORD nEBP,const char *pszPath,const int nLen,DWORD & nCRC,DWORD & nFileSize);
{
 DWORD ModuleBase = 0;
 DWORD ModuleSize = 0;
 WCHAR szModuleName[MAX_PATH] = {0};
 WCHAR szPathName[MAX_PATH] = {0};
 HANDLE hProcess = INVALID_HANDLE_VALUE;
 
 memset(pszPath,0x00,nLen);
 nCRC = nFileSize = 0;
 if(nProcessID == 0) 
  return FALSE;
  hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
 if(!hProcess) 
  return FALSE;
  for(int i=-1, BaseAddress=0; BaseAddress<0x80000000; BaseAddress+=0x1000)
 {
  MEMORY_BASIC_INFORMATION mbi;
  if(NT_SUCCESS(ZwQueryVirtualMemory(hProcess, (PVOID)BaseAddress, MemoryBasicInformation, &mbi, sizeof(mbi), 0)))
  {
   if(ModuleBase && ModuleBase != (DWORD)mbi.AllocationBase && ModuleSize)  
    ModuleBase = ModuleSize = 0;
    if(mbi.Type != MEM_IMAGE)
    continue;
    ModuleBase = (DWORD)mbi.AllocationBase;
   if(ModuleBase && mbi.BaseAddress == (PVOID)BaseAddress)
    ModuleSize = (DWORD)mbi.BaseAddress + mbi.RegionSize - (DWORD)mbi.AllocationBase;
    if(mbi.AllocationBase != (PVOID)BaseAddress)
    continue;
   
   if(ModuleBase == 0 || ModuleSize == 0)
    continue;
    if((ModuleBase <= nEBP) && (ModuleBase + ModuleSize) >= nEBP)
   {
    ULONG  NameSize = 0x100;
    LPVOID  pBuffer = malloc(NameSize);
    if(!pBuffer) 
    {
     ASSERT(FALSE);
     continue;
    }
    
    NTSTATUS status = ZwQueryVirtualMemory(hProcess, (PVOID)mbi.AllocationBase, MemorySectionName, pBuffer, NameSize, &NameSize);
    if(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_BUFFER_OVERFLOW)
    {
     free(pBuffer);
     pBuffer = malloc(NameSize);
     status = ZwQueryVirtualMemory(hProcess, (PVOID)mbi.AllocationBase, MemorySectionName, pBuffer, NameSize, 0);
    }
    
    if(!pBuffer) continue;
     if(!NT_SUCCESS(status))
    {
     free(pBuffer);
     continue;
    }
     POBJECT_NAME_INFORMATION SectionName = (POBJECT_NAME_INFORMATION)pBuffer;
    ANSI_STRING  tmpAS;
     PUNICODE_STRING usSectionName; 
    char   *pTemp = NULL;
     usSectionName = (PUNICODE_STRING)pBuffer;
    _wcsnicmp(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR));
    wcsncpy(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR) );
    szModuleName[usSectionName->Length / sizeof(WCHAR)] = UNICODE_NULL;
    DeviceName2PathName(szPathName, szModuleName);
    pTemp = (char *)malloc(sizeof(char)*(2*wcslen(szPathName)+1));
    memset(pTemp , 0 , 2 * wcslen(szPathName)+1);
    W2C(pTemp,szPathName,2 * wcslen(szPathName)+1) ;
     memcpy(pszPath,pTemp,strlen(pTemp));
    delete [] pTemp;
    pTemp = NULL;
     nFileSize = mbi.RegionSize;
    nCRC   = GetFileCRC(pszPath);
     free(pBuffer);
    pBuffer = NULL;
    CloseHandle(hProcess);
    return TRUE;
   }
  } else if(ModuleBase && ModuleSize)
  {
   ModuleBase = ModuleSize = 0;
  }
 }
  CloseHandle(hProcess);
 return FALSE;
}

 


vs2010工程源码下载

http://www.rayfile.com/zh-cn/files/a1dcbf4c-d15e-11e4-ac13-0015c55db73d/

博客地址:

http://blog.csdn.net/wangningyu/article/details/44569803

[课程]FART 脱壳王!加量不加价!FART作者讲授!

收藏
免费 3
支持
分享
最新回复 (41)
雪    币: 9941
活跃值: (2143)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
楼主能6年更新csdn的blog 表示崇拜
好多帖子如果能发到看雪,更好
2015-8-1 23:00
0
雪    币: 9941
活跃值: (2143)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
_asm 这么长的情况下,为何不用 { }  ? 这样就不用打那么多 _asm 了
2015-8-1 23:20
0
雪    币: 29
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
有点意思
2015-8-2 00:48
0
雪    币: 8865
活跃值: (2379)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
5
要是能用RtlCaptureStackBackTrace获取EBP模式的调用堆栈+ZwQueryVirtualMemory查地址到模块,就更漂亮了——纯建议~
2015-8-2 02:31
0
雪    币: 407
活跃值: (125)
能力值: ( LV13,RANK:280 )
在线值:
发帖
回帖
粉丝
6
内存加载dll,无视了
2015-8-3 10:06
0
雪    币: 96
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
看下,谢谢楼主的分享
2015-8-3 10:46
0
雪    币: 369
活跃值: (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
6年时间,见证了从Hello World到现在的,能坚持下来都不容易
2015-8-3 15:20
0
雪    币: 23
活跃值: (1321)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
LZ这个是调用CALL堆栈检测,跟你是不是内存加载的DLL 没有半毛关系,一样检测
2015-8-3 23:48
0
雪    币: 407
活跃值: (125)
能力值: ( LV13,RANK:280 )
在线值:
发帖
回帖
粉丝
10
内存加载无模块,你怎么检测
2015-8-5 14:44
0
雪    币: 1631
活跃值: (3810)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
人家call 堆栈检测设置检测层数,你内存有毛用...
2015-8-6 00:17
0
雪    币: 1631
活跃值: (3810)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
感觉除非不用API,否则都能破
2015-8-6 00:25
0
雪    币: 1631
活跃值: (3810)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
我建议,不要用任何系统API,才能做到隐秘检测
2015-8-6 00:30
0
雪    币: 43
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
源码工程文件无法下载啊,能换一个上传空间吗?
2015-8-7 08:41
0
雪    币: 369
活跃值: (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
没错,目的是检查CALL的合法性,跟内存加载隐藏模块没多大关系,大多游戏都会根据调用栈来检测是不是合法调用,当然R3的话能力有限,暂时用的思路是

1、检查是否为合法CALL

     inline hook opengl32,然后根据调用栈检测是不是游戏正常调用(比如主进程、XX.DLL),排除这些的话,任何CALL都是非法调用,此时根据EBP来获取作弊器的模块名,取不到的则ZwQueryVirtualMemory暴力扫描内存模块(这里for 4GB可能会导致爆卡慎用!!!)

2、保护自己

    这里定时检查自己的HOOK是不是被恢复或反操了,比如他强制恢复HOOK,或者在我的DLL模块上接着HOOK就被反操了,所以需要定时器去检查,另外模块也做内存校验,一旦内存HASH不对也有可能是被其他方法给人撸了~~

   大致思路就是这样的了,如有其他建议,欢迎补充,谢谢!
2015-8-7 11:59
0
雪    币: 369
活跃值: (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
内存加载只是手段,你始逃不过调用栈检查的话就没有多大意义!

这个代码是检查调用栈的合法性,比如一个游戏正常是这样的

main.exe       ->>  // 主进程
engine.dll      ->>  // 游戏引擎
opengl32.dll  ->>  // 这是GL核心模块
.....

你只需要检查调用栈的地址是不是在这三个模块里,你单独内存加载cheat.dll是不会在这三个地址空间,所以就检查出是非法调用视为作弊了
2015-8-7 12:07
0
雪    币: 369
活跃值: (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
下载地址已补
链接: http://pan.baidu.com/s/1ntN5yel
密码: fn5g
2015-8-7 12:08
0
雪    币: 256
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
远程注入shellcode可以检测?
2015-8-7 16:56
0
雪    币: 8865
活跃值: (2379)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
19
其实就是逼着人玩一些什么ret2lib啊,rop啊~
2015-8-8 23:12
0
雪    币: 438
活跃值: (228)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
20
LZ,源码链接打不开,换成百度云网盘把,或直接上传到论坛。
2015-8-9 01:48
0
雪    币: 407
活跃值: (125)
能力值: ( LV13,RANK:280 )
在线值:
发帖
回帖
粉丝
21
过你这种检测方法多的是,在你main.exe随便找个跳板就pass了
2015-8-12 16:36
0
雪    币: 407
活跃值: (125)
能力值: ( LV13,RANK:280 )
在线值:
发帖
回帖
粉丝
22
你能检测多少层,层数越多,误判率也越高
2015-8-12 16:47
0
雪    币: 773
活跃值: (442)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
23
真奇怪哈,楼主存那网盘啊,飞速网,我10次有9次半下不下来,你自己试过能下载么,我就是好奇没别的意思
2015-8-13 11:54
0
雪    币: 6400
活跃值: (4160)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
24
EXE,DLL里总有空的地方吧,要调的东西写进去直接调,STACK就是主模块的.....
2015-8-13 12:02
0
雪    币: 1631
活跃值: (3810)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
其实所有系统dll,飞系统dll,都有大量空地可以放代码,这种检查,怎么说只能说以一种思路,真用起来效果难讲
2015-8-13 13:39
0
游客
登录 | 注册 方可回帖
返回
//