为了更好地理解您的选择,我们需要退后一步并考虑进程如何获取其 I/O。
在最低级别有系统调用序列:open(2) 来获取 FD,read(2)/write(2)(以及其他一些)来实际执行 I/O,以及 close(2) 。这些都是由内核直接提供的。要发出它们,您需要一个低级汇编命令(SYSCALL/SYSENTER 或 ARM 的 SVC)。
其上一层是系统调用包装器。上述调用(open(2) 等)通常从 libc 包装器调用,这些包装器是“隐藏”底层 SVC 调用的导出函数。
上面的一个级别是 Python/Java 的任何包装器(例如 InputStreams 等)或更高级别的语言。
如果你想拦截,你有几种选择:
LD_PRELOAD,您已经尝试过 - 这只适用于两种情况:
A) 您可以在进程开始之前执行此操作,因为 LD_* 变量由链接器解析 B)你劫持的是系统调用包装器(或其他函数)。换句话说,如果进程正在内联执行低级汇编指令,那么这是行不通的。
挂钩实际的低级汇编调用/SVC - 每次都有效,但很麻烦(需要有效调试目标进程并等待它发出系统调用,然后捕获所有系统调用,并过滤掉 read(2)。
通过重定向 FD 在内核级别挂钩 - 始终有效,但可能超出您的要求,并且还需要执行内核代码(通常通过内核模块)。这里提到只是为了完整性 - 再次,可能不适用并且矫枉过正。
系统调用的动态挂钩:使用 ptrace(2) API,因此您保持在用户模式而不进入内核模式。众所周知的 strace(1) 工具会记录系统调用,但无法拦截它们。 jtrace - http://NewAndroidBook.com/tools/jtrace.html - 可以使用简单的插件 API 来完成此操作。这可能是您阻力最小的道路。
将 FD 从目标进程重定向到注入进程:通过 LD_PRELOAD 或其他一些动态注入代码,您可以打开管道、套接字或其他 IPC 原语来代替原始 FD。 dup2(2) 对此非常有用。
根据您的具体问题 - 当标准输入是终端时 - 还有其他注意事项。也就是说,bash 或任何 shell 也可以直接操作终端 - 使用 ioctl(2) 代码(也可以查看 stty(1) 的示例)。另一个考虑因素是语法突出显示是通过 ANSI 转义序列完成的,这涉及写入终端 - (+其他诅咒)。这可能可以解释您提到的遇到的“被盗字节”。
TL;DR,就像你说的 - 以我们可以尝试的最全面的方式回答注入问题,但是你的语法突出显示的细节可能需要以不同的方式解决。更具体一些,我们也可以尝试:-)