话说 war3 会自动把 war3.exe 所在目录下的 .mix 文件作为 DLL 加载~~
所以编译成 DLL 改成 xxx.mix 放在 war3.exe 的目录里面就好了~~
Update at 0:23 2012/10/28: 我忽然发现我几乎把所有的错误处理都忽略掉了。大概是太久没写代码了。
//
// 默认调用约定:__stdcall
//
// WAR3 数据包格式来源于 W3GStreamer。
//
#include
#include
#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((ULONG_PTR)(Pointer) + (Increment)))
#define PtrOffset(Pointer2, Pointer1) ((reinterpret_cast(Pointer2) - reinterpret_cast(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(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(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(EntryToLookup))) {
return FALSE;
}
if (IMAGE_SNAP_BY_ORDINAL(ImportedEntry)) {
//
// ImportedEntry 是一个函数的序号。也意味着 EntryToLookup 也是一个序号。
//
return IMAGE_ORDINAL(ImportedEntry) == IMAGE_ORDINAL(reinterpret_cast(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(GetModuleHandle(TargetName));
//
// 定位导入描述符。
//
ImportDescriptor = LocateImportDescriptor(TargetBase, DllName);
//
// 查找导入的函数的索引。
//
ImportEntryIndex = LookupImportEntryIndex(TargetBase, ImportDescriptor, ProcName);
//
// 定位导入函数的地址表。
//
Entries = LocateImportEntries(TargetBase, ImportDescriptor, FALSE);
//
// 读取当前地址并替换。
//
return reinterpret_cast(InterlockedExchange(reinterpret_cast(&Entries[ImportEntryIndex]),
reinterpret_cast(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(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(InterceptImportedProc(TEXT("war3.exe"), "wsock32.dll",
reinterpret_cast(ORDINAL_SEND | IMAGE_ORDINAL_FLAG), reinterpret_cast(NewSend)));
} else if (Reason == DLL_PROCESS_DETACH) {
//
// UNHOOK。
//
InterceptImportedProc(TEXT("war3.exe"), "wsock32.dll",
reinterpret_cast(ORDINAL_SEND | IMAGE_ORDINAL_FLAG), reinterpret_cast(OldSend));
}
return TRUE;
}