Skip to content

Commit

Permalink
Add patch with masking support (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
PMheart authored Jun 23, 2022
1 parent 8542170 commit be0a2f6
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 18 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Lilu Changelog
- Allow loading on macOS 13 without `-lilubetaall`
- Added Ventura dyld shared cache pathing
- Changed SKL default ig-platform-id to KBL on macOS 13+
- Added patch with masking support

#### v1.6.0
- Dropped internal shared patcher instance grabbing API
Expand Down
67 changes: 49 additions & 18 deletions Lilu/Headers/kern_patcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,28 +592,31 @@ class KernelPatcher {
}

/**
* Simple find and replace in kernel memory.
* Find one pattern with optional masking within a block of memory
*
* @param pattern pattern to search
* @param patternMask pattern mask
* @param patternSize size of pattern
* @param data a block of memory
* @param dataSize size of memory
* @param dataOffset data offset, to be set by this function
*
* @return true if pattern is found in data
*/
static inline bool findAndReplace(void *data, size_t dataSize, const void *find, size_t findSize, const void *replace, size_t replaceSize) {
void *res;
if (UNLIKELY((res = lilu_os_memmem(data, dataSize, find, findSize)) != nullptr)) {
if (UNLIKELY(MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock) != KERN_SUCCESS)) {
SYSLOG("patcher", "failed to obtain write permissions for f/r");
return false;
}
EXPORT static bool findPattern(const void *pattern, const void *patternMask, size_t patternSize, const void *data, size_t dataSize, size_t *dataOffset);

lilu_os_memcpy(res, replace, replaceSize);

if (UNLIKELY(MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock) != KERN_SUCCESS)) {
SYSLOG("patcher", "failed to restore write permissions for f/r");
}

return true;
}
/**
* Simple find and replace with masking in kernel memory.
*/
EXPORT static bool findAndReplaceWithMask(void *data, size_t dataSize, const void *find, size_t findSize, const void *findMask, size_t findMaskSize, const void *replace, size_t replaceSize, const void *replaceMask, size_t replaceMaskSize, size_t count=0, size_t skip=0);

return false;
/**
* Simple find and replace in kernel memory.
*/
static inline bool findAndReplace(void *data, size_t dataSize, const void *find, size_t findSize, const void *replace, size_t replaceSize) {
return findAndReplaceWithMask(data, dataSize, find, findSize, nullptr, 0, replace, replaceSize, nullptr, 0, 0, 0);
}

/**
* Simple find and replace in kernel memory but require both `find` and `replace` buffers to have the same length
*/
Expand All @@ -622,6 +625,14 @@ class KernelPatcher {
return findAndReplace(data, dataSize, find, N, replace, N);
}

/**
* Simple find and replace with masking in kernel memory but require both `find` and `replace` buffers and masking buffers to have the same length
*/
template <size_t N>
static inline bool findAndReplaceWithMask(void *data, size_t dataSize, const uint8_t (&find)[N], const uint8_t (&findMask)[N], const uint8_t (&replace)[N], const uint8_t (&replaceMask)[N], size_t count, size_t skip) {
return findAndReplaceWithMask(data, dataSize, find, N, findMask, N, replace, N, replaceMask, N, count, skip);
}

private:
/**
* Jump type for routing
Expand Down Expand Up @@ -707,6 +718,26 @@ class KernelPatcher {
*/
bool routeMultipleInternal(size_t id, RouteRequest *requests, size_t num, mach_vm_address_t start=0, size_t size=0, bool kernelRoute=true, bool force=false, JumpType jumpType=JumpType::Auto);

/**
* Simple find and replace with masking in kernel memory
*
* @param data kernel memory
* @param dataSize size of kernel memory
* @param find find pattern
* @param findSize size of find pattern
* @param findMask find masking pattern
* @param findMaskSize size of find masking pattern
* @param replace replace pattern
* @param replaceSize size of replace pattern
* @param replaceMask replace masking pattern
* @param replaceMaskSize repalce masking pattern
* @param count maximum times of patching
* @param skip number of skipping times before performing replacement
*
* @return true if the finding and replacing performance is successful
*/
static bool findAndReplaceWithMaskInternal(void *data, size_t dataSize, const void *find, size_t findSize, const void *findMask, size_t findMaskSize, const void *replace, size_t replaceSize, const void *replaceMask, size_t replaceMaskSize, size_t count, size_t skip);

#ifdef LILU_KEXTPATCH_SUPPORT
/**
* Process loaded kext
Expand Down
101 changes: 101 additions & 0 deletions Lilu/Sources/kern_patcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,107 @@ bool KernelPatcher::routeMultipleShort(size_t id, RouteRequest *requests, size_t
return routeMultipleInternal(id, requests, num, start, size, kernelRoute, force, JumpType::Short);
}

bool KernelPatcher::findPattern(const void *pattern, const void *patternMask, size_t patternSize, const void *data, size_t dataSize, size_t *dataOffset) {
if (patternSize == 0 || dataSize < patternSize)
return false;

size_t currOffset = *dataOffset;
size_t lastOffset = dataSize - patternSize;

const uint8_t *d = (const uint8_t *) data;
const uint8_t *ptn = (const uint8_t *) pattern;
const uint8_t *ptnMask = (const uint8_t *) patternMask;

if (patternMask == nullptr) {
while (currOffset <= lastOffset) {
size_t i;
for (i = 0; i < patternSize; i++) {
if (d[currOffset + i] != ptn[i])
break;
}

if (i == patternSize) {
*dataOffset = currOffset;
return true;
}

currOffset++;
}
} else {
while (currOffset <= lastOffset) {
size_t i;
for (i = 0; i < patternSize; i++) {
if ((d[currOffset + i] & ptnMask[i]) != ptn[i])
break;
}

if (i == patternSize) {
*dataOffset = currOffset;
return true;
}

currOffset++;
}
}

return false;
}

bool KernelPatcher::findAndReplaceWithMask(void *data, size_t dataSize, const void *find, size_t findSize, const void *findMask, size_t findMaskSize, const void *replace, size_t replaceSize, const void *replaceMask, size_t replaceMaskSize, size_t count, size_t skip) {
if (dataSize < findSize) return false;

uint8_t *d = (uint8_t *) data;
const uint8_t *repl = (const uint8_t *) replace;
const uint8_t *replMsk = (const uint8_t *) replaceMask;

size_t replCount = 0;
size_t dataOffset = 0;

while (true) {
bool found = findPattern(find, findMask, findSize, data, dataSize, &dataOffset);
if (!found) break;

// dataOffset + findSize - 1 is guaranteed to be a valid offset here. As
// dataSize can at most be SIZE_T_MAX, the maximum valid offset is
// SIZE_T_MAX - 1. In consequence, dataOffset + findSize cannot wrap around.

// skip this finding if requested
if (skip > 0) {
skip--;
dataOffset += findSize;
continue;
}

if (UNLIKELY(MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock) != KERN_SUCCESS)) {
SYSLOG("patcher", "failed to obtain write permissions for f/r");
return false;
}

// perform replacement
if (replaceMask == nullptr) {
lilu_os_memcpy(&d[dataOffset], replace, replaceSize);
} else {
for (size_t i = 0; i < findSize; i++)
d[dataOffset + i] = (d[dataOffset + i] & ~replMsk[i]) | (repl[i] & replMsk[i]);
}

if (UNLIKELY(MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock) != KERN_SUCCESS)) {
SYSLOG("patcher", "failed to restore write permissions for f/r");
}

replCount++;
dataOffset += replaceSize;

// check replace count if requested
if (count > 0) {
count--;
if (count == 0)
break;
}
}

return replCount > 0;
}

bool KernelPatcher::routeMultipleInternal(size_t id, RouteRequest *requests, size_t num, mach_vm_address_t start, size_t size, bool kernelRoute, bool force, JumpType jump) {
bool errorsFound = false;
Expand Down

0 comments on commit be0a2f6

Please sign in to comment.