I want to write a simple C program with hardcoded options, which does nothing else than remount root filesystem to read-only
I see, the mount() syscall takes following parameters:
mount(const char *spec, const char *node, const char *type, int flags, void *data)
I have following C code:
#include <stdio.h>
#include <errno.h>
#include <sys/mount.h>
int main(int argc, char *argv[]) {
return mount ("/dev/sda1", "/", "ext4", "MS_RDONLY", NULL);
}
I know, in place of MS_RDONLY I should use a type int. But where do I find the value corresponding to MS_RDONLY (or which ever option I need to use) ?
MS_RDONLY should be defined in mount.h, that you already included in your code. Changing "MS_RDONLY" to MS_RDONLY should do the trick.
It's #define'd in sys/mount.h.
mount ("/dev/sda1", "/", "ext4", MS_RDONLY, NULL);
Related
If dlsym is available in dynamic linking setup, I can get access to the original impl pointers using dlsym with RTLD_NEXT and use them in my overrides, e.g. as follows:
// paste these in main.c
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
int open(const char *path, int flags)
{
fprintf(stderr, "log_file_access_preload: open(\"%s\", %d)\n", path, flags);
typedef int (*orig_open_func_type)(const char *pathname, int flags);
orig_open_func_type orig_func = (orig_open_func_type)dlsym(RTLD_NEXT, "open");
return orig_func(path, flags);
}
FILE* fopen(const char *path, const char *mode)
{
fprintf(stderr, "log_file_access_preload: fopen(\"%s\", \"%s\")\n", path, mode);
typedef FILE* (*orig_fopen_func_type)(const char *path, const char *mode);
orig_fopen_func_type orig_func = (orig_fopen_func_type)dlsym(RTLD_NEXT, "fopen");
return orig_func(path, mode);
}
Is there a way to do static linking in such a way that doesn't hide the original libc/POSIX symbols and so that I can use them in my overrides? Should I create my own copy of musl *.a files with renamed original symbols? Should it work? Is there another way?
Usecase: implement redirection of file read/access functions for a custom LaTeX program (compilation process is controlled by me, statically built with musl) to read files from ISO or TAR archive (that contains a prepared TeX Directory Structure) without extraction to disk
I am trying to reset my program when it receives a SIGSEGV by using ececl() in my signal handler. But, my current program needs commandline arguments to start that I can pass via execl() + 1 extra argument "RESTART" to notify the program that it just restarted instead of a fresh start.
But how can I pass my argv[] via exec()?
Objective:
execl("./myprog","./myprog",argv[1],argv[2],...,argv[argc],"RESTART");
OR
execl("./myprog","./myprog","RESTART",argv[1],argv[2],...,argv[argc]);
Use execv():
SYNOPSIS
#include <unistd.h>
...
int execv(const char *path, char *const argv[]);
...
The execv(), execvp(), and execvpe() functions provide an array of
pointers to null-terminated strings that represent the argument list
available to the new program. The first argument, by convention,
should point to the filename associated with the file being executed.
The array of pointers must be terminated by a null pointer.
Perhaps like this:
int main( int argc, char **argv )
{
...
int rc = execv( "./myprog", argv );
}
You may need to modify specific values in argv or create an entirely new argument array to fit what you need.
You need to save argv in a global, either from main:
static char **Argv;
int main(int c, char **v) { Argv = v; //...
or from a gcc constructor:
static char **Argv;
__attribute__((constructor))
static void ctor(int c, char **v) { Argv = v; }
Then you can do what you want:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
static char **Argv;
static void hndlr(int s)
{
execv("/proc/self/exe", Argv);
_exit(127);
}
int main(int argc, char **argv)
{
Argv = argv;
struct sigaction sa = { .sa_handler = hndlr, .sa_flags = SA_NODEFER };
sigaction(SIGSEGV, &sa, 0);
sleep(1);
fputs("start\n", stderr);
//keep re-executing the same program
raise(SIGSEGV);
}
Note that without the SA_NODEFER, you'll only see the message twice, because SIGSEGV will be blocked during the second run of the executable.
While this should be defined (especially if you add a signal stack so that you can handle stack overflows with this too), wrappers scripts/programs are a safer and more robust way of doing this. With the SISEGV handler approach, you aren't really starting from scratch -- you are inheriting signal masks, effective uids/gids, workings directories, open file descriptors, etc. etc., whereas with a wrapper script you start from a well defined state.
I was playing around with symbols and function pointers recently and noticed that though the following code runs fine:
#include <stdio.h>
int main(int argc, const char * argv[]) {
printf("%p\n",printf); // <--this line makes it work
int (*printfptr)(const char * restrict, ...);
printfptr = 0x1001fe910;
(*printfptr)("Hello world\n");
return 0;
}
This does not:
#include <stdio.h>
int main(int argc, const char * argv[]) {
// printf("%p\n",printf); // <-- commenting this out breaks it
int (*printfptr)(const char * restrict, ...);
printfptr = 0x1001fe910;
(*printfptr)("Hello world\n");
return 0;
}
(EXC_BAD_ACCESS)
How come dereferencing the exact same pointer causes issues when there is no reference to printf in the code? Even this works fine:
#include <stdio.h>
int main(int argc, const char * argv[]) {
int (*printfptr)(const char * restrict, ...);
printfptr = 0x1001fe910;
(*printfptr)("Hello world\n");
return 0;
}
void *_ = printf; // <-- because of this
Why is this?
On shared objects (.so) the symbols are really resolved only at the moment of first use. By default the linker sets the option -z lazy which tells:
When generating an executable or shared library, mark it to
tell the dynamic linker to defer function call resolution to
the point when the function is called (lazy binding), rather
than at load time. Lazy binding is the default.
You can change that behaviour by providing option -z now.
man ld for all gory details.
EDIT: Resolving a symbol is done with dynamic link API on POSIX systems. Functions dlsym(), dlopen(), dlclose() and dlerror() defined in <dlfcn.h>. This edition added so that you can search for these names.
I have successfuly intercepted calls to read(),write(),open(),unlink(),rename(), creat() but somehow with exactly the same semantics intercepting stat() is not taking place. I have changed the execution environmnet using LD_PRELOAD.
Am I missing something?
The code is quite huge, which part of it will be most helpful to post so you can help?
Thanks.
Edit: I kept the interposed stat() wrapper simple to check if it works.
int stat(const char *path,struct stat *buff)
{
printf("client invoke: stat %s",path);
return 1;
}
Compile a function that calls stat(); see what reference(s) are generated (nm -g stat.o). Then you'll have a better idea of which function(s) to interpose. Hint: it probably isn't called stat().
If you are compiling with 64 bit file offsets, then stat() is either a macro or a redirected function declaration that resolves to stat64(), so you will have to interpose on that function too.
Well it was not very simple when running in linux. Gnu libc does some tricks. You need to intercept the __xstat and if you want to call the original save the call.
Here is how I got it to work
gcc -fPIC -shared -o stat.so stat.c -ldl
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static int (*old_xstat)(int ver, const char *path, struct stat *buf) = NULL;
static int (*old_xstat64)(int ver, const char *path, struct stat64 *buf) = NULL;
int __xstat(int ver, const char *path, struct stat *buf)
{
if ( old_xstat == NULL ) {
old_xstat = dlsym(RTLD_NEXT, "__xstat");
}
printf("xstat %s\n",path);
return old_xstat(ver,path, buf);
}
int __xstat64(int ver, const char *path, struct stat64 *buf)
{
if ( old_xstat64 == NULL ) {
old_xstat64 = dlsym(RTLD_NEXT, "__xstat64");
}
printf("xstat64 %s\n",path);
return old_xstat64(ver,path, buf);
}
Is there a way to enumerate environment variables and retrieve values using C?
Take a look at the environ global variable.
extern char **environ;
It might be defined in unistd.h (take a look at the environ (5) man page above).
Here's a little code demo I wrote:
#include <stdio.h>
extern char **environ;
int main()
{
for (char **env = environ; *env; ++env)
printf("%s\n", *env);
}
Here's how to use it:
matt#stanley:~/Desktop$ make enumenv CFLAGS=-std=c99
cc -std=c99 enumenv.c -o enumenv
matt#stanley:~/Desktop$ ./enumenv
ORBIT_SOCKETDIR=/tmp/orbit-matt
SSH_AGENT_PID=1474
TERM=xterm
SHELL=/bin/bash
... (so forth)
The environment information can be passed as an extra parameter to main. I don't know if it is compliant or not, but it definitely works (tested on Ubuntu). Just define the extra argument and its an array of char pointers terminated by a NULL pointer. The following will print out the lot.
#include <stdio>
int main(int argc, char *argv[], char *envp[])
{
int index = 0;
while (envp[index])
printf("%s\n", envp[index++];
}
There is a demo in the book "The Linux Programming Interface" at page 127.
Listing 6-3: Displaying the process environment
––––––––––––––––––––––––––––––––––––––––––––––––proc/display_env.c
#include "tlpi_hdr.h"
extern char **environ;
int
main(int argc, char *argv[])
{
char **ep;
for (ep = environ; *ep != NULL; ep++)
puts(*ep);
exit(EXIT_SUCCESS);
}