存档

2012年10月 的存档

魔兽争霸 3 踢人内挂

2012年10月27日 4 条评论

话说 war3 会自动把 war3.exe 所在目录下的 .mix 文件作为 DLL 加载~~

所以编译成 DLL 改成 xxx.mix 放在 war3.exe 的目录里面就好了~~

Update at 0:23 2012/10/28: 我忽然发现我几乎把所有的错误处理都忽略掉了。大概是太久没写代码了。

//
// 默认调用约定:__stdcall
//
// WAR3 数据包格式来源于 W3GStreamer。
//

#include <Windows.h>
#include <WinSock.h>

#define ORDINAL_SEND (0x00000013)

#define ENTRY_NOT_FOUND (-1)

#define PACKET_SIGNATURE (0xF7)
#define PACKET_CHAT (0x0F)

#define PACKET_CHAT_FLAG_IN_PREPARE_SCREEN (0x10)
#define PACKET_CHAT_FLAG_IN_GAME (0x20)

#define COMMAND_KICK "/kick"

#define Add2Ptr(Pointer, Increment, ResultType) (reinterpret_cast<ResultType>((ULONG_PTR)(Pointer) + (Increment)))
#define PtrOffset(Pointer2, Pointer1) ((reinterpret_cast<ULONG_PTR>(Pointer2) - reinterpret_cast<ULONG_PTR>(Pointer1)))

typedef DWORD (SEND) (
  __in SOCKET Socket,
  __in_bcount(BufferSize) PVOID Buffer,
  __in DWORD BufferSize,
  __in DWORD Flags
  );

typedef SEND *PSEND;

typedef __declspec(align(1)) struct _GAME_PACKET {
  BYTE Signature;
  BYTE Operation;
  WORD PacketSize;

  union {
    struct {
      BYTE TargetCount;
      
      //
      // 接下来是目标 ID。
      //
      // BYTE Id[TargetCount];
      //

      BYTE SelfId;
      BYTE Flags;
      
      //
      // 如果 Flags 是 PACKET_CHAT_FLAG_IN_GAME,那么接下来会有 Target。
      //
      // DWORD Target;
      //

      CHAR Message[1];
    } Chat;
  };
} GAME_PACKET, *PGAME_PACKET;

PSEND OldSend;

//
// 定位映像数据目录。
//

PVOID LocateImageDataDirectory(
  __in PVOID ImageBase,
  __in ULONG DataDirectory
  )
{
  PIMAGE_DOS_HEADER DosHeader;
  PIMAGE_NT_HEADERS NtHeaders;

  DosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(ImageBase);
  NtHeaders = Add2Ptr(ImageBase, DosHeader->e_lfanew, PIMAGE_NT_HEADERS);

  //
  // 从文件头中定位数据目录。
  //

  return Add2Ptr(ImageBase, NtHeaders->OptionalHeader.DataDirectory[DataDirectory].VirtualAddress, PVOID);
}

//
// 根据 DLL 文件名定位导入描述符。
//

PIMAGE_IMPORT_DESCRIPTOR LocateImportDescriptor(
  __in PVOID ImageBase,
  __in PSTR DllName
  )
{
  PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;

  //
  // 定位导入描述符。
  //

  ImportDescriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(LocateImageDataDirectory(ImageBase, 
    IMAGE_DIRECTORY_ENTRY_IMPORT));

  while (ImportDescriptor->Name) {
    PSTR ImportedDllName;

    //
    // 比较 DLL 文件名。
    //

    ImportedDllName = Add2Ptr(ImageBase, ImportDescriptor->Name, PSTR);
    
    if (!_strcmpi(DllName, ImportedDllName)) {
      return ImportDescriptor;
    }

    ImportDescriptor++;
  }

  return NULL;
}

//
// 定位导入查找表。
//

PULONG_PTR LocateImportEntries(
  __in PVOID ImageBase,
  __in PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor,
  __in BOOL LocateOriginalEntries
  )
{
  if (LocateOriginalEntries) {
    return Add2Ptr(ImageBase, ImportDescriptor->OriginalFirstThunk, PULONG_PTR);
  } else {
    return Add2Ptr(ImageBase, ImportDescriptor->FirstThunk, PULONG_PTR);
  }
}

//
// 比较导入查找表中的表项。
//
// 这个函数可以正确处理按照序号导入的表项。
//

BOOL CompareImportEntry(
  __in PVOID ImageBase,
  __in ULONG_PTR ImportedEntry,
  __in PSTR EntryToLookup
  )
{
  if (IMAGE_SNAP_BY_ORDINAL(ImportedEntry) != 
    IMAGE_SNAP_BY_ORDINAL(reinterpret_cast<ULONG_PTR>(EntryToLookup))) {
    return FALSE;
  }
  
  if (IMAGE_SNAP_BY_ORDINAL(ImportedEntry)) {

    //
    // ImportedEntry 是一个函数的序号。也意味着 EntryToLookup 也是一个序号。
    //

    return IMAGE_ORDINAL(ImportedEntry) == IMAGE_ORDINAL(reinterpret_cast<ULONG_PTR>(EntryToLookup));
  } else {

    //
    // 比较字符串。
    //

    return !strcmp(Add2Ptr(ImageBase, ImportedEntry, PSTR), EntryToLookup);
  }
}

//
// 查找导入查找表表项。
//
// 如果 ProcName 是一个序号,那么它的最高位需要被设为 1。
//

DWORD LookupImportEntryIndex(
  __in PVOID ImageBase,
  __in PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor,
  __in PSTR ProcName
  )
{
  DWORD Index;
  PULONG_PTR Entries;

  Index = 0;

  //
  // 定位导入查找表。
  //

  Entries = LocateImportEntries(ImageBase, ImportDescriptor, TRUE);

  //
  // 遍历导入查找表。
  //
  
  while (ULONG_PTR Offset = Entries[Index]) {

    //
    // 比较函数名 / 序号。
    //

    if (CompareImportEntry(ImageBase, Offset, ProcName)) {
      return Index;
    }

    Index++;
  }

  return ENTRY_NOT_FOUND;
}

//
// IAT HOOK。
//

PVOID InterceptImportedProc(
  __in PTSTR TargetName,
  __in PSTR DllName,
  __in PSTR ProcName,
  __in PVOID HookProc
  )
{
  PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  DWORD ImportEntryIndex;
  PULONG_PTR Entries;
  PVOID TargetBase;

  //
  // 定位要 HOOK 的模块。
  //

  TargetBase = reinterpret_cast<PVOID>(GetModuleHandle(TargetName));

  //
  // 定位导入描述符。
  //

  ImportDescriptor = LocateImportDescriptor(TargetBase, DllName);

  //
  // 查找导入的函数的索引。
  //

  ImportEntryIndex = LookupImportEntryIndex(TargetBase, ImportDescriptor, ProcName);

  //
  // 定位导入函数的地址表。
  //

  Entries = LocateImportEntries(TargetBase, ImportDescriptor, FALSE);

  //
  // 读取当前地址并替换。
  //

  return reinterpret_cast<PVOID>(InterlockedExchange(reinterpret_cast<volatile ULONG_PTR *>(&Entries[ImportEntryIndex]),
    reinterpret_cast<ULONG_PTR>(HookProc)));
}

//
// WSASend 的过滤函数。
//

DWORD NewSend(
  __in SOCKET Socket,
  __in_bcount(BufferSize) PVOID Buffer,
  __in DWORD BufferSize,
  __in DWORD Flags
    )
{
  PGAME_PACKET GamePacket, NewGamePacket;

  GamePacket = reinterpret_cast<PGAME_PACKET>(Buffer);

  //
  // 检测是否为发送消息的包。
  //

  if (GamePacket->Signature == PACKET_SIGNATURE && GamePacket->Operation == PACKET_CHAT) {
    PSTR MessageBuffer;
    
    //
    // 标志位之前有 TargetCount 个 ID,每个 ID 占用一个 BYTE 的大小。
    //

    NewGamePacket = Add2Ptr(GamePacket, GamePacket->Chat.TargetCount * sizeof(BYTE), PGAME_PACKET);

    if (NewGamePacket->Chat.Flags == PACKET_CHAT_FLAG_IN_GAME) {

      //
      // 如果 Flags == PACKET_CHAT_FLAG_IN_GAME,那么在消息正文之前还有一个 Target,占用一个 DWORD。
      //

      NewGamePacket = Add2Ptr(NewGamePacket, sizeof(DWORD), PGAME_PACKET);
    }

    MessageBuffer = NewGamePacket->Chat.Message;

    //
    // 比较命令。(MessageBuffer 是以 0 结尾的。)
    //

    if (!strcmp(MessageBuffer, COMMAND_KICK)) {

      //
      // 返回错误。游戏会自动断开连接。
      //

      return SOCKET_ERROR;
    }
  }

  return OldSend(Socket, Buffer, BufferSize, Flags);
}

//
// DLL 入口点。
//

BOOL DllMain(
  __in HINSTANCE DllBase,
  __in DWORD Reason,
  __reserved PVOID Reserved
  )
{
  if (Reason == DLL_PROCESS_ATTACH) {

    //
    // HOOK。
    //

    OldSend = reinterpret_cast<PSEND>(InterceptImportedProc(TEXT("war3.exe"), "wsock32.dll", 
      reinterpret_cast<PSTR>(ORDINAL_SEND | IMAGE_ORDINAL_FLAG), reinterpret_cast<PVOID>(NewSend)));

  } else if (Reason == DLL_PROCESS_DETACH) {

    //
    // UNHOOK。
    //

    InterceptImportedProc(TEXT("war3.exe"), "wsock32.dll", 
      reinterpret_cast<PSTR>(ORDINAL_SEND | IMAGE_ORDINAL_FLAG), reinterpret_cast<PVOID>(OldSend));
  }

  return TRUE;
}