Fail to mprotect with PROT_WRITE in Catalina

Now we want to use the system call of `

mprotect
` to access and execute any function in dynamic way. It is really usage in hot patching running process. Normaly, we use `
mprotect
` with flags like `PROT_WRITE` or `PROT_READ` and it should work in Linux and MacOS(before Catalina).


Recently we found that `

mprotect
` with `PROT_WRITE` will fail and return zero even throught we have grant the priviledges to the process. It may be the security issue so we disable the csrutil and codesign with almost all the entitlements before testing but it still fail.


Here are the related discussion: https://github.com/flutter/flutter/issues/36714https://github.com/agiledragon/gomonkey/issues/10https://stackoverflow.com/questions/60654834/using-mprotect-to-make-text-segment-writable-on-macos
https://github.com/flutter/flutter/issues/36714

Accepted Reply

Right, so you’re modifying

__TEXT
sections.

I have concerns about the long-term sustainability of this approach. macOS is evolving so that all code on the system must be signed, and changing code in memory will break its code signature. There are limited circumstances under which doing this is fine, for example, the debugger does this whenever it needs to set a breakpoint. However, I must emphasise the limited circumstances. Doing this in a debugger or development tool is fine. I’d be much more concerned if you planned to ship a system based on this technology to users.

Speaking of debuggers, the way that LLDB does this without monkeying with your segment protections is by way of

VM_PROT_COPY
. The header has this to say about its behaviour:

When a caller finds that it cannot obtain write permission on a mapped entry, the following flag can be used. The entry will be made “needs copy” effectively copying the object (using COW), and write permission will be added to the maximum protections for the associated entry.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Are you trying to generate code? Or patch existing code within the

__TEXT
section of a Mach-O image?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks @eskimo. We are trying to replace the funciton implementation by reseting the instruction to jump to new function address, kind of like JIT.


We found the workaround solution in https://stackoverflow.com/questions/60654834/using-mprotect-to-make-text-segment-writable-on-macos and that could work well in MacOS Catalina.

Right, so you’re modifying

__TEXT
sections.

I have concerns about the long-term sustainability of this approach. macOS is evolving so that all code on the system must be signed, and changing code in memory will break its code signature. There are limited circumstances under which doing this is fine, for example, the debugger does this whenever it needs to set a breakpoint. However, I must emphasise the limited circumstances. Doing this in a debugger or development tool is fine. I’d be much more concerned if you planned to ship a system based on this technology to users.

Speaking of debuggers, the way that LLDB does this without monkeying with your segment protections is by way of

VM_PROT_COPY
. The header has this to say about its behaviour:

When a caller finds that it cannot obtain write permission on a mapped entry, the following flag can be used. The entry will be made “needs copy” effectively copying the object (using COW), and write permission will be added to the maximum protections for the associated entry.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks @eskmo again and agree with your consideration. We have try to codesign with almost all the priviledges and it didn't work for this. It would be great to support this with codesign which allow root users to assign the priviledges to specified applications.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.debugger</key>
    <true/>
    <key>com.apple.security.get-task-allow</key>
    <true/>
</dict>
</plist>


By the way, we are working on https://github.com/tobegit3hub/hotpatch which allows developers to write hot patches to upgrade the functions without restart processes. Maintaining the hot patches of function implementations may be harder than static code patches but it may be useful for stable softwares which wants to run without restarting. Anyways, thanks for your help and work on MacOS.