-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added actual source, Makefile and description
- Loading branch information
Showing
4 changed files
with
192 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pipe-spy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Compile pipe-spy | ||
|
||
# This is the target to exec... | ||
TARGET=possibly/relative/path/to/the/actual/target | ||
# ... with the full path... | ||
TARGET_PATH=`python -c "import os,sys; print(os.path.realpath(os.path.expanduser('$(TARGET)')))" "${1}"` | ||
|
||
pipe-spy: | ||
$(CC) -o pipe-spy -g -Wall -DTARGET=\"$(TARGET_PATH)\" pipe-spy.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,55 @@ | ||
# pipe-spy | ||
A C-program that can be injected between two processes to spy on thier communcation over pipes | ||
|
||
A C-program that can be injected between two processes to spy on their communcation over pipes. | ||
|
||
## Use case | ||
|
||
I had a situation where one program exec:ed another and communicated | ||
bi-directionally over pipes. As I was interested in learning the | ||
details of this communication I wanted to spy in that conversation. | ||
|
||
## Alternatives | ||
|
||
Actually I could not find any alternatives here. My question on | ||
stackoverflow did not get any answers and googling found none either. | ||
|
||
I'm happy to add some alternatives here, if you'd like to share. | ||
|
||
(Of course there is always `strace` but that just generates a lot of | ||
extra information that was in the way.) | ||
|
||
So I implemented this little utility. | ||
|
||
## Usage | ||
|
||
Set the TARGET variable (environment or Makefile) to point to the | ||
actual `target` and compile. | ||
|
||
Then you have to find a way to get the `caller` to exec this instead of the | ||
actual `target`. The way I used it was to create a link to `pipe-spy` | ||
with the same name as the actual target but in a directory that was | ||
earlier in the PATH. This might not always be possible, so YMMV. | ||
|
||
If the `caller` tries to exec the actual `target` this utility will | ||
instead be run. It will in turn exec the `target` and spy and log the | ||
downstream and upstream communication in a file | ||
(`/tmp/pipe-spyPID.log`). | ||
|
||
## Implementation | ||
|
||
`pipe-spy` forks three processes, two for the up- / down-stream | ||
communication and one that execs the actual `target`. | ||
|
||
The complicated part is to set up the pipes correctly. | ||
|
||
## Improvements | ||
|
||
- The handling of closed pipes is probably not correct, so if your | ||
`caller` closes the communication `pipe-spy` will probably hang. | ||
|
||
- The path to the `target` now has to be compiled in. Any ideas on how | ||
to do avoid this are most welcome (note that we can't use command | ||
line arguments, since the requirement is that the `caller` cannot be | ||
changed). | ||
|
||
PR:s are welcome. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
#include <errno.h> | ||
#include <sys/wait.h> | ||
#include <stdbool.h> | ||
|
||
#define READ_END 0 | ||
#define WRITE_END 1 | ||
|
||
FILE *logFile; | ||
|
||
/* You need to define TARGET as a compile-time constant using -DTARGET=... */ | ||
//#define TARGET "path/to/some/executable" | ||
|
||
|
||
int main(int argc, char **argv) { | ||
bool trace = false; | ||
char logFileName[100]; | ||
|
||
sprintf(logFileName, "/tmp/pipespy%d.log", getpid()); | ||
logFile = fopen(logFileName, "w"); | ||
for (int arg=0; arg<argc; arg++) | ||
fprintf(logFile, "%s ", argv[arg]); | ||
fprintf(logFile, "\n"); | ||
fflush(logFile); | ||
|
||
/* Create the pipes for the sub-spies to the target */ | ||
int downstream_pipe[2]; | ||
int upstream_pipe[2]; | ||
|
||
if (pipe(downstream_pipe) < 0) { | ||
perror("pipe(downstream_pipe)"); | ||
_exit(-1); | ||
} | ||
|
||
if (pipe(upstream_pipe) < 0) { | ||
close(downstream_pipe[READ_END]); | ||
close(downstream_pipe[WRITE_END]); | ||
perror("pipe(upstream_pipe)"); | ||
_exit(-1); | ||
} | ||
|
||
/* Fork a sub-spy to listen, log and propagate stdin */ | ||
|
||
pid_t downstream_pid = fork(); | ||
if (downstream_pid == 0) { | ||
char buffer[10000]; | ||
if (trace) { fprintf(logFile, "** downstream child to fdopen\n"); fflush(logFile); } | ||
FILE *toTarget = fdopen(downstream_pipe[WRITE_END], "w"); | ||
if (trace) { fprintf(logFile, "** downstream child did fdopen\n"); fflush(logFile); } | ||
|
||
/* Close the pipe ends we don't use */ | ||
close(downstream_pipe[READ_END]); | ||
close(upstream_pipe[READ_END]); | ||
close(upstream_pipe[WRITE_END]); | ||
|
||
/* Read from stdin which is the same as the parent has */ | ||
if (trace) { fprintf(logFile, "** downstream child to fgets\n"); fflush(logFile); } | ||
while (fgets(buffer, 10000, stdin) != NULL) { | ||
if (trace) { fprintf(logFile, "** downstream child did fgets\n"); fflush(logFile); } | ||
fprintf(logFile, "->:%s", buffer); fflush(logFile); | ||
fputs(buffer, toTarget); fflush(toTarget); | ||
} | ||
fprintf(logFile, "** downstream child got NULL\n"); | ||
_exit(1); | ||
} | ||
if (trace) { fprintf(logFile, "** parent forked downstream child to %d\n", downstream_pid); fflush(logFile); } | ||
|
||
/* Fork a sub-spy to listen, log and propagate stdout */ | ||
|
||
pid_t upstream_pid = fork(); | ||
if (upstream_pid == 0) { | ||
char buffer[10000]; | ||
if (trace) { fprintf(logFile, "** upstream child to fdopen\n"); fflush(logFile); } | ||
FILE *fromTarget = fdopen(upstream_pipe[READ_END], "r"); | ||
if (trace) { fprintf(logFile, "** upstream child did fdopen\n"); fflush(logFile); } | ||
|
||
/* Close the pipe ends we don't use */ | ||
close(downstream_pipe[READ_END]); | ||
close(downstream_pipe[WRITE_END]); | ||
close(upstream_pipe[WRITE_END]); | ||
|
||
if (trace) { fprintf(logFile, "** upstream child to fgets\n"); fflush(logFile); } | ||
while (fgets(buffer, 10000, fromTarget) != NULL) { | ||
if (trace) { fprintf(logFile, "** upstream child did fgets\n"); fflush(logFile); } | ||
fprintf(logFile, "<-:%s", buffer); fflush(logFile); | ||
/* Write to stdout which is the same as the parent has */ | ||
fputs(buffer, stdout); fflush(stdout); | ||
} | ||
fprintf(logFile, "** upstream child got NULL\n"); fflush(logFile); | ||
_exit(1); | ||
} | ||
if (trace) { fprintf(logFile, "** parent forked upstream child to %d\n", upstream_pid); fflush(logFile); } | ||
|
||
/* Fork & exec the target with stdin & stdout pipes connected to | ||
the upstream and downstream pipes */ | ||
pid_t target_pid = fork(); | ||
if (target_pid == 0) { | ||
/* In the target, so... */ | ||
/* ... connect the stdin to the downstream pipes read end... */ | ||
if (trace) { fprintf(logFile, "** target child to dup2 on read end\n"); fflush(logFile); } | ||
if (dup2(downstream_pipe[READ_END], STDIN_FILENO) == -1) exit(errno); | ||
if (trace) { fprintf(logFile, "** target child did dup2 on read end\n"); fflush(logFile); } | ||
/* ... the stdout to the upstream pipes write end... */ | ||
if (trace) { fprintf(logFile, "** target child to dup2 in write end\n"); fflush(logFile); } | ||
if (dup2(upstream_pipe[WRITE_END], STDOUT_FILENO) == -1) exit(errno); | ||
if (trace) { fprintf(logFile, "** target child did dup2 in write end\n"); fflush(logFile); } | ||
/* ... and exec ... */ | ||
if (trace) { fprintf(logFile, "** target child to execv\n"); fflush(logFile); } | ||
execv(TARGET, argv); | ||
/* ... if we get here execv failed... */ | ||
perror(TARGET); | ||
_exit(1); | ||
} | ||
if (trace) { fprintf(logFile, "** parent forked target to %d\n", target_pid); fflush(logFile); } | ||
|
||
/* Parent need to close all pipes... */ | ||
close(downstream_pipe[READ_END]); | ||
close(downstream_pipe[WRITE_END]); | ||
close(upstream_pipe[READ_END]); | ||
close(upstream_pipe[WRITE_END]); | ||
|
||
int status; | ||
waitpid(downstream_pid, &status, 0); | ||
waitpid(upstream_pid, &status, 0); | ||
waitpid(target_pid, &status, 0); | ||
} |