Move WormKit loaders, modules, and libraries to module sub folder. Add signature scanning and import replacing.

This commit is contained in:
Ray Koopa 2020-07-21 21:06:03 +02:00
parent 518207b7e6
commit a99a883f6f
37 changed files with 735 additions and 354 deletions

View File

@ -49,13 +49,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = " Solution Items", " Solut
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Worms2.GameServer", "tool\Syroot.Worms.Worms2.GameServer\Syroot.Worms.Worms2.GameServer.csproj", "{13ABF717-5809-441D-A5D8-66E1EE75A390}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fkNetcode", "tool\fkNetcode\fkNetcode.vcxproj", "{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module Projects", "Module Projects", "{2A8C6AAA-BA1F-4FB6-A598-114930217B6C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FrontendKitWS", "tool\FrontendKitWS\FrontendKitWS.vcxproj", "{E391EA12-B929-466C-932F-DEF72B8CEB5D}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WormKitTools", "module\WormKitTools\WormKitTools.vcxproj", "{068A8647-0A66-4E39-983B-43ACEAC5C937}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wkUnlimiter", "tool\wkUnlimiter\wkUnlimiter.vcxproj", "{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FrontendKitWS", "module\FrontendKitWS\FrontendKitWS.vcxproj", "{E391EA12-B929-466C-932F-DEF72B8CEB5D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WormKitTools", "tool\WormKitTools\FrontendKitLib.vcxproj", "{068A8647-0A66-4E39-983B-43ACEAC5C937}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fkDesPatch", "module\fkDesPatch\fkDesPatch.vcxproj", "{6B4D57EA-4642-440A-AB62-2E011D7B64E1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fkNetcode", "module\fkNetcode\fkNetcode.vcxproj", "{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wkUnlimiter", "module\wkUnlimiter\wkUnlimiter.vcxproj", "{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -131,22 +135,26 @@ Global
{13ABF717-5809-441D-A5D8-66E1EE75A390}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13ABF717-5809-441D-A5D8-66E1EE75A390}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13ABF717-5809-441D-A5D8-66E1EE75A390}.Release|Any CPU.Build.0 = Release|Any CPU
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.ActiveCfg = Debug|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.Build.0 = Debug|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.ActiveCfg = Release|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.Build.0 = Release|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.Build.0 = Debug|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.ActiveCfg = Release|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.Build.0 = Release|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.Build.0 = Debug|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.ActiveCfg = Release|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.Build.0 = Release|Win32
{068A8647-0A66-4E39-983B-43ACEAC5C937}.Debug|Any CPU.ActiveCfg = Debug|Win32
{068A8647-0A66-4E39-983B-43ACEAC5C937}.Debug|Any CPU.Build.0 = Debug|Win32
{068A8647-0A66-4E39-983B-43ACEAC5C937}.Release|Any CPU.ActiveCfg = Release|Win32
{068A8647-0A66-4E39-983B-43ACEAC5C937}.Release|Any CPU.Build.0 = Release|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.Build.0 = Debug|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.ActiveCfg = Release|Win32
{E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.Build.0 = Release|Win32
{6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Debug|Any CPU.ActiveCfg = Debug|Win32
{6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Debug|Any CPU.Build.0 = Debug|Win32
{6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Release|Any CPU.ActiveCfg = Release|Win32
{6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Release|Any CPU.Build.0 = Release|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.ActiveCfg = Debug|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.Build.0 = Debug|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.ActiveCfg = Release|Win32
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.Build.0 = Release|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.Build.0 = Debug|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.ActiveCfg = Release|Win32
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -169,10 +177,11 @@ Global
{212F8090-9775-4098-BD44-9ABC01FBE553} = {99E56312-A064-4AD3-8443-0B56A5F76E6B}
{1FAB6B9F-2585-46DC-81C0-579DC808C389} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
{13ABF717-5809-441D-A5D8-66E1EE75A390} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
{E391EA12-B929-466C-932F-DEF72B8CEB5D} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
{068A8647-0A66-4E39-983B-43ACEAC5C937} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
{068A8647-0A66-4E39-983B-43ACEAC5C937} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C}
{E391EA12-B929-466C-932F-DEF72B8CEB5D} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C}
{6B4D57EA-4642-440A-AB62-2E011D7B64E1} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C}
{C4138811-7CFA-4826-A3DD-AF2B618EAFC1} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C}
{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1CD4EDE2-A5FB-4A58-A850-3506AB7E7B69}

View File

@ -93,7 +93,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\wkConfig.cpp" />
<ClCompile Include="src\wkIni.cpp" />
<ClCompile Include="src\wkPatch.cpp" />
<ClCompile Include="src\wkUtils.cpp" />
<ClCompile Include="src\pch.cpp">
@ -103,13 +103,14 @@
<ClCompile Include="src\wkExe.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\wkConfig.h" />
<ClInclude Include="include\wkIni.h" />
<ClInclude Include="include\wkPatch.h" />
<ClInclude Include="include\wkUtils.h" />
<ClInclude Include="include\wkExe.h" />
<ClInclude Include="src\pch.h" />
</ItemGroup>
<ItemGroup>
<None Include="include\wkExe.inl" />
<None Include="include\wkPatch.inl" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -18,9 +18,6 @@
<ClCompile Include="src\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\wkConfig.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\wkUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -30,14 +27,14 @@
<ClCompile Include="src\wkExe.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\wkIni.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\wkConfig.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\wkUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -47,10 +44,16 @@
<ClInclude Include="include\wkExe.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\wkIni.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="include\wkPatch.inl">
<Filter>Header Files</Filter>
</None>
<None Include="include\wkExe.inl">
<Filter>Header Files</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,88 @@
#pragma once
#include <stdint.h>
#include <string>
#include <Windows.h>
#include "wkPatch.h"
namespace wk
{
enum JumpType
{
JT_JMP, // jmp (0xE9)
JT_CALL, // call (0xE8)
JT_FARJMP, // farjump (0xEA)
JT_FARCALL, // farcall (0x9A)
JT_PUSHRET, // pushret
};
struct Exe
{
public:
// ---- FIELDS -------------------------------------------------------------------------------------------------
HANDLE Handle;
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS NtHeader;
PIMAGE_FILE_HEADER FileHeader;
PIMAGE_OPTIONAL_HEADER OptHeader;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
Exe(HMODULE hModule = NULL);
// ---- METHODS ------------------------------------------------------------------------------------------------
/// <summary>
/// Returns the offset of the first match of the given signature. It must take the form "12 FF ?? EB 34", where
/// two question marks represent a wildcard.
/// </summary>
/// <param name="signature">The signature to find.</param>
/// <returns>The offset of the first match of the signature.</returns>
ULONG_PTR find(std::string signature);
/// <summary>
/// Returns the offset of the first match of the given signature. It is an array of bytes, where all values
/// being negative or bigger than 0xFF represent a wildcard.
/// </summary>
/// <param name="signature">The signature to find.</param>
/// <returns>The offset of the first match of the signature.</returns>
ULONG_PTR find(int16_t signature[], size_t signatureSize); // any non-uint8_t value is a wildcard
/// <summary>
/// Writes the given value to the specified offset.
/// </summary>
/// <typeparam name="T">The type of the value to write.</typeparam>
/// <param name="offset">The relative address at which to write the value.</param>
/// <param name="value">The value to write.</param>
template <class T> void set(ULONG_PTR offset, T value);
/// <summary>
/// Replaces the given import with the specified hook, which must have the same signature.
/// </summary>
/// <param name="import">The imported method to replace.</param>
/// <param name="hook">The method to replace the import with.</param>
void setImp(LPVOID import, LPVOID hook);
/// <summary>
/// Writes a jump to the given callee at the specified offset.
/// </summary>
/// <param name="offset">The relative address at which to write the value.</param>
/// <param name="size">The number of bytes overwritten for the jump opcode.</param>
/// <param name="callee">The called method.</param>
/// <param name="jumpType">The type of the jump to insert.</param>
void setJmp(ULONG_PTR offset, SIZE_T size, LPVOID callee, DWORD jumpType);
/// <summary>
/// Nops out the given number of bytes at the specified offset.
/// </summary>
/// <param name="offset">The relative address at which to nop out code.</param>
/// <param name="size">The number of bytes to nop out.</param>
void setNop(ULONG_PTR offset, SIZE_T size);
private:
LPVOID getAddress(ULONG_PTR offset);
PIMAGE_IMPORT_DESCRIPTOR getImports();
};
}
#include "wkExe.inl"

View File

@ -0,0 +1,9 @@
namespace wk
{
template <class T>
void Exe::set(ULONG_PTR offset, T newValue)
{
Patch patch(getAddress(offset), sizeof(T));
patch.write(newValue);
}
}

View File

@ -3,10 +3,10 @@
namespace wk
{
class Config
class Ini
{
public:
Config(LPCSTR fileName);
Ini(LPCSTR fileName);
void get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const;
void get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const;

View File

@ -0,0 +1,31 @@
#pragma once
#include <Windows.h>
namespace wk
{
struct Patch
{
public:
// ---- FIELDS -------------------------------------------------------------------------------------------------
ULONG_PTR position;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
Patch(LPVOID address, SIZE_T size);
~Patch();
// ---- METHODS ------------------------------------------------------------------------------------------------
void close() const;
template <class T> void write(const T& value);
private:
// ---- FIELDS -------------------------------------------------------------------------------------------------
LPBYTE _address;
SIZE_T _size;
DWORD _oldProtect;
};
}
#include "wkPatch.inl"

View File

@ -14,21 +14,28 @@ namespace wk
GAMEID_W2_1_05 = GAMEID_W2 | 0x010500,
GAMEID_W2_1_05_BR = GAMEID_W2_1_05 | 0x10, // Worms2 1.05 Br
GAMEID_W2_1_05_EN = GAMEID_W2_1_05 | 0x20, // Worms2 1.05 Du, En, Fr, It, Po, Sp, Sw
GAMEID_W2_1_05_GE = GAMEID_W2_1_05 | 0x30, // Worms2 1.05
GAMEID_W2_1_05_NA = GAMEID_W2_1_05 | 0x40, // Worms2 1.05
GAMEID_W2_1_05_SA = GAMEID_W2_1_05 | 0x50, // Worms2 1.05
GAMEID_W2_1_05_GE = GAMEID_W2_1_05 | 0x30, // Worms2 1.05 De
GAMEID_W2_1_05_NA = GAMEID_W2_1_05 | 0x40, // Worms2 1.05 NA
GAMEID_W2_1_05_SA = GAMEID_W2_1_05 | 0x50, // Worms2 1.05 SA
GAMEID_W2_1_07 = GAMEID_W2 | 0x010700,
GAMEID_W2_1_07_TRY = GAMEID_W2_1_07 | 0x60, // Worms2 1.07 Trymedia
GAMEID_W2_LAST = GAMEID_W2_1_07_TRY,
GAMEID_WA = 0x30000000,
GAMEID_WA_3_6_31 = GAMEID_WA | 0x030630, // Worms Armageddon 3.6.31
GAMEID_WA_3_7_2_1 = GAMEID_WA | 0x030721, // Worms Armageddon 3.7.2.1
GAMEID_WA_3_8 = GAMEID_WA | 0x030800,
GAMEID_WA_3_8_CD = GAMEID_WA_3_8 | 0x10, // Worms Armageddon 3.8 CD
GAMEID_WA_LAST = GAMEID_WA_3_8_CD,
GAMEID_WWP = 0x40000000,
GAMEID_WWP_LAST = GAMEID_WWP,
GAMEID_OW = 0x50000000,
GAMEID_OW_LAST = GAMEID_OW,
GAMEID_WWPA = 0x60000000,
GAMEID_WWPA_LAST = GAMEID_WWPA,
};
int getGameID(DWORD timeDateStamp);

View File

@ -0,0 +1,160 @@
#include "wkExe.h"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
namespace wk
{
// ---- CONSTRUCTORS & DESTRUCTOR ----------------------------------------------------------------------------------
Exe::Exe(HMODULE hModule)
{
Handle = hModule == NULL ? GetModuleHandleA(NULL) : hModule;
DosHeader = (PIMAGE_DOS_HEADER)Handle;
NtHeader = (PIMAGE_NT_HEADERS)((DWORD)DosHeader + DosHeader->e_lfanew);
FileHeader = (PIMAGE_FILE_HEADER)&NtHeader->FileHeader;
OptHeader = (PIMAGE_OPTIONAL_HEADER)&NtHeader->OptionalHeader;
}
// ---- METHODS (PUBLIC) -------------------------------------------------------------------------------------------
ULONG_PTR Exe::find(std::string signature)
{
std::istringstream iss(signature);
std::istream_iterator<std::string> begin(iss), end;
std::vector<std::string> tokens(begin, end);
std::vector<int16_t> sig;
for (auto& token : tokens)
sig.push_back(token == "??" ? -1 : std::stoi(token, 0, 16));
return find(&sig[0], sig.size());
}
ULONG_PTR Exe::find(int16_t signature[], size_t signatureSize)
{
PIMAGE_SECTION_HEADER sections = IMAGE_FIRST_SECTION(NtHeader);
for (int index = 0; index < FileHeader->NumberOfSections; index++)
{
if (sections[index].Characteristics != (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ))
continue;
LPBYTE code = (LPBYTE)(ULONG_PTR)Handle + sections[index].VirtualAddress;
LPBYTE last = &code[sections[index].SizeOfRawData - signatureSize];
for (; code < last; code++)
{
bool found = true;
for (size_t i = 0; i < signatureSize; i++)
{
if (signature[i] < 0x00 || signature[i] > 0xFF || code[i] == (BYTE)signature[i])
{
continue;
}
else
{
found = false;
break;
}
}
if (found)
return (ULONG_PTR)code - (ULONG_PTR)Handle;
}
}
throw std::exception("Could not find signature.");
}
void Exe::setImp(LPVOID import, LPVOID hook)
{
bool success = false;
PIMAGE_IMPORT_DESCRIPTOR imports = getImports();
if (!imports)
throw std::exception("Import patching failed, import table not found.");
while (imports->Characteristics)
{
PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)Handle + imports->OriginalFirstThunk);
PIMAGE_THUNK_DATA table = (PIMAGE_THUNK_DATA)((ULONG_PTR)Handle + imports->FirstThunk);
while (thunk->u1.Ordinal)
{
if (table->u1.Function == (ULONG_PTR)import)
{
Patch patch(&table->u1.Function, sizeof(ULONG_PTR));
patch.write(hook);
success = true;
}
thunk++;
table++;
}
imports++;
}
if (!success)
throw std::exception("Import patching failed, import not found.");
}
void Exe::setJmp(ULONG_PTR offset, SIZE_T size, LPVOID callee, DWORD jumpType)
{
Patch patch(getAddress(offset), size);
if (size >= 5 && offset)
{
BYTE opSize, opCode;
switch (jumpType)
{
case JT_PUSHRET: opSize = 6; opCode = 0x68; break;
case JT_FARJMP: opSize = 7; opCode = 0xEA; break;
case JT_FARCALL: opSize = 7; opCode = 0x9A; break;
case JT_CALL: opSize = 5; opCode = 0xE8; break;
default: opSize = 5; opCode = 0xE9; break;
}
if (size < opSize)
throw std::exception("Not enough space to patch opcode.");
patch.write(opCode);
switch (opSize)
{
case 7:
patch.write((ULONG)callee);
patch.write<WORD>(0x23);
break;
case 6:
patch.write((ULONG)callee);
patch.write<BYTE>(0xC3);
break;
default:
patch.write((ULONG)callee - (ULONG_PTR)offset - 5);
break;
}
for (DWORD i = opSize; i < size; i++)
patch.write((uint8_t)0x90);
}
}
void Exe::setNop(ULONG_PTR offset, SIZE_T size)
{
Patch patch(getAddress(offset), size);
while (size--)
patch.write((uint8_t)0x90);
}
// ---- METHODS (PRIVATE) ------------------------------------------------------------------------------------------
LPVOID Exe::getAddress(ULONG_PTR offset)
{
return (LPVOID)((ULONG_PTR)Handle + offset);
}
PIMAGE_IMPORT_DESCRIPTOR Exe::getImports()
{
if ((FileHeader->SizeOfOptionalHeader
>= FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT + 1]))
&& OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
&& OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)
{
return (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)Handle
+ OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
}
return NULL;
}
}

View File

@ -1,38 +1,38 @@
#include "wkConfig.h"
#include "wkIni.h"
#include <stdio.h>
namespace wk
{
Config::Config(LPCSTR fileName)
Ini::Ini(LPCSTR fileName)
{
GetModuleFileName(NULL, _filePath, MAX_PATH);
char* sepIdx = strrchr(_filePath, '\\') + 1;
strcpy_s(sepIdx, MAX_PATH - (int)(sepIdx - _filePath), fileName);
}
void Config::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const
void Ini::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const
{
result = GetPrivateProfileInt(category, key, fallback, _filePath);
}
void Config::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const
void Ini::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const
{
result = GetPrivateProfileInt(category, key, fallback, _filePath);
}
void Config::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const
void Ini::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const
{
GetPrivateProfileString(category, key, fallback, result, resultLength, _filePath);
}
void Config::set(LPCSTR category, LPCSTR key, UINT value) const
void Ini::set(LPCSTR category, LPCSTR key, UINT value) const
{
CHAR buffer[32];
sprintf_s(buffer, "%d", value);
WritePrivateProfileString(category, key, buffer, _filePath);
}
void Config::set(LPCSTR category, LPCSTR key, LPCSTR value) const
void Ini::set(LPCSTR category, LPCSTR key, LPCSTR value) const
{
WritePrivateProfileString(category, key, value, _filePath);
}

View File

@ -0,0 +1,32 @@
#include "wkPatch.h"
#include <stdexcept>
namespace wk
{
// ---- CONSTRUCTORS & DESTRUCTOR ----------------------------------------------------------------------------------
Patch::Patch(LPVOID address, SIZE_T size)
: _address((LPBYTE)address)
, _size(size)
, position(0)
{
if (!_address || !_size)
throw std::invalid_argument("Address and size must not be 0.");
if (!VirtualProtect(_address, _size, PAGE_EXECUTE_READWRITE, &_oldProtect))
throw std::exception("VirtualProtect failed, call GetLastError for more info.");
}
Patch::~Patch()
{
close();
}
// ---- METHODS (PUBLIC) -------------------------------------------------------------------------------------------
void Patch::close() const
{
DWORD oldProtect;
if (!VirtualProtect(_address, _size, _oldProtect, &oldProtect))
throw std::exception("VirtualProtect failed, call GetLastError for more info.");
}
};

View File

@ -14,6 +14,8 @@ namespace wk
case 0x3528DA98: return GAMEID_W2_1_05_NA;
case 0x3528DBDA: return GAMEID_W2_1_05_SA;
case 0x3587BE19: return GAMEID_W2_1_07_TRY;
case 0x4CE25091: return GAMEID_WA_3_6_31;
case 0x513D83BC: return GAMEID_WA_3_7_2_1;
case 0x5EF04515: return GAMEID_WA_3_8_CD;
default: return GAMEID_NONE;
}

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{6b4d57ea-4642-440a-ab62-2e011d7b64e1}</ProjectGuid>
<RootNamespace>fkDesPatch</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>bin\$(Configuration)\</OutDir>
<IntDir>obj\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>bin\$(Configuration)\</OutDir>
<IntDir>obj\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;FKDESPATCH_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\WormKitTools\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;FKDESPATCH_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\WormKitTools\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WormKitTools\WormKitTools.vcxproj">
<Project>{068a8647-0a66-4e39-983b-43aceac5c937}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,84 @@
#define WIN32_LEAN_AND_MEAN
#include <stdint.h>
#include <Windows.h>
#include "wkExe.h"
#include "wkIni.h"
#include "wkUtils.h"
// ---- Initialization ----
BOOL iniAutoOssett;
void init()
{
wk::Ini ini("fkDesPatch.ini");
// Load INI settings.
ini.get("Frontend", "AutoOssett", iniAutoOssett, FALSE);
// Ensure INI file has been created with default setting.
ini.set("Frontend", "AutoOssett", iniAutoOssett);
}
// ---- Patch ----
void patch(wk::Exe& exe, int gameVersion)
{
if (gameVersion == wk::GAMEID_W2_1_07_TRY)
{
}
else
{
// getAddress file to code 0x400C02
if (iniAutoOssett)
exe.set(0x000446A2, (uint8_t)0xEB);
}
}
// ---- Main ----
int getVersion(DWORD timeDateStamp)
{
int id = wk::getGameID(timeDateStamp);
switch (id)
{
case wk::GAMEID_W2_1_05_BR:
case wk::GAMEID_W2_1_05_EN:
case wk::GAMEID_W2_1_05_GE:
case wk::GAMEID_W2_1_05_NA:
case wk::GAMEID_W2_1_05_SA:
case wk::GAMEID_W2_1_07_TRY:
return id;
default:
return 0;
}
}
BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
wk::Exe exe;
int version = getVersion(exe.FileHeader->TimeDateStamp);
if (version)
{
init();
patch(exe, version);
}
else
{
MessageBox(NULL, "fkDesPatch is incompatible with your game version. Please run the 1.05 patch or 1.07 "
"release of Worms 2. Otherwise, you can delete the module to remove this warning.", "fkDesPatch",
MB_ICONWARNING);
}
}
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

View File

@ -98,7 +98,7 @@
<ResourceCompile Include="fkNetcode.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WormKitTools\FrontendKitLib.vcxproj">
<ProjectReference Include="..\WormKitTools\WormKitTools.vcxproj">
<Project>{068a8647-0a66-4e39-983b-43aceac5c937}</Project>
</ProjectReference>
</ItemGroup>

View File

@ -2,36 +2,36 @@
#include <Windows.h>
#include <WinInet.h>
#include <winsock.h>
#include "wkConfig.h"
#include "wkIni.h"
#include "wkExe.h"
#include "wkPatch.h"
#include "wkUtils.h"
// ---- Configuration ----
// ---- Initialization ----
CHAR cfgFallbackIP[16];
CHAR cfgServiceUrl[MAX_PATH];
BOOL cfgShowErrors;
CHAR iniFallbackIP[16];
CHAR iniServiceUrl[MAX_PATH];
BOOL iniShowErrors;
void configure()
void init()
{
wk::Config config("fkNetcode.ini");
wk::Ini ini("fkNetcode.ini");
// Load INI settings.
config.get("AddressResolval", "FallbackIP", cfgFallbackIP, 16);
config.get("AddressResolval", "ServiceUrl", cfgServiceUrl, MAX_PATH, "http://ip.syroot.com");
config.get("AddressResolval", "ShowErrors", cfgShowErrors, TRUE);
ini.get("AddressResolval", "FallbackIP", iniFallbackIP, 16);
ini.get("AddressResolval", "ServiceUrl", iniServiceUrl, MAX_PATH, "http://ip.syroot.com");
ini.get("AddressResolval", "ShowErrors", iniShowErrors, TRUE);
// Ensure INI file has been created with default setting.
config.set("AddressResolval", "FallbackIP", cfgFallbackIP);
config.set("AddressResolval", "ServiceUrl", cfgServiceUrl);
config.set("AddressResolval", "ShowErrors", cfgShowErrors);
ini.set("AddressResolval", "FallbackIP", iniFallbackIP);
ini.set("AddressResolval", "ServiceUrl", iniServiceUrl);
ini.set("AddressResolval", "ShowErrors", iniShowErrors);
// Validate fallback IP.
BYTE b;
if (*cfgFallbackIP && sscanf_s(cfgFallbackIP, "%hhu.%hhu.%hhu.%hhu", &b, &b, &b, &b) != 4)
if (*iniFallbackIP && sscanf_s(iniFallbackIP, "%hhu.%hhu.%hhu.%hhu", &b, &b, &b, &b) != 4)
{
*cfgFallbackIP = NULL;
*iniFallbackIP = NULL;
MessageBox(NULL, "Invalid fallback IP setting in fkNetcode.ini has been ignored.", "fkNetcode", MB_ICONWARNING);
}
}
@ -50,14 +50,14 @@ bool resolveIPCached(LPSTR buffer)
bool resolveIPExternal(LPSTR buffer)
{
if (!*cfgServiceUrl)
if (!*iniServiceUrl)
return false;
// Query a web service which replies with the IP in plain text.
HINTERNET hInternet = 0, hFile = 0;
if (hInternet = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0))
{
if (hFile = InternetOpenUrl(hInternet, cfgServiceUrl, NULL, 0,
if (hFile = InternetOpenUrl(hInternet, iniServiceUrl, NULL, 0,
INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD, NULL))
{
DWORD responseLength = 0;
@ -84,7 +84,7 @@ bool resolveIPExternal(LPSTR buffer)
DWORD error = GetLastError();
if (hFile) InternetCloseHandle(hFile);
if (hInternet) InternetCloseHandle(hInternet);
if (error && cfgShowErrors)
if (error && iniShowErrors)
{
CHAR msg[512];
sprintf_s(msg, "Could not resolve your IP through the web service. %s", wk::getErrorMessage(error).c_str());
@ -95,9 +95,9 @@ bool resolveIPExternal(LPSTR buffer)
bool resolveIPFallback(LPSTR buffer)
{
if (!*cfgFallbackIP)
if (!*iniFallbackIP)
return false;
lstrcpy(buffer, cfgFallbackIP);
lstrcpy(buffer, iniFallbackIP);
return true;
}
@ -136,19 +136,19 @@ bool __stdcall patchResolveIP(LPSTR buffer, int bufferLength)
void patch(wk::Exe& exe, int gameVersion)
{
wk::Patch::jump(exe.Offset(0x00001799), 5, &patchResolveIP, wk::IJ_JUMP); // replace IP resolve with web service
exe.setJmp(0x00001799, 5, &patchResolveIP, wk::JT_JMP); // replace IP resolve with web service
if (gameVersion == wk::GAMEID_W2_1_07_TRY)
{
wk::Patch::nops(exe.Offset(0x00053B96), 5); // prevent overriding IP with user name
wk::Patch::nops(exe.Offset(0x00054693), 5); // prevent overriding IP with NAT IP
wk::Patch::nops(exe.Offset(0x00054635), 11); // useless sleep when connecting to server
exe.setNop(0x00053B96, 5); // prevent overriding IP with user name
exe.setNop(0x00054693, 5); // prevent overriding IP with NAT IP
exe.setNop(0x00054635, 11); // useless sleep when connecting to server
}
else
{
wk::Patch::nops(exe.Offset(0x00053E96), 5); // prevent overriding IP with user name
wk::Patch::nops(exe.Offset(0x00054935), 11); // useless sleep when connecting to server
wk::Patch::nops(exe.Offset(0x00054993), 5); // prevent overriding IP with NAT IP
exe.setNop(0x00053E96, 5); // prevent overriding IP with user name
exe.setNop(0x00054935, 11); // useless sleep when connecting to server
exe.setNop(0x00054993, 5); // prevent overriding IP with NAT IP
}
}
@ -181,7 +181,7 @@ BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
int version = getVersion(exe.FileHeader->TimeDateStamp);
if (version)
{
configure();
init();
patch(exe, version);
}
else

View File

@ -0,0 +1,132 @@
#define WIN32_LEAN_AND_MEAN
#include <stdint.h>
#include <Windows.h>
#include "wkConfig.h"
#include "wkExe.h"
#include "wkUtils.h"
// ---- Initialization ----
BOOL iniAllowMultiInstance;
BOOL iniEnableAllControls;
BOOL iniShowAllControls;
BOOL iniUnlockCamera;
BOOL iniUnlockCursor;
void init(int gameID)
{
wk::Ini ini("wkUnlimiter.ini");
// Load INI settings.
ini.get("Common", "AllowMultiInstance", iniAllowMultiInstance, TRUE);
ini.get("Frontend", "EnableAllControls", iniEnableAllControls, TRUE);
ini.get("Frontend", "ShowAllControls", iniShowAllControls, FALSE);
if (gameID == wk::GAMEID_WA_3_8_CD)
{
ini.get("Game", "UnlockCamera", iniUnlockCamera, TRUE);
ini.get("Game", "UnlockCursor", iniUnlockCursor, TRUE);
}
// Ensure INI file has been created with default setting.
ini.set("Common", "AllowMultiInstance", iniAllowMultiInstance);
ini.set("Frontend", "EnableAllControls", iniEnableAllControls);
ini.set("Frontend", "ShowAllControls", iniShowAllControls);
if (gameID == wk::GAMEID_WA_3_8_CD)
{
ini.set("Game", "UnlockCamera", iniUnlockCamera);
ini.set("Game", "UnlockCursor", iniUnlockCursor);
}
}
// ---- Patch ----
HANDLE WINAPI Kernel32_CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount,
LONG lMaximumCount, LPCSTR lpName)
{
HANDLE handle = CreateSemaphoreA(lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName);
SetLastError(ERROR_SUCCESS);
return handle;
}
void patch(wk::Exe& exe, int gameID)
{
if (iniAllowMultiInstance)
{
// Requires WormKitDS as game loads modules after this
exe.setImp(CreateSemaphoreA, Kernel32_CreateSemaphoreA);
}
if (iniEnableAllControls)
{
// CWnd::EnableWindow(bEnable) -> CWnd::EnableWindow(TRUE)
exe.set(exe.find("C2 04 00 8B 49 50 8B 01 FF A0 A8") - 13, 0x9090016A);
}
if (iniShowAllControls)
{
// CWnd::ShowWindow(bShow) -> CWnd::ShowWindow(TRUE)
exe.set(exe.find("C2 04 00 8B 49 50 8B 01 FF A0 A0") - 13, 0x9090016A);
}
if (iniUnlockCamera)
{
exe.setNop(exe.find("89 13 EB 06 3B C7 7E 02"), 2); // X- axis
exe.setNop(exe.find("89 3B 8B 43 04 3B C1 7D 09 5F 5E"), 2); // X+ axis
exe.setNop(exe.find("89 4B 04 5B C2 14 00 3B C6 7E 03"), 3); // Y- axis
exe.setNop(exe.find("89 73 04 5F 5E 5B C2 14 00"), 3); // Y+ axis
}
if (iniUnlockCursor)
{
exe.setNop(0x00159A72, 6); // X- axis 1
exe.setNop(0x00159A8F, 6); // X+ axis 1
exe.setNop(0x00159AE5, 6); // Y+ axis 1
exe.setNop(0x00159B00, 10); // Y- axis 1 cave
exe.setNop(0x00159B19, 6); // Y- axis 1 island
exe.setNop(0x00159EAD, 6); // X- axis 2
exe.setNop(0x00159ECA, 6); // X+ axis 2
exe.setNop(0x00159EE7, 6); // Y+ axis 2
exe.setNop(0x00159F02, 10); // Y- axis 2 cave
exe.setNop(0x00159F1B, 6); // Y- axis 2 island
}
}
// ---- Main ----
int getVersion(DWORD timeDateStamp)
{
int id = wk::getGameID(timeDateStamp);
switch (id)
{
case wk::GAMEID_WA_3_6_31:
case wk::GAMEID_WA_3_7_2_1:
case wk::GAMEID_WA_3_8_CD:
return id;
default:
return 0;
}
}
BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
wk::Exe exe;
int version = getVersion(exe.FileHeader->TimeDateStamp);
if (version)
{
init(version);
patch(exe, version);
}
else
{
MessageBox(NULL, "wkUnlimiter is incompatible with your game version. Please run the 3.6.31.0, "
"3.7.2.1, or 3.8 CD release of Worms Armageddon. Otherwise, you can delete the module to remove "
"this warning.", "wkUnlimiter", MB_ICONWARNING);
}
}
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

View File

@ -3,36 +3,36 @@
namespace wk
{
Config::Config(LPCSTR fileName)
Ini::Ini(LPCSTR fileName)
{
GetModuleFileName(NULL, _filePath, MAX_PATH);
char* sepIdx = strrchr(_filePath, '\\') + 1;
strcpy_s(sepIdx, MAX_PATH - (int)(sepIdx - _filePath), fileName);
}
void Config::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const
void Ini::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const
{
result = GetPrivateProfileInt(category, key, fallback, _filePath);
}
void Config::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const
void Ini::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const
{
result = GetPrivateProfileInt(category, key, fallback, _filePath);
}
void Config::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const
void Ini::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const
{
GetPrivateProfileString(category, key, fallback, result, resultLength, _filePath);
}
void Config::set(LPCSTR category, LPCSTR key, UINT value) const
void Ini::set(LPCSTR category, LPCSTR key, UINT value) const
{
CHAR buffer[32];
sprintf_s(buffer, "%d", value);
WritePrivateProfileString(category, key, buffer, _filePath);
}
void Config::set(LPCSTR category, LPCSTR key, LPCSTR value) const
void Ini::set(LPCSTR category, LPCSTR key, LPCSTR value) const
{
WritePrivateProfileString(category, key, value, _filePath);
}

View File

@ -3,10 +3,10 @@
namespace wk
{
class Config
class Ini
{
public:
Config(LPCSTR fileName);
Ini(LPCSTR fileName);
void get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const;
void get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const;

View File

@ -93,7 +93,7 @@
<ClInclude Include="wkConfig.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WormKitTools\FrontendKitLib.vcxproj">
<ProjectReference Include="..\WormKitTools\WormKitTools.vcxproj">
<Project>{068a8647-0a66-4e39-983b-43aceac5c937}</Project>
</ProjectReference>
</ItemGroup>

View File

@ -1,21 +0,0 @@
#pragma once
#include <Windows.h>
namespace wk
{
struct Exe
{
public:
HANDLE Handle;
IMAGE_DOS_HEADER* DosHeader;
IMAGE_NT_HEADERS* NtHeader;
IMAGE_FILE_HEADER* FileHeader;
IMAGE_OPTIONAL_HEADER* OptionalHeader;
Exe(HMODULE hModule = NULL);
ULONG_PTR Offset(ULONG_PTR off);
BOOL IsCode(LPVOID ptr);
BOOL IsData(LPVOID ptr);
};
}

View File

@ -1,37 +0,0 @@
#pragma once
#include <Windows.h>
namespace wk
{
enum InsertJump
{
IJ_JUMP, // Insert a jump (0xE9) with patchJump
IJ_CALL, // Insert a call (0xE8) with patchJump
IJ_FARJUMP, // Insert a farjump (0xEA) with patchJump
IJ_FARCALL, // Insert a farcall (0x9A) with patchJump
IJ_PUSHRET, // Insert a pushret with patchJump
};
struct Patch
{
public:
ULONG_PTR position;
Patch(ULONG_PTR address, SIZE_T size);
~Patch();
void close() const;
template <class T> void write(const T& value);
static void byte(ULONG_PTR address, BYTE newValue);
static void nops(ULONG_PTR address, SIZE_T size);
static void jump(ULONG_PTR address, SIZE_T size, PVOID callee, DWORD jumpType);
private:
LPBYTE _address;
SIZE_T _size;
DWORD _oldProtect;
};
}
#include "wkPatch.inl"

View File

@ -1,30 +0,0 @@
#include "wkExe.h"
namespace wk
{
Exe::Exe(HMODULE hModule)
{
Handle = hModule == 0 ? GetModuleHandleA(NULL) : hModule;
DosHeader = (IMAGE_DOS_HEADER*)Handle;
NtHeader = (IMAGE_NT_HEADERS*)((DWORD)DosHeader + DosHeader->e_lfanew);
FileHeader = (IMAGE_FILE_HEADER*)&NtHeader->FileHeader;
OptionalHeader = (IMAGE_OPTIONAL_HEADER*)&NtHeader->OptionalHeader;
}
ULONG_PTR Exe::Offset(ULONG_PTR off)
{
return (DWORD)Handle + off;
}
BOOL Exe::IsCode(LPVOID ptr)
{
return DWORD(ptr) >= Offset(OptionalHeader->BaseOfCode)
&& DWORD(ptr) < Offset(OptionalHeader->BaseOfCode) + OptionalHeader->SizeOfCode;
}
BOOL Exe::IsData(LPVOID ptr)
{
return DWORD(ptr) >= Offset(OptionalHeader->BaseOfData)
&& DWORD(ptr) < Offset(OptionalHeader->BaseOfData) + OptionalHeader->SizeOfInitializedData + OptionalHeader->SizeOfUninitializedData;
}
}

View File

@ -1,80 +0,0 @@
#include "wkPatch.h"
#include <stdexcept>
namespace wk
{
Patch::Patch(ULONG_PTR address, SIZE_T size)
: _address(reinterpret_cast<LPBYTE>(address))
, _size(size)
, position(0)
{
if (!_address || !_size)
throw std::invalid_argument("Address and size must not be 0.");
if (!VirtualProtect(_address, _size, PAGE_EXECUTE_READWRITE, &_oldProtect))
throw std::exception("VirtualProtect failed, call GetLastError for more info.");
}
Patch::~Patch()
{
close();
}
void Patch::close() const
{
DWORD oldProtect;
if (!VirtualProtect(_address, _size, _oldProtect, &oldProtect))
throw std::exception("VirtualProtect failed, call GetLastError for more info.");
}
void Patch::byte(ULONG_PTR address, BYTE newValue)
{
wk::Patch patch(address, 1);
patch.write(newValue);
}
void Patch::nops(ULONG_PTR address, SIZE_T size)
{
wk::Patch patch(address, size);
while (size--)
patch.write<BYTE>(0x90);
}
void Patch::jump(ULONG_PTR address, SIZE_T size, PVOID callee, DWORD jumpType)
{
wk::Patch patch(address, size);
if (size >= 5 && address)
{
BYTE opSize, opCode;
switch (jumpType)
{
case IJ_PUSHRET: opSize = 6; opCode = 0x68; break;
case IJ_FARJUMP: opSize = 7; opCode = 0xEA; break;
case IJ_FARCALL: opSize = 7; opCode = 0x9A; break;
case IJ_CALL: opSize = 5; opCode = 0xE8; break;
default: opSize = 5; opCode = 0xE9; break;
}
if (size < opSize)
throw std::exception("Not enough space to patch opcode.");
patch.write(opCode);
switch (opSize)
{
case 7:
patch.write((ULONG)callee);
patch.write<WORD>(0x23);
break;
case 6:
patch.write((ULONG)callee);
patch.write<BYTE>(0xC3);
break;
default:
patch.write((ULONG)callee - address - 5);
break;
}
for (DWORD i = opSize; i < size; i++)
patch.write<BYTE>(0x90);
}
}
};

View File

@ -1,108 +0,0 @@
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "wkConfig.h"
#include "wkExe.h"
#include "wkPatch.h"
#include "wkUtils.h"
// ---- Configuration ----
BOOL cfgAllowMultiInstance;
BOOL cfgEnableAllControls;
BOOL cfgUnlockCamera;
BOOL cfgUnlockCursor;
void configure()
{
wk::Config config("wkUnlimiter.ini");
// Load INI settings.
config.get("Common", "AllowMultiInstance", cfgAllowMultiInstance, TRUE);
config.get("Frontend", "EnableAllControls", cfgEnableAllControls, TRUE);
config.get("Game", "UnlockCamera", cfgUnlockCamera, TRUE);
config.get("Game", "UnlockCursor", cfgUnlockCursor, TRUE);
// Ensure INI file has been created with default setting.
config.set("Common", "AllowMultiInstance", cfgAllowMultiInstance);
config.set("Frontend", "EnableAllControls", cfgEnableAllControls);
config.set("Game", "UnlockCamera", cfgUnlockCamera);
config.set("Game", "UnlockCursor", cfgUnlockCursor);
}
// ---- Patch ----
void patch(wk::Exe& exe, int gameVersion)
{
if (cfgAllowMultiInstance)
{
wk::Patch patch(exe.Offset(0x000E7AD1), 1);
patch.write((char)0xEB); // Requires WormKitDS as game executable loads modules after this patched out check.
}
if (cfgEnableAllControls)
{
wk::Patch patch(exe.Offset(0x001C5D26), 4);
patch.write(0x9090016A); // CWnd::EnableWindow(bEnable) -> CWnd::EnableWindow(TRUE)
}
if (cfgUnlockCamera)
{
wk::Patch::nops(exe.Offset(0x00142A89), 2); // X- axis
wk::Patch::nops(exe.Offset(0x00142A91), 2); // X+ axis
wk::Patch::nops(exe.Offset(0x00142AA7), 3); // Y- axis
wk::Patch::nops(exe.Offset(0x00142A9C), 3); // Y+ axis
}
if (cfgUnlockCursor)
{
wk::Patch::nops(exe.Offset(0x00159EAD), 6); // X- axis 1
wk::Patch::nops(exe.Offset(0x00159A72), 6); // X- axis 2
wk::Patch::nops(exe.Offset(0x00159ECA), 6); // X+ axis 1
wk::Patch::nops(exe.Offset(0x00159A8F), 6); // X+ axis 2
wk::Patch::nops(exe.Offset(0x00159B00), 10); // Y- axis 1 cave
wk::Patch::nops(exe.Offset(0x00159F02), 10); // Y- axis 2 cave
wk::Patch::nops(exe.Offset(0x00159B19), 6); // Y- axis 1 island
wk::Patch::nops(exe.Offset(0x00159F1B), 6); // Y- axis 2 island
wk::Patch::nops(exe.Offset(0x00159AE5), 6); // Y+ axis 1
wk::Patch::nops(exe.Offset(0x00159EE7), 6); // Y+ axis 2
}
}
// ---- Main ----
int getVersion(DWORD timeDateStamp)
{
int id = wk::getGameID(timeDateStamp);
switch (id)
{
case wk::GAMEID_WA_3_8_CD:
return id;
default:
return 0;
}
}
BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
wk::Exe exe;
int version = getVersion(exe.FileHeader->TimeDateStamp);
if (version)
{
configure();
patch(exe, version);
}
else
{
MessageBox(NULL, "wkUnlimiter is incompatible with your game version. Please run the 3.8 CD release of "
"Worms Armageddon. Otherwise, you can delete the module to remove this warning.", "wkUnlimiter",
MB_ICONWARNING);
}
}
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}