从链接的框架中进行的系统调用的替代或包装

huangapple go评论70阅读模式
英文:

Interposing or wrapping syscall made from a linked framework

问题

我有一个可执行文件,它链接到Mac上的一个系统框架。系统框架执行一些系统调用。我对 socketconnect 调用感兴趣,以将套接字连接重定向到其他地址。我只需要这对我的可执行文件起作用,不关心将其注入到其他进程中。

我已经尝试创建另一个动态链接库,并在我的可执行文件中使用 DYLD_INTERPOSEDYLD_INSERT_LIBRARIES。这方法相当简单,似乎按预期工作。

尽管如此,我更喜欢没有另一个动态链接库,而是有一个独立的可执行文件可以实现这一点。是否有任何方法可以做到这一点?

我看到一些关于 gcc 链接器中的 --wrap 选项和 OS X 链接器中的 -alias 选项的参考,但不确定如何以及是否可以使用它们来实现这一点。

如果这些选项行不通,还有什么其他方法可以尝试?

英文:

I have a executable, which links to one system framework on Mac. The system framework makes some system calls. I'm interested in socket and connect calls, to redirect the socket connection to some other address. I only need this to work for my executable, I don't care about injecting it in other processes.

I've experimented creating another dylib and using DYLD_INTERPOSE with DYLD_INSERT_LIBRARIES with my executable. It is fairly simple and seems to work as expected.

Though, I'd prefer not having another dylib and having a standalone executable that can achieve this. Is there any way to do this?

I saw some references to --wrap option in gcc linker, and -alias option in OS X linker, but not sure how and if they can be used to achieve this.

If not these options what else can I try?

答案1

得分: 2

Dyld interposing only works at import boundaries. If you're on any semi-recent version of macOS, then all system libraries and framework exist inside the "dyld shared cache", which means there is not gonna be any import boundaries between your target framework and libsystem_kernel.dylib.

You basically have two options:

  1. Patch the system framework.
    With the com.apple.security.cs.allow-unsigned-executable-memory entitlement, you should be able to:

    • mach_vm_protect(..., VM_PROT_READ|VM_PROT_WRITE|VM_PROT_COPY)
    • apply your patches
    • sys_dcache_flush(...)
    • mach_vm_protect(..., VM_PROT_READ|VM_PROT_EXECUTE)
    • sys_icache_invalidate(...)

    This allows you to do basically anything. Just be sure to not use the specific code on those pages while patching it. Due to the entitlement requirement, this will only work on macOS.

  2. Use hardware breakpoints.
    As long as your process still only has one thread, you can use task_set_state() to set up a debug state that will be copied to all new threads, and install an exception handler for debug exceptions.
    Here you are quite constrained by the number of hardware breakpoints available (6 on Apple Silicon) and it's slower due to the whole mach message handler, but it's less invasive and it can work on iOS too.

In either case you'll have to decide how you want to go about this. If you can afford to hook socket and connect themselves, then that's gonna be simple: just dlsym() them and you know where they are.
If you have a more complex codebase where lots of things call socket and connect and you can't filter by arguments, then you'll likely want to hook at the callsite. And that will most likely involve finding the mach_header of the framework in question and parsing it to find the segment bounds. If your code only has to work on macOS 13 and up, then you can use _dyld_get_dlopen_image_header with a handle from dlopen() - otherwise you'll have to look up a known exported symbol with dlsym(), then use dladdr() on that to get the binary header.

英文:

Dyld interposing only works at import boundaries. If you're on any semi-recent version of macOS, then all system libraries and framework exist inside the "dyld shared cache", which means there is not gonna be any import boundaries between your target framework and libsystem_kernel.dylib.

You basically have two options:

  1. Patch the system framework.
    With the com.apple.security.cs.allow-unsigned-executable-memory entitlement, you should be able to:

    • mach_vm_protect(..., VM_PROT_READ|VM_PROT_WRITE|VM_PROT_COPY)
    • apply your patches
    • sys_dcache_flush(...)
    • mach_vm_protect(..., VM_PROT_READ|VM_PROT_EXECUTE)
    • sys_icache_invalidate(...)

    This allows you to do basically anything. Just be sure to not use the specific code on those pages while patching it. Due to the entitlement requirement, this will only work on macOS.

  2. Use hardware breakpoints.
    As long as your process still only has one thread, you can use task_set_state() to set up a debug state that will be copied to all new threads, and install an exception handler for debug exceptions.
    Here you are quite constrained by the number of hardware breakpoints available (6 on Apple Silicon) and it's slower due to the whole mach message handler, but it's less invasive and it can work on iOS too.

In either case you'll have to decide how you want to go about this. If you can afford to hook socket and connect themselves, then that's gonna be simple: just dlsym() them and you know where they are.
If you have a more complex codebase where lots of things call socket and connect and you can't filter by arguments, then you'll likely want to hook at the callsite. And that will most likely involve finding the mach_header of the framework in question and parsing it to find the segment bounds. If your code only has to work on macOS 13 and up, then you can use _dyld_get_dlopen_image_header with a handle from dlopen() - otherwise you'll have to look up a known exported symbol with dlsym(), then use dladdr() on that to get the binary header.

huangapple
  • 本文由 发表于 2023年6月19日 19:04:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76506020.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定