Skip to content

Commit

Permalink
Enhancement: Further refactored the pattern searcher
Browse files Browse the repository at this point in the history
  • Loading branch information
Ceiridge committed Aug 1, 2021
1 parent 970b0d5 commit 59b6fc4
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 79 deletions.
115 changes: 42 additions & 73 deletions ChromePatcherDll/patches.cpp
Expand Up @@ -156,96 +156,65 @@ namespace ChromePatch {
for (uintptr_t i = (uintptr_t)chromeDll; i < (uintptr_t)chromeDll + (uintptr_t)chromeDllInfo.SizeOfImage; i++) {
if (VirtualQuery((LPCVOID)i, &mbi, sizeof(mbi))) {
if (mbi.Protect & (PAGE_GUARD | PAGE_NOCACHE | PAGE_NOACCESS) || !(mbi.State & MEM_COMMIT)) {
i += mbi.RegionSize;
}
else {
for (uintptr_t addr = (uintptr_t)mbi.BaseAddress; addr < (uintptr_t)mbi.BaseAddress + (uintptr_t)mbi.RegionSize; addr++) {
byte byt = *reinterpret_cast<byte*>(addr);
i += mbi.RegionSize; // Skip these regions
} else {
for (Patch& patch : patches) {
if (patch.finishedPatch) {
continue;
}

byte* searchResult = simpleSearcher->SearchBytePattern(patch, static_cast<byte*>(mbi.BaseAddress), mbi.RegionSize);

int offsetAttempt = 0;
while(!patch.successfulPatch) {
byte* patchAddr = searchResult + patch.offsets[offsetAttempt];
std::cout << "Reading address " << std::hex << patchAddr << std::endl;


for (Patch& patch : patches) {
if (patch.finishedPatch) {
continue;
if(patch.isSig) { // Add the offset found at the patchAddr (with a 4 byte rel. addr. offset) to the patchAddr
patchAddr += *reinterpret_cast<int*>(patchAddr) + 4 + patch.sigOffset;
std::cout << "New aftersig address: " << std::hex << patchAddr << std::endl;
}

for (PatchPattern& pattern : patch.patterns) {
byte searchByte = pattern.pattern[pattern.searchOffset];
if (searchByte == byt || searchByte == 0xFF) {
pattern.searchOffset++;
}
else {
pattern.searchOffset = 0;
}
if(patch.origByte == 0xFF || *patchAddr == patch.origByte) {
std::cout << "Patching byte " << std::hex << (int)*patchAddr << " to " << (int)patch.patchByte << " at " << patchAddr << std::endl;
DWORD oldProtect;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &oldProtect);

__try {
if (pattern.searchOffset == pattern.pattern.size()) {
int offset = 0;
RETRY_OFFSET_LABEL:
uintptr_t patchAddr = addr - pattern.searchOffset + patch.offsets.at(offset) + 1;
std::cout << "Reading address " << std::hex << patchAddr << std::endl;

if (patch.isSig) { // Add the offset found at the patchAddr (with a 4 byte rel. addr. offset) to the patchAddr
patchAddr += *(reinterpret_cast<std::int32_t*>(patchAddr)) + 4 + patch.sigOffset;
std::cout << "New aftersig address: " << std::hex << patchAddr << std::endl;
}

byte* patchByte = reinterpret_cast<byte*>(patchAddr);

if (*patchByte == patch.origByte || patch.origByte == 0xFF) {
std::cout << "Patching byte " << (int)*patchByte << " to " << (int)patch.patchByte << " at " << std::hex << patchAddr << std::endl;

DWORD oldProtect;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &oldProtect);

if(patch.newBytes.empty()) { // Patch a single byte
*patchByte = patch.patchByte;
} else { // Write the newBytes array if it is filled instead
const int newBytesSize = patch.newBytes.size();
memcpy_s(patchByte, newBytesSize, patch.newBytes.data(), newBytesSize);

std::cout << newBytesSize << " NewBytes have been written" << std::endl;
}


VirtualProtect(mbi.BaseAddress, mbi.RegionSize, oldProtect, &oldProtect);
patch.successfulPatch = true;
}
else {
offset++;
if (offset != patch.offsets.size()) {
goto RETRY_OFFSET_LABEL;
}

std::cerr << "Byte (" << (int)*patchByte << ") not original (" << (int)patch.origByte << ") at " << std::hex << patchAddr << std::endl;
}

pattern.searchOffset = -1;
patch.finishedPatch = true;
patchedPatches++;

if (patchedPatches >= patches.size()) {
goto END_PATCH_SEARCH_LABEL;
}
break;
}
if (patch.newBytes.empty()) { // Patch a single byte
*patchAddr = patch.patchByte;
} else { // Write the newBytes array if it is filled instead
const size_t newBytesSize = patch.newBytes.size();
memcpy_s(patchAddr, newBytesSize, patch.newBytes.data(), newBytesSize);

std::cout << newBytesSize << " NewBytes have been written" << std::endl;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
std::cerr << "SEH error" << std::endl;

VirtualProtect(mbi.BaseAddress, mbi.RegionSize, oldProtect, &oldProtect);
patch.successfulPatch = true;
} else {
offsetAttempt++;
std::cerr << "Byte (" << std::hex << (int)*patchAddr << ") not original (" << (int)patch.origByte << ") at " << patchAddr << std::endl;

if(offsetAttempt == patch.offsets.size()) {
break; // Abort trying out offsets if all didn't work
}
}
}

patch.finishedPatch = true;
patchedPatches++;
}

i = (uintptr_t)mbi.BaseAddress + mbi.RegionSize;
i = (uintptr_t)mbi.BaseAddress + mbi.RegionSize; // Skip to the next region after this one has been searched
}
}
}

END_PATCH_SEARCH_LABEL:
for (Patch& patch : patches) {
if (!patch.successfulPatch) {
std::cerr << "Couldn't patch " << patch << std::endl;
}
else {
} else {
successfulPatches++;
}
}
Expand Down
2 changes: 1 addition & 1 deletion ChromePatcherDll/patches.hpp
Expand Up @@ -39,6 +39,6 @@ namespace ChromePatch {

class PatternSearcher {
public:
virtual byte* SearchBytePattern(std::vector<Patch>& patchList, byte* startAddr, size_t length) = 0;
virtual byte* SearchBytePattern(Patch& patch, byte* startAddr, size_t length) = 0;
};
}
20 changes: 18 additions & 2 deletions ChromePatcherDll/simplepatternsearcher.cpp
Expand Up @@ -3,8 +3,24 @@
#include "simplepatternsearcher.hpp"

namespace ChromePatch {
byte* SimplePatternSearcher::SearchBytePattern(std::vector<Patch>& patchList, byte* startAddr, size_t length) {
byte* SimplePatternSearcher::SearchBytePattern(Patch& patch, byte* startAddr, size_t length) {
for(size_t i = 0; i < length; i++) {
for (PatchPattern& pattern : patch.patterns) {
const byte searchByte = pattern.pattern[pattern.searchOffset];
if (searchByte == startAddr[i] || searchByte == 0xFF) {
pattern.searchOffset++;
} else {
pattern.searchOffset = 0; // Reset found offsets if the byte differs from the pattern
}

if (pattern.searchOffset == pattern.pattern.size()) {
return startAddr + i - pattern.searchOffset + 1; // Pattern found
}
}

}

return nullptr;
}

}
}
2 changes: 1 addition & 1 deletion ChromePatcherDll/simplepatternsearcher.hpp
Expand Up @@ -7,6 +7,6 @@ namespace ChromePatch {
#endif
SimplePatternSearcher : PatternSearcher {
public:
byte* SearchBytePattern(std::vector<Patch>& patchList, byte* startAddr, size_t length) override;
byte* SearchBytePattern(Patch& patch, byte* startAddr, size_t length) override;
};
}
59 changes: 58 additions & 1 deletion ChromePatcherDllUnitTests/patterntests.cpp
Expand Up @@ -5,13 +5,70 @@

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace ChromePatch{
namespace ChromePatch {
byte SearchPattern1[10] = {0xAB, 0xCD, 0xEF, 0x00, 0xCE, 0x16, 0x1D, 0x6E, 0xFF, 0x02}; // Example bytes to search for
byte SearchPattern2[5] = { 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 };
byte SearchPatternNowhere[2] = { 0xAA, 0xAA};

struct TestBytes {
byte* Bytes;
size_t BytesLength;

byte* Pattern1Ptr;
byte* Pattern2Ptr;

~TestBytes() {
delete[] this->Bytes;
}
};

constexpr size_t BYTE_ARRAY_SIZE = 150 * 1000 * 1000; // 150 MB
constexpr size_t PATTERN1_OFFSET = BYTE_ARRAY_SIZE >> 1; // Divide by 2
constexpr size_t PATTERN2_OFFSET = (BYTE_ARRAY_SIZE >> 1) - (BYTE_ARRAY_SIZE >> 2); // *0.25

TEST_CLASS(ChromePatcherDllPatternTests) { // Test the pattern searchers
public:
TEST_METHOD(SimplePatternSearcherTest) {
const TestBytes testBytes = CreateTestBytes();
Assert::AreEqual(testBytes.BytesLength, BYTE_ARRAY_SIZE);

auto simpleSearcher = std::make_unique<SimplePatternSearcher>();
std::vector<Patch> createdPatches = CreatePatches();

Assert::AreEqual(simpleSearcher->SearchBytePattern(createdPatches[0], testBytes.Bytes, testBytes.BytesLength), testBytes.Pattern1Ptr);
Assert::AreEqual(simpleSearcher->SearchBytePattern(createdPatches[1], testBytes.Bytes, testBytes.BytesLength), testBytes.Pattern2Ptr);
}

private:
// Create two patches: Patch 1 has 2 patterns, of which one has a result. Patch 2 has 1 pattern with a result, but with offsets.
static std::vector<Patch> CreatePatches() {
std::vector<Patch> patchList;

const std::vector<byte> pattern1Vec(SearchPattern1, SearchPattern1 + ARRAYSIZE(SearchPattern1));
const std::vector<byte> patternNVec(SearchPatternNowhere, SearchPatternNowhere + ARRAYSIZE(SearchPatternNowhere));
patchList.push_back(Patch{ {{patternNVec}, {pattern1Vec}}, 0, 0 }); // Constructor hell

const std::vector<byte> pattern2Vec(SearchPattern2, SearchPattern2 + ARRAYSIZE(SearchPattern2));
const std::vector<int> pattern2Offsets = { 7 };
patchList.push_back(Patch{ {{pattern2Vec}}, 0, 0, pattern2Offsets});

return patchList;
}

static TestBytes CreateTestBytes() {
byte* byteArray = new byte[BYTE_ARRAY_SIZE];
memset(byteArray, '\0', BYTE_ARRAY_SIZE);

byte* pattern1Ptr = byteArray + PATTERN1_OFFSET;
byte* pattern2Ptr = byteArray + PATTERN2_OFFSET;

memcpy_s(pattern1Ptr, ARRAYSIZE(SearchPattern1), SearchPattern1, ARRAYSIZE(SearchPattern1));
memcpy_s(pattern2Ptr, ARRAYSIZE(SearchPattern2), SearchPattern2, ARRAYSIZE(SearchPattern2));

Assert::IsTrue(byteArray[0] == 0);
Assert::IsTrue(pattern1Ptr[0] == SearchPattern1[0]);

return TestBytes{ byteArray, BYTE_ARRAY_SIZE, pattern1Ptr, pattern2Ptr };
}
};
}
2 changes: 1 addition & 1 deletion patterns.xml
Expand Up @@ -16,7 +16,7 @@
<Defaults version="5.1">
<Patterns>
<Pattern name="Remove Extension Warning">
<!-- ShouldIncludeExtension; "ProxyOverriddenBubble.UserSelection" 2nd function in the vtable -->
<!-- DevModeBubbleDelegate::ShouldIncludeExtension; ("ProxyOverriddenBubble.UserSelection" 2nd function in the vtable) -->
<BytePattern>56 48 83 EC 20 48 89 D6 48 89 D1 E8 ? ? ? ? 89 C1</BytePattern> <!-- Chr 79-84 -->

<BytePattern>56 48 83 EC ? 48 89 D6 48 89 D1 E8 ? ? ? ? 83 F8 ? 74 ?</BytePattern> <!-- Edg 81-84 -->
Expand Down

0 comments on commit 59b6fc4

Please sign in to comment.