/** * Ashita - Copyright (c) 2014 - 2016 atom0s [atom0s@live.com] * * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to * Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. * * By using Ashita, you agree to the above license and its terms. * * Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were * made. You must do so in any reasonable manner, but not in any way that suggests the licensor * endorses you or your use. * * Non-Commercial - You may not use the material (Ashita) for commercial purposes. * * No-Derivatives - If you remix, transform, or build upon the material (Ashita), you may not distribute the * modified material. You are, however, allowed to submit the modified works back to the original * Ashita project in attempt to have it added to the original project. * * You may not apply legal terms or technological measures that legally restrict others * from doing anything the license permits. * * No warranties are given. */ #ifndef __ASHITA_AS_MEMORY_H_INCLUDED__ #define __ASHITA_AS_MEMORY_H_INCLUDED__ #if defined (_MSC_VER) && (_MSC_VER >= 1020) #pragma once #endif #include #include #include namespace Ashita { class Memory { public: /** * Determines the length of data from the given offset valid to scan with. * * @param {std::vector} pattern - The pattern to find the length within. * @param {uintptr_t} offset - The offset to begin iterating at. * @returns {uintptr_t} The length of data from the given offset. */ static uintptr_t FindLength(const std::vector>& pattern, uintptr_t offset) { // Ensure the offset is within the pattern range.. if (offset >= pattern.size()) return 0; auto len = 0; for (size_t x = 0; x < pattern.size(); x++) { // Compare the data for wildcards or null characters.. if (pattern[x].second == false || pattern[x].first == 0x00) break; len++; } return len; } /** * Finds the largest chunk of valid data within the pattern to scan for. * * @param {std::vector} pattern - The pattern to find the chunk within. * @param {int32_t [2]} output - The output data to store the offset and length of data to use. */ static void FindLargestChunk(const std::vector>& pattern, uintptr_t output[2]) { auto offset = (uintptr_t)0; auto length = (uintptr_t)FindLength(pattern, 0); auto maxlen = (uintptr_t)pattern.size(); // Loop the pattern and find its largest chunk.. for (auto x = length; x < maxlen; x++) { // Ensure the data is required.. if (pattern[x].second != true) continue; // Find the length at the given offset.. auto count = FindLength(pattern, x); if (count > length) { offset = x; length = count; } // Step past the current data.. if (count > 0) x += count - 1; } // Return the largest offset and length.. output[0] = offset; output[1] = length; } /** * Finds a pattern within the given range of data. * * @param {uintptr_t} baseAddress - The address of the data to begin searching within. * @param {uintptr_t} baseSize - The size of the data to search within. * @param {const char*} pattern - The pattern to search for. * @param {uintptr_t} offset - The offset from the found location to add to the pointer. * @param {uintptr_t} count - The result count to use if the pattern is found more than once. * @returns {uintptr_t} The address where the pattern was located, 0 otherwise. */ static uintptr_t FindPattern(uintptr_t baseAddress, uintptr_t baseSize, const char* pattern, uintptr_t offset, uintptr_t count) { // Ensure the incoming pattern is properly aligned.. if (strlen(pattern) % 2 > 0) return 0; // Convert the pattern to a vector of data.. std::vector> vpattern; for (size_t x = 0, y = strlen(pattern) / 2; x < y; x++) { // Obtain the current byte.. std::stringstream stream(std::string(pattern + (x * 2), 2)); // Check if this is a wildcard.. if (stream.str() == "??") vpattern.push_back(std::make_pair(00, false)); else { auto byte = strtol(stream.str().c_str(), nullptr, 16); vpattern.push_back(std::make_pair((uint8_t)byte, true)); } } // Find the largest chunk of valid data to scan with.. uintptr_t p[2] = { 0 }; FindLargestChunk(vpattern, p); // Prepare the required variables.. const auto psize = vpattern.size(); const auto pstart = p[0]; const auto plen = (intptr_t)p[1]; const auto pfirst = vpattern[0].first; // Build the wildcard table.. uint8_t wildcard[UCHAR_MAX + 1] = { 0 }; for (auto x = pstart; x < pstart + plen; x++) wildcard[(uint8_t)vpattern[x].first] = 1; const auto data = (uint8_t*)baseAddress; const auto size = (uint32_t)baseSize; std::vector matches; // Loop the data and look for the pattern.. for (intptr_t x = size - psize; x >= 0; x--) { auto c = data[x]; auto w = wildcard[c]; auto k = 0; // Step over wildcard characters.. while (w == 0 && x > (intptr_t)plen) { x -= plen; w = wildcard[data[x]]; k = 1; } if (k == 1) { x++; continue; } // Ensure the first byte matches.. if (c != pfirst) continue; // Validate the size.. if (x - pstart < 0 || x - pstart + psize > size) return 0; // Walk the pattern and match its data.. for (size_t y = 0; y < psize; y++) { if (y == pstart || vpattern[y].second != true) continue; if (data[x - pstart + y] != vpattern[y].first) break; if (y + 1 == psize) matches.insert(matches.begin(), (uintptr_t)(data + (x - pstart)) + offset); } } // Handle the count match.. if (count < matches.size()) return matches[count]; return 0; } /** * Obtains the calling module handle for the given address. * * @param {uintptr_t} returnAddress - The address to locate the module owner of. * @returns {HMODULE} The module handle if found, nullptr otherwise. */ static HMODULE __stdcall GetCallingModule(uintptr_t returnAddress) { if (returnAddress == 0) return nullptr; MEMORY_BASIC_INFORMATION mbi = { 0 }; if (::VirtualQuery((LPCVOID)returnAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION)) return (HMODULE)mbi.AllocationBase; return nullptr; } }; }; // namespace Ashita #endif // __ASHITA_AS_MEMORY_H_INCLUDED__