CRAW now runs on Windows 7 too - the problem was that Windows 7 has moved some functi...
[craw.git] / craw / patch.cpp
blob7ac417b6b2c67f3764180012af8615aaea622929
1 #include <boost/foreach.hpp>
2 #include <ail/memory.hpp>
3 #include <ail/string.hpp>
4 #include "patch.hpp"
5 #include "utility.hpp"
6 #include "arguments.hpp"
7 #include "python.hpp"
9 hot_patch_entry::hot_patch_entry(std::string const & module, std::string const & procedure, void * function, void * & real_function, bool enabled):
10 module(module),
11 procedure(procedure),
12 function(function),
13 real_function(real_function),
14 enabled(enabled)
18 bool hot_patch_function(std::string const & module, std::string const & function, void * function_pointer, void * & old_address)
20 HMODULE module_handle = GetModuleHandle(module.c_str());
21 if(module_handle == 0)
23 error("Unable to retrieve " + module + " module handle");
24 return false;
27 char * address = reinterpret_cast<char *>(GetProcAddress(module_handle, function.c_str()));
28 if(address == NULL)
30 error("Unable to get procedure address of " + function + " in module " + module);
31 return false;
34 old_address = address + 2;
36 std::string expected_bytes = "\x8b\xff";
37 std::string procedure_bytes;
38 procedure_bytes.assign(address, expected_bytes.length());
40 if(procedure_bytes != expected_bytes)
42 error("Unable to patch " + function + " in module " + module + " - are you running a pre Windows XP SP1 operating system by Microsoft?");
43 return false;
46 unsigned function_address = reinterpret_cast<unsigned>(function_pointer) - reinterpret_cast<unsigned>(address);
47 std::string replacement_string = ail::little_endian_string(function_address, 4);
48 std::string replacement = "\xe9" + replacement_string + "\xeb\xf9";
50 char * offset = address - 5;
52 DWORD old_protection;
53 if(VirtualProtect(offset, replacement.length(), PAGE_EXECUTE_READWRITE, &old_protection) == 0)
55 error("Unable to patch " + function + " in module " + module + " because I was unable to make it writable");
56 return false;
59 std::memcpy(offset, replacement.c_str(), replacement.length());
61 DWORD unused;
62 if(VirtualProtect(offset, replacement.length(), old_protection, &unused) == 0)
64 error("Unable to protect our patch of " + function + " in module " + module);
65 return false;
68 return true;
71 bool patch_function(std::string const & module, std::string const & procedure, void * & address, void * custom_function)
73 std::string name = module + "!" + procedure;
74 if(!procedure_lookup(module, procedure, address))
75 return false;
77 unsigned const patch_size = 5 + 5;
78 //unsigned const patch_size = 5;
80 DWORD old_protection;
81 if(!VirtualProtect(address, patch_size, PAGE_EXECUTE_READWRITE, &old_protection))
83 error("Failed to make the page of " + name + " writable");
84 false;
87 unsigned eip = reinterpret_cast<unsigned>(address) + patch_size;
88 unsigned offset = reinterpret_cast<unsigned>(custom_function) - eip;
90 char * data = reinterpret_cast<char *>(address);
92 //lea esi, [esi + 0]
93 data[0] = '\x8d';
94 data[1] = '\x76';
95 data[2] = '\x00';
97 //mov esi, esi
98 data[3] = '\x89';
99 data[4] = '\xf6';
101 //call address
102 data[5] = '\xe8';
103 unsigned * offset_pointer = reinterpret_cast<unsigned *>(data + 6);
106 data[0] = '\xe9';
107 unsigned * offset_pointer = reinterpret_cast<unsigned *>(data + 1);
110 *offset_pointer = offset;
112 DWORD unused;
113 if(!VirtualProtect(address, patch_size, old_protection, &unused))
115 error("Failed to restore the old page permissions for " + name);
116 return false;
119 return true;
122 //dangerous, this is detected by Warden due to 0xe9 and possibly the 4 address bytes! Use for singleplayer testing only!
124 bool patch_address(unsigned address, void * target)
126 std::size_t patch_size = 5;
128 LPVOID address_pointer = reinterpret_cast<LPVOID>(address);
130 DWORD old_protection;
131 if(!VirtualProtect(address_pointer, patch_size, PAGE_EXECUTE_READWRITE, &old_protection))
133 error("Failed to patch address " + ail::hex_string_32(address));
134 false;
137 unsigned eip = address + static_cast<unsigned>(patch_size);
138 *reinterpret_cast<uchar *>(address) = 0xe9;
139 *reinterpret_cast<unsigned *>(address + 1) = reinterpret_cast<unsigned>(target) - eip;
141 DWORD unused;
142 if(!VirtualProtect(address_pointer, patch_size, old_protection, &unused))
144 error("Failed to restore the old page permissions for address " + ail::hex_string_32(address));
145 return false;
148 return true;