tags:

views:

140

answers:

4

Hey guys,

I was wondering if there was an API, however obscure it would be, that would allow someone to send data to another process's stdin stream under Mac OS X. Under Linux, if I remember correctly, you can use the filesystem in /proc to access these streams (with the correct permissions, of course).

I dunno. Mach ports, anyone?

+1  A: 

Unfortunately, I don't believe you can do that -- MacPorts are all userland, and the operation you require needs (either a lot of trickery, see below, or) kernel cooperation, which, I believe, isn't forthcoming. For example, Mac OSX Internals, a System Approach in the section on file-descriptor passing, says

The descriptor is local to the process in that it is meaningful only in the process that acquired the descriptorsay, by opening a file. In particular, a process A cannot access a file that is open in another process B by simply using the value of the descriptor representing that file in B.

and then goes on to describe how FDs are sent.

The "trickery" part would require you getting some code of yours to run (either in userland or as part of the kernel) within the other process.

For example, you could do it in userland by patching the binary file that's their executable -- find any instruction early in its startup path that's sure to be executed, and put in its stead a jump to your own code which sends the FD to your watching daemon process, executes the patched-away instruction, then jumps back to the other process's normal sequential flow.

To do it at kernel level would require similar patches to either the kernel code itself, or to code which the kernel loads and runs with entire, unverified trust (so they can hijack an unrelated process's file descriptor table entries) -- I sure hope there are no such code paths left in Mac OS X (since their main use would no doubt be by viruses, trojan horses, and other malware of all kinds) but, if there are and you can find them, this might be a more general solution than patching every single binary executable of interest.

Back to userland, another fairly general approach might be to patch dynamically loaded libraries that all processes of interest load, instead of patching the various processes' several executables.

Alex Martelli
Well, one can install a driver and hijack the kernel all day long; but that would of course require administrative permissions in the first place, with which one could basically destroy the installation anyway.
Billy ONeal
@Billy, the general problem is that the typical MacOSX user does have administrative permission and is unfortunately used to typing his password, when requested, to enable the installation of anything downloaded from the net. I don't think the installation of a driver would require anything different, would it?
Alex Martelli
@Alex: No, that's no different. That said, if a program can gain admin rights it can destroy the machine completely. Being able to redirect stdin for a process is relatively minor by comparison.
Billy ONeal
A: 

Well, technically you could mach-inject a thread into the target process and then have it send a dup of the stdin file descriptor back to you… But you probably shouldn't. :-)

What are you really trying to do?

Kaelin Colclasure
Well, I made a plugin for Adium (a chat client) that can launch processes from the text input and it sends the output to the conversation. However, I gave no way to access a launched process's `stdin` stream from Adium, so I thought it would be "fun" to inject data into `stdin` instead of working a real, viable solution from my plugin.
zneak
Anyways, writing to `stdin` simply fails with `fwrite`, and `write(0, ...)` writes to the terminal but does not make that data readable by a further `read` call. Just for science, any idea how I could work around that?
zneak
I'd recommend exploring JulesLt's suggestion below… If Adium implements an AppleScript interface, that's probably the best way to allow your plug-in to send chat messages (if I understand correctly what you're trying to do).If Adium is a Cocoa GUI app, it's not going to be reading from stdin anyway!Mach injection is an advanced technique… as is passing open file descriptors between processes. This level of Darwin-fu is not likely required for writing a simple application plug-in.
Kaelin Colclasure
@Kaelin Colclasure: my plan was rather to make a standalone program that I could invoke from my plugin to talk to another process's `stdin`. I'm not afraid of low-level-ish stuff either.
zneak
Ah, so re-reading your responses it seems it's not Adium's stdin you are trying to interpose, but that of a process that your plug-in is launching? That is a much simpler scenario, requiring only the standard Unix dance of providing the file descriptors for child processes you fork / spawn yourself. (Does that describe what you're doing accurately?)
Kaelin Colclasure
+1  A: 

Presuming that it's with the users permission (i.e. you want to capture information from a third party app, to redirect to another app, like Rogue Amoeba's audio apps or some video stream capture apps) then I'd say you either want to look at kernel extensions, or Input Managers.

(See also fscript anywhere, SIMBL and Application Enhancer - all examples of software that inject functionality into third party apps).

A lot of older techniques for user driven code injection have been restricted in 10.6 (input managers are harder to install, for instance).

If you're interested in user input rather than stdin, the replacement Input Method Kit may actually be 'good enough' - classically, Input Managers have been used to inject all sorts of code into applications.

On the other hand, if you want to do this without user permission (i.e. key logging) then you're into hacking. There probably is a chain of as yet unpatched vulnerabilities that can be combined to do what you want to do, but whoever knows it is likely to be making money from it.

JulesLt
No no, I really just want to put stuff into stdin. Not even read from it. I just want to pipe user input from my program to another program's stdin, that was launched prior to mine.
zneak
Given that AppleScript can simulate invidividual keypress and mouse events, as well as higher level actions like paste, I wonder if the AppleEvent APIs might work - it's a level higher than stdin / would only really work for Cocoa apps, but it may be possible to achieve your goal that way.
JulesLt
Well, I'm not into _capturing_ `stdin`. Rather, I'd like to inject data into it.
zneak
That's what I understood. The AppleEvent APIs let you pass events (a key-event like 'shift-key-down', a text string, an action like paste) etc, into a running program. It's at the Cocoa rather than Unix level, but if the data you want to send in is supported by the AppleEvent APIs, it could be an option. It's the supported option for controlling/scripting third party applications (limited as it is)
JulesLt
Oh, I see. I was too sleepy when I read your comment, I suppose. Being at the Cocoa rather than UNIX level might be a problem: communication through `stdin` is largely not the same as communication by keystrokes. I'm pretty sure I'm not typing into Safari's standard input to write this comment; and I'm pretty sure UNIX CLI programs don't respond to keystrokes themselves.
zneak
No - the Apple Event interface isn't going to work directly for CLI apps, only Cocoa ones (I was reading your comment re.scripting Adium at the time, and this is a solution that could work). It just provides a relatively simple / relatively well supported way for controlling Cocoa apps. The more I understand what you require, the more I think your only option will be a Kernel Extension (with all the risk that entails) http://developer.apple.com/mac/library/documentation/Darwin/Conceptual/KernelProgramming/boundaries/boundaries.html#//apple_ref/doc/uid/TP30000905-CH217-BEHJDFCA
JulesLt
A: 

Just a thought, but couldn't you make a pipe, and redirect that (named) pipe to the standard input of the process when starting that process?

Roughly somethink like

mkfifo MYPIPE
Prog < MYPIPE
echo "test" > MYPIPE
extraneon
I'm not the one launching the process, so I can't do this.
zneak
So you want your Adium plug-in to interpose the input of some other running command-line process? You're probably back to looking at mach-injection again…
Kaelin Colclasure