邪恶八进制信息安全团队技术讨论组's Archiver

magicbear 2007-7-20 23:12

[原创]简易网络程序反HOOK技术

文章作者:MagicBear QQ130012321
信息来源:邪恶八进制信息安全团队([url]www.eviloctal.com[/url])

最近在搞一个网络程序,出后不久被破解,于是弄了一些反HOOK的技术
经测试WPE,IPSniffer,XSniffer等等著名Sniffer均告失效

检测内容:
1. 子程序地址是否在DLL空间内
2. 检查本机是否有运行RAW SOCKET类Sniffer
3. 检查GetFuncAddress返回地址是否与从IAT表获取地址相同
4. 检查地址开头是否有JMP
5. 对程序地址进行CRC校验,防止修改主程序及DLL内地址而使保护失效

P.S 需要主程序配合校验
主要是主程序调用,然后同时在DLL内备份一份并做CRC校验备份,主程序在每次发送前都调用DLL校验一次,对网络用量大的可能-0-...........欢迎丟鸡旦

[code]
// LvKrnl.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include <winsock2.h>

#define MAX_PACK_LEN 65535
#define MAX_HOSTNAME_LAN 255
#pragma comment (lib , "ws2_32.lib")

DWORD GetFunctionAddress( HMODULE phModule,char* pProcName )
{
    if (!phModule)
        return    0;
    PIMAGE_DOS_HEADER    pimDH    =    (PIMAGE_DOS_HEADER)phModule;
    PIMAGE_NT_HEADERS    pimNH    =    (PIMAGE_NT_HEADERS)((char*)phModule+pimDH->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY    pimED    =    (PIMAGE_EXPORT_DIRECTORY)((DWORD)phModule+pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    DWORD    pExportSize    =    pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
    DWORD    pResult    =    0;

   
    if ((DWORD)pProcName < 0x10000)
    {
        if ((DWORD)pProcName    >=    pimED->NumberOfFunctions+pimED->Base || (DWORD)pProcName < pimED->Base)
            return    0;
        pResult    =    (DWORD)phModule+((DWORD*)((DWORD)phModule+pimED->AddressOfFunctions))[(DWORD)pProcName-pimED->Base];
    }else
    {
        DWORD*    pAddressOfNames    =    (DWORD*)((DWORD)phModule+pimED->AddressOfNames);
        for (int i=0;i<pimED->NumberOfNames;i++)
        {
            char*    pExportName    =    (char*)(pAddressOfNames[i]+(DWORD)phModule);
            if (strcmp(pProcName,pExportName) == 0)
            {
                WORD*    pAddressOfNameOrdinals    =    (WORD*)((DWORD)phModule+pimED->AddressOfNameOrdinals);
                pResult        =    (DWORD)phModule+((DWORD*)((DWORD)phModule+pimED->AddressOfFunctions))[pAddressOfNameOrdinals[i]];
                break;
            }
        }
    }
    if (pResult != 0 && pResult    >=    (DWORD)pimED    &&    pResult <    (DWORD)pimED+pExportSize)
    {
        char*    pDirectStr    =    (char*)pResult;
        bool    pstrok    =    false;
        while (*pDirectStr)
        {
            if (*pDirectStr == &#39;.&#39;)
            {
                pstrok    =    true;
                break;
            }
            pDirectStr++;
        }
        if (!pstrok)
            return    0;
        char    pdllname[MAX_PATH];
        int        pnamelen    =    pDirectStr-(char*)pResult;
        if (pnamelen <= 0)
            return    0;
        memcpy(pdllname,(char*)pResult,pnamelen);
        pdllname[pnamelen] = 0;
        HMODULE    phexmodule    =    GetModuleHandle(pdllname);
        pResult    =    GetFunctionAddress(phexmodule,pDirectStr+1);
    }

    return pResult;
}

BOOL RawSnifferCheck()
{
  SOCKET SockRaw,Sock;
  WSADATA wsaData;
  int ret=0;
  struct sockaddr_in sAddr,addr;
  char RecvBuf[MAX_PACK_LEN];
  char FAR name[MAX_HOSTNAME_LAN];

  struct hostent FAR * pHostent;
  char *Buf;
  int settimeout=100;//这里我们设置了一秒钟超时
  WSAStartup(MAKEWORD(2,2),&wsaData);
  //建立一条RawSocket
  SockRaw=socket(AF_INET,SOCK_RAW,IPPROTO_IP);
  //再建立一条UDP
  Sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
  memset(&sAddr,0,sizeof(sAddr));
  memset(&addr,0,sizeof(addr));
  sAddr.sin_family=AF_INET;
  sAddr.sin_port=htons(35257);
  addr.sin_family=AF_INET;

  addr.sin_port=htons(35258);
  //把IP地址指向本机
  addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
  memset(RecvBuf,0, sizeof(RecvBuf));
  gethostname(name, MAX_HOSTNAME_LAN);
  pHostent=gethostbyname(name);

  //取得自己的IP地址
  memcpy(&sAddr.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length);

  //绑定一个本机的接收端口
  bind(SockRaw, (struct sockaddr *)&sAddr, sizeof(sAddr));

  //虚连接到本机的一个未打开的端口
  connect(Sock,(struct sockaddr *)&addr,sizeof(addr));
  Buf="1234567890!@#$%^&*";
  //设置超时
  setsockopt(SockRaw,SOL_SOCKET,SO_RCVTIMEO,(char *)&settimeout,sizeof(int));
  //向虚连接端口发送一个数据包
  send(Sock,Buf,strlen(Buf),0);
  //使用SockRaw尝试接收这个数据包
  ret=recv(SockRaw,RecvBuf,sizeof(RecvBuf),0);
  if(ret==SOCKET_ERROR || ret==0)
    return true;
  else
  {
    //进行ChkSum
    if(Buf=="1234567890!@#$%^&*")
      return false;
  }
  closesocket(Sock);
  closesocket(SockRaw);

  WSACleanup();
  return true;
}

#define MAIN_API  extern "C" __declspec(dllexport)

FARPROC pconnect;
FARPROC psend;
FARPROC precv;
HINSTANCE hProcModule;

DWORD dwConnect,dwSend,dwRecv;

const codesize=1048576;

DWORD GetCRC32(const BYTE *pbData, int nSize);

BOOL checkJMP(void *pFun)
{
  LPBYTE pByte = (LPBYTE)pFun;

  if(*pByte == 0xE9) //JMP CODE
  {
    return false;
  }
  if(((long)pFun < (long)hProcModule) || ((long)pFun > ((long)hProcModule + codesize)))
  {
    return false;
  }
  return true;
}

BOOL APIENTRY DllMain( HANDLE hModule,
            DWORD ul_reason_for_call,
            LPVOID lpReserved
          )
{
  char syspath[256];
  DWORD pcheck;
  switch (ul_reason_for_call)
  {
    case DLL_PROCESS_ATTACH:  //注入进程运行
      GetSystemDirectory(syspath,sizeof(syspath));
      strcat(syspath,"\\ws2_32.dll");
      hProcModule = LoadLibrary(syspath);
      if (hModule == NULL)
      {
        MessageBox(0,"Load library error!","Error",MB_ICONSTOP);
        return false;
      }
      pconnect = GetProcAddress(hProcModule,"connect");
      pcheck = GetFunctionAddress(hProcModule,"connect");
      dwConnect = GetCRC32((unsigned char *)&pconnect,4);
      if(!checkJMP(pconnect) || pcheck!=(unsigned long)pconnect){
        MessageBox(0,"Load function connect error!","Error",MB_ICONSTOP);
        return false;
      }
      psend = GetProcAddress(hProcModule,"send");
      pcheck = GetFunctionAddress(hProcModule,"send");
      dwSend = GetCRC32((unsigned char *)&psend,4);
      if(!checkJMP(psend) || pcheck!=(unsigned long)psend){
        MessageBox(0,"Load function send error!","Error",MB_ICONSTOP);
        return false;
      }
      precv = GetProcAddress(hProcModule,"recv");
      pcheck = GetFunctionAddress(hProcModule,"recv");
      dwRecv = GetCRC32((unsigned char *)&precv,4);
      if(!checkJMP(precv) || pcheck!=(unsigned long)precv){
        MessageBox(0,"Load function recv error!","Error",MB_ICONSTOP);
        return false;
      }
      if (!RawSnifferCheck())
      {
        MessageBox(0,"Wanring: Find a sniffer in local computer, please check virus!","Error",MB_ICONSTOP);
        return false;
      }
      break;
    case DLL_THREAD_ATTACH:    //注入线程运行
      break;
    case DLL_THREAD_DETACH:    //分离线程运行
      break;
    case DLL_PROCESS_DETACH:  //分离进程运行
      break;
  }
  return TRUE;
}

MAIN_API unsigned long KrnlLo(int vdcode,void *c_connect,void *c_send,void *c_recv,int chkey)
{
  if (vdcode != 0x123456) //确认是否为主程序调用
  {
    return 0;
  }
  if (GetTickCount()-chkey > 1000)
  {
    return 0;
  }
  if(!checkJMP(pconnect))
    return false;
  if(!checkJMP(psend))
    return false;
  if(!checkJMP(precv))
    return false;
  if (dwConnect != GetCRC32((unsigned char *)&pconnect,4) || dwSend != GetCRC32((unsigned char *)&psend,4) || dwRecv != GetCRC32((unsigned char *)&precv,4))
  {
    return 0;
  }
  if (c_connect != pconnect || c_send != psend || c_recv != precv)
  {
    return 123456789 ^ chkey;// 成功
  }
  return 0; //失败
}

/////////////////////////////////////////////////////////////////////
//GetCRC32: 求字节流的CRC32校验码
//参数:
// pbData: 指向字节流缓冲区首地址
// nSize: 字节流长度
//
//返回值:
// 字节流的CRC32校验码
//
//这里使用查表法求CRC32校验码,这部分是参考老罗的文章《 矛与盾的较量(2)——CRC原理篇》该写的。
//原文的具体内容请参看: [url]http://asp.7i24.com/netcool/laoluo/articles/show_article.asp?Article_ID=15[/url]
//
//下面使用内联汇编求CRC32校验码,充分使用了CPU中的寄存器,速度和方便性都是使用C/C++所不能比拟的
//
DWORD GetCRC32(const BYTE *pbData, int nSize)
{
DWORD dwCRC32Table[256];

__asm //这片内联汇编是初始化CRC32表
{
MOV ECX, 256

_NextTable:
LEA EAX, [ECX-1]
PUSH ECX
MOV ECX, 8

_NextBit:
SHR EAX, 1
JNC _NotCarry
XOR EAX, 0xEDB88320
_NotCarry:
DEC ECX
JNZ _NextBit

POP ECX
MOV [dwCRC32Table + ECX*4 - 4], EAX
DEC ECX
JNZ _NextTable
}

__asm //下面是求CRC32校验码
{
MOV EAX, -1
MOV EBX, pbData
OR EBX, EBX
JZ _Done
MOV ECX, nSize
OR ECX, ECX
JZ _Done

_NextByte:
MOV DL, [EBX]

XOR DL, AL
MOVZX EDX, DL
SHR EAX, 8
XOR EAX, [dwCRC32Table + EDX*4]

INC EBX
LOOP _NextByte
_Done:
NOT EAX
}
}
////////////////////////////////////////////////////////////////////////////
[/code]

页: [1]
© 1999-2008 EvilOctal Security Team