ptrace to change an array in another program - arrays

I'm trying to use ptrace to change another program variable at run time, in my case a dummy shell.
int main(void)
{
char buf[MAXLINE];
fgets(buf, MAXLINE, stdin);
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0;
execlp("sh", "sh", "-c", buf, (char *)0);
printf("couldn't execute: %s", buf);
exit(127);
}
In my injector app I use PTRACE_ATTACH and listen for sys_read syscalls; then PTRACE_GETREGS to try to obtain the registers that should contain the address of the "buf" array.
ptrace(PTRACE_SYSCALL, pid, 0, 0);
wait(&status);
ptrace(PTRACE_GETREGS, pid, 0, &regs);
syscall_n = regs.orig_rax;
if (syscall_n == SYS_read){
switch(syscall_status){
case STARTED:
printf("Waiting user input... ");
syscall_status = WAITING;
break;
case WAITING:
printf("\t[ OK ]\n");
syscall_status = RETURNED;
break;
default:
break;
}
}
While I can catch the correct open syscall when fgets is called, I have no idea in which of the registers I should use to obtain the address of the string array. Or if there is a better way to obtain it.
So I can later use PEEKDATA and POKEDATA to inject the modified string.
Btw, I have read the "Playing with ptrace Part I" article, but I don't understand how adapt it to my purpose.

First, fgets() is not a system call, so you're not going to catch it with ptrace. What you are going to catch is sys_read() or similar system call. Even more so, the actual sys_read() may actually be invoked with different buffer and length arguments than those passed to original fgets() because of I/O caching in stdio.
Second, to establish reliably, which registers to check you need to know the exact ABI you're using. For example, on ARM/EABI the sys_read() call will expect the buffer pointer in register "r1" (with register "r0" holding the first argument, e.g. file descriptor and register "r2" holding the length). On x86 it will be ebx/ecx/edx combo; on amd64 - rdi/rsi/rdx.
If you actually want to trace the effects of higher level functions (as opposed to system calls), you will need to dig deep into the debugging information produced by the compiler, because it's more or less the only way to do such things (you can try using the libelf library from elfutils, just like ltrace utility does). You will actually have to programmatically set breakpoints in proper places and then rely on debugging info to deduce the variable layout on stack (to get to the stack allocated buffer).
Consider using the source of an ltrace utility for a working example:
http://anonscm.debian.org/gitweb/?p=collab-maint/ltrace.git;a=tree;h=refs/heads/master;hb=refs/heads/master
By the way, if you're using a sufficiently new kernel, you may benefit from using the 2 new system calls: process_vm_readv and process_vm_writev which can manipulate remote process memory "in bulk", given only the target PID and address (this is much more convenient approach than ptrace PEEK and POKE).
http://man7.org/linux/man-pages/man2/process_vm_readv.2.html

Related

Manipulating process name and arguments by way of argv

I have a program written in C which runs on Linux only. I want to be able to change the process name as shown in the ps command. For this I am directly changing the string in argv[0] and also using prctl(PR_SET_NAME, argv[0]) call from the main thread. I also want to access the /proc/self/cmdline from dynamically loaded shared libraries and in future maybe even from other programs.
I read that for this to work, I have to use the original memory space starting at argv[0]. The ELF standard specifies that this space is \0 separated from environ space. Looking into ps_status.c from Postgres code, one can see that they are using all this space for argv strings. And really, when I memset this space to 'a', I can see over 3000 chars in ps and read it from /proc filesystem both. Problem starts when I try to use this space to dynamically (at runtime) create new arguments in this space. (I have read and from basic tests know that Chrome/Chromium does something similar - export status of it's forked processes in ps by command line arguments.) Anything which contains the NULL delimiter in space reaching into originally environment is treated as an end. (I originally had 105 chars in cmdline arguments, I am able to get 130 chars but others arguments up to this 3000 char mark are not read.) From this I gather that the system remembers the original size and is only letting me "read over" until the end of string. (Changing the char** argv pointer is no help.)
But the Chrome is somehow doing this. Looking into command_line.cc source I see no immediate way how.
Is it even possible to do this this way? And if so, how? To tell the Linux kernel that the size of argv memory and argc changed?
Thank you.
PR_SET_MM_ARG_START and PR_SET_MM_ARG_END let you do this, if you're root (more specifically, if the process has the CAP_SYS_RESOURCE capability).
Usage:
prctl(PR_SET_NAME, constructed_argv[0]);
prctl(PR_SET_MM, PR_SET_MM_ARG_START, constructed_argv, 0, 0);
prctl(PR_SET_MM, PR_SET_MM_ARG_END, end_of_constructed_argv, 0, 0);
Here's an example of well-documented usage of them from systemd:
/* Now, let's tell the kernel about this new memory */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
/* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
* below the desired start address, in which case the kernel may have kicked this back due
* to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
* action). The proper solution would be to have a prctl() API that could set both start+end
* simultaneously, or at least let us query the existing address to anticipate this condition
* and respond accordingly. For now, we can only guess at the cause of this failure and try
* a workaround--which will briefly expand the arg space to something potentially huge before
* resizing it to what we want. */
log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
(void) munmap(nn, nn_size);
goto use_saved_argv;
}
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
goto use_saved_argv;
}
} else {
/* And update the end pointer to the new end, too. If this fails, we don't really know what
* to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
* and continue. */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
}
Chrome doesn't have a special trick; rather, it's cheating. It is overwriting into the environ area, but it uses spaces instead of null bytes to separate arguments. This looks exactly the same in ps, but if you examine the /proc/PID/environ file with xxd or similar, you'll see what it's doing. This lets it basically ignore the constraint you found of "Anything which contains the NULL delimiter in space reaching into originally environment is treated as an end."

Why does printing to stderr cause segmentation fault when dealing with ucontext?

I was working on a project for a course on Operating Systems. The task was to implement a library for dealing with threads, similar to pthreads, but much more simpler. The purpose of it is to practice scheduling algorithms. The final product is a .a file. The course is over and everything worked just fine (in terms of functionality).
Though, I got curious about an issue I faced. On three different functions of my source file, if I add the following line, for instance:
fprintf(stderr, "My lucky number is %d\n", 4);
I get a segmentation fault. The same doesn't happen if stdout is used instead, or if the formatting doesn't contain any variables.
That leaves me with two main questions:
Why does it only happen in three functions of my code, and not the others?
Could the creation of contexts using getcontext() and makecontext(), or the changing of contexts using setcontext() or swapcontext() mess up with the standard file descriptors?
My intuition says those functions could be responsible for that. Even more when given the fact that the three functions of my code in which this happens are functions that have contexts which other parts of the code switch to. Usually by setcontext(), though swapcontext() is used to go to the scheduler, for choosing another thread to execute.
Additionally, if that is the case, then:
What is the proper way to create threads using those functions?
I'm currently doing the following:
/*------------------------------------------------------------------------------
Funct: Creates an execution context for the function and arguments passed.
Input: uc -> Pointer where the context will be created.
funct -> Function to be executed in the context.
arg -> Argument to the function.
Return: If the function succeeds, 0 will be returned. Otherwise -1.
------------------------------------------------------------------------------*/
static int create_context(ucontext_t *uc, void *funct, void *arg)
{
if(getcontext(uc) != 0) // Gets a context "model"
{
return -1;
}
stack_t *sp = (stack_t*)malloc(STACK_SIZE); // Stack area for the execution context
if(!sp) // A stack area is mandatory
{
return -1;
}
uc->uc_stack.ss_sp = sp; // Sets stack pointer
uc->uc_stack.ss_size = STACK_SIZE; // Sets stack size
uc->uc_link = &context_end; // Sets the context to go after execution
makecontext(uc, funct, 1, arg); // "Makes everything work" (can't fail)
return 0;
}
This code is probably a little modified, but it is originally an online example on how to use u_context.
Assuming glibc, the explanation is that fprintf with an unbuffered stream (such as stderr by default) internally creates an on-stack buffer which as a size of BUFSIZE bytes. See the function buffered_vfprintf in stdio-common/vfprintf.c. BUFSIZ is 8192, so you end up with a stack overflow because the stack you create is too small.

alloca over variable length arrays for character buffer

Consider this code (error checking removed for brevity):
int main()
{
int fd, nread;
struct stat st_buff;
/* Get information about the file */
stat("data",&st_buff);
/* Open file data for reading */
char strbuff[st_buff.st_blksize];
fd = open("data",O_RDONLY);
/* read and write data */
do {
nread = read(fd,strbuff,st_buff.st_blksize);
if (!nread)
break;
write(STDOUT_FILENO, strbuff, nread);
} while (nread == st_buff.st_blksize);
/* close the file */
close(fd);
return 0;
}
This code allocates memory on stack for buffer (if I am not misunderstanding something.) There is also alloca() function which I could have used for same purpose (I guess). I was wondering if there were any reason why I would want to choose one over other?
You'd generally want to use a VLA as you have above, because it's clean and standard, whereas alloca is ugly and not in the standard (well, not in the C standard, anyway -- it probably is in POSIX).
I'm pretty sure both are the same at machine-code level. Both take the memory from the stack. This has the following implications:
The esp is moved by an appropriate value.
The taken stack memory is probe'd.
The function will have to have a proper stack frame (i.e. it should use ebp to access other locals).
Both methods do this. Both don't have a "conventional" error handling (raise a SEH exception in Windows and whatever on Linux).
There is a reason to choose one over another if you mind about portability. VLAs are not standard IMHO. alloca seems somewhat more standard.
P.S. consider using malloca ?

linux threads and fopen() fclose() fgets()

I'm looking at some legacy Linux code which uses pthreads.
In one thread a file is read via fgets(). The FILE variable is a global variable shared across all threads. (Hey, I didn't write this...)
In another thread every now and again the FILE is closed and reopened with another filename.
For several seconds after this has happened, the thread fgets() acts as if it is continuing to read the last record it read from the previous file: almost as if there was an error but fgets() was not returning NULL. Then it sorts itself out and starts reading from the new file.
The code looks a bit like this (snipped for brevity so I hope it's still intelligible):
In one thread:
while(gRunState != S_EXIT){
nanosleep(&timer_delay,0);
flag = fgets(buff, sizeof(buff), gFile);
if (flag != NULL){
// do something with buff...
}
}
In the other thread:
fclose(gFile);
gFile = fopen(newFileName,"r");
There's no lock to make sure that the fgets() is not called at the same time as the fclose()/fopen().
Any thoughts as to failure modes which might cause fgets() to fail but not return NULL?
How the described code goes wrong
The stdio library buffers data, allocating memory to store the buffered data. The GNU C library dynamically allocates file structures (some libraries, notably on Solaris, use pointers to statically allocated file structures, but the buffer is still dynamically allocated unless you set the buffering otherwise).
If your thread works with a copy of a pointer to the global file pointer (because you passed the file pointer to the function as an argument), then it is conceivable that the code would continue to access the data structure that was orginally allocated (even though it was freed by the close), and would read data from the buffer that was already present. It would only be when you exit the function, or read beyond the contents of the buffer, that things start going wrong - or the space that was previously allocated to the file structure is reallocated for a new use.
FILE *global_fp;
void somefunc(FILE *fp, ...)
{
...
while (fgets(buffer, sizeof(buffer), fp) != 0)
...
}
void another_function(...)
{
...
/* Pass global file pointer by value */
somefunc(global_fp, ...);
...
}
Proof of Concept Code
Tested on MacOS X 10.5.8 (Leopard) with GCC 4.0.1:
#include <stdio.h>
#include <stdlib.h>
FILE *global_fp;
const char etc_passwd[] = "/etc/passwd";
static void error(const char *fmt, const char *str)
{
fprintf(stderr, fmt, str);
exit(1);
}
static void abuse(FILE *fp, const char *filename)
{
char buffer1[1024];
char buffer2[1024];
if (fgets(buffer1, sizeof(buffer1), fp) == 0)
error("Failed to read buffer1 from %s\n", filename);
printf("buffer1: %s", buffer1);
/* Dangerous!!! */
fclose(global_fp);
if ((global_fp = fopen(etc_passwd, "r")) == 0)
error("Failed to open file %s\n", etc_passwd);
if (fgets(buffer2, sizeof(buffer2), fp) == 0)
error("Failed to read buffer2 from %s\n", filename);
printf("buffer2: %s", buffer2);
}
int main(int argc, char **argv)
{
if (argc != 2)
error("Usage: %s file\n", argv[0]);
if ((global_fp = fopen(argv[1], "r")) == 0)
error("Failed to open file %s\n", argv[1]);
abuse(global_fp, argv[1]);
return(0);
}
When run on its own source code, the output was:
Osiris JL: ./xx xx.c
buffer1: #include <stdio.h>
buffer2: ##
Osiris JL:
So, empirical proof that on some systems, the scenario I outlined can occur.
How to fix the code
The fix to the code is discussed well in other answers. If you avoid the problem I illustrated (for example, by avoiding global file pointers), that is simplest. Assuming that is not possible, it may be sufficient to compile with the appropriate flags (on many Unix-like systems, the compiler flag '-D_REENTRANT' does the job), and you will end up using thread-safe versions of the basic standard I/O functions. Failing that, you may need to put explicit thread-safe management policies around the access to the file pointers; a mutex or something similar (and modify the code to ensure that the threads use the mutex before using the corresponding file pointer).
A FILE * is just a pointer to the various resources. If the fclose does not zero out those resource, it's possible that the values may make enough sense that fgets does not immediately notice it.
That said, until you add some locking, I would consider this code completely broken.
Umm, you really need to control access to the FILE stream with a mutex, at the minimum. You aren't looking at some clever implementation of lock free methods, you are looking at really bad (and dusty) code.
Using thread local FILE streams is the obvious and most elegant fix, just use locks appropriately to ensure no two threads operate on the same offset of the same file at once. Or, more simply, ensure that threads block (or do other work) while waiting for the file lock to clear. POSIX advisory locks would be best for this, or your dealing with dynamically growing a tree of mutexes... or initializing a file lock mutex per thread and making each thread check the other's lock (yuck!) (since files can be re-named).
I think you are staring down the barrel of some major fixes .. unfortunately (from what you have indicated) there is no choice but to make them. In this case, its actually easier to debug a threaded program written in this manner than it would be to debug something using forks, consider yourself lucky :)
You can also put some condition-wait (pthread_cond_wait) instead of just some nanosleep which will get signaled when intended e.g. when a new file gets fopened.

Path to binary in C

How can I get the path where the binary that is executing resides in a C program?
I'm looking for something similar to __FILE__ in ruby/perl/PHP (but of course, the __FILE__ macro in C is determined at compile time).
dirname(argv[0]) will give me what I want in all cases unless the binary is in the user's $PATH... then I do not get the information I want at all, but rather "" or "."
Totally non-portable Linux solution:
#include <stdio.h>
#include <unistd.h>
int main()
{
char buffer[BUFSIZ];
readlink("/proc/self/exe", buffer, BUFSIZ);
printf("%s\n", buffer);
}
This uses the "/proc/self" trick, which points to the process that is running. That way it saves faffing about looking up the PID. Error handling left as an exercise to the wary.
The non-portable Windows solution:
WCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, ARRAYSIZE(path));
Here's an example that might be helpful for Linux systems:
/*
* getexename - Get the filename of the currently running executable
*
* The getexename() function copies an absolute filename of the currently
* running executable to the array pointed to by buf, which is of length size.
*
* If the filename would require a buffer longer than size elements, NULL is
* returned, and errno is set to ERANGE; an application should check for this
* error, and allocate a larger buffer if necessary.
*
* Return value:
* NULL on failure, with errno set accordingly, and buf on success. The
* contents of the array pointed to by buf is undefined on error.
*
* Notes:
* This function is tested on Linux only. It relies on information supplied by
* the /proc file system.
* The returned filename points to the final executable loaded by the execve()
* system call. In the case of scripts, the filename points to the script
* handler, not to the script.
* The filename returned points to the actual exectuable and not a symlink.
*
*/
char* getexename(char* buf, size_t size)
{
char linkname[64]; /* /proc/<pid>/exe */
pid_t pid;
int ret;
/* Get our PID and build the name of the link in /proc */
pid = getpid();
if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid) < 0)
{
/* This should only happen on large word systems. I'm not sure
what the proper response is here.
Since it really is an assert-like condition, aborting the
program seems to be in order. */
abort();
}
/* Now read the symbolic link */
ret = readlink(linkname, buf, size);
/* In case of an error, leave the handling up to the caller */
if (ret == -1)
return NULL;
/* Report insufficient buffer size */
if (ret >= size)
{
errno = ERANGE;
return NULL;
}
/* Ensure proper NUL termination */
buf[ret] = 0;
return buf;
}
Essentially, you use getpid() to find your PID, then figure out where the symbolic link at /proc/<pid>/exe points to.
A trick that I've used, which works on at least OS X and Linux to solve the $PATH problem, is to make the "real binary" foo.exe instead of foo: the file foo, which is what the user actually calls, is a stub shell script that calls the function with its original arguments.
#!/bin/sh
$0.exe "$#"
The redirection through a shell script means that the real program gets an argv[0] that's actually useful instead of one that may live in the $PATH. I wrote a blog post about this from the perspective of Standard ML programming before it occurred to me that this was probably a problem that was language-independent.
dirname(argv[0]) will give me what I want in all cases unless the binary is in the user's $PATH... then I do not get the information I want at all, but rather "" or "."
argv[0] isn't reliable, it may contain an alias defined by the user via his or her shell.
Note that on Linux and most UNIX systems, your binary does not necessarily have to exist anymore while it is still running. Also, the binary could have been replaced. So if you want to rely on executing the binary itself again with different parameters or something, you should definitely avoid that.
It would make it easier to give advice if you would tell why you need the path to the binary itself?
Yet another non-portable solution, for MacOS X:
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef execURL = CFBundleCopyExecutableURL(mainBundle);
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(execURL, TRUE, (UInt8 *)path, PATH_MAX))
{
// error!
}
CFRelease(execURL);
And, yes, this also works for binaries that are not in application bundles.
Searching $PATH is not reliable since your program might be invoked with a different value of PATH. e.g.
$ /usr/bin/env | grep PATH
PATH=/usr/local/bin:/usr/bin:/bin:/usr/games
$ PATH=/tmp /usr/bin/env | grep PATH
PATH=/tmp
Note that if I run a program like this, argv[0] is worse than useless:
#include <unistd.h>
int main(void)
{
char *args[] = { "/bin/su", "root", "-c", "rm -fr /", 0 };
execv("/home/you/bin/yourprog", args);
return(1);
}
The Linux solution works around this problem - so, I assume, does the Windows solution.

Resources