Want the excutable run by execve() to use my preloaded library - c

I am executing a program say A from another by first fork-ing followed by execve(). Now the problem is I would want A to use my library that I would generaly do by using LD_PRELOAD. How do I do it within execve().
Thanks

you can pass the LD_PRELOAD in envp execve's argument:
the program that gets execved, named "run":
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("%s\n",getenv("LD_PRELOAD"));
}
the program that does the execve, named "ex":
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char *const args[] = {"./run",NULL};
char *const envs[] = {"LD_PRELOAD=caca",NULL};
execve("./run",args,envs);
}
running it:
root#pinkpony:~# ./ex
ERROR: ld.so: object 'caca' from LD_PRELOAD cannot be preloaded: ignored.
caca
EDIT:
the error shown gets thrown because "caca" lib can't be preloaded for run, so it works. (I skipped the fork() part for clarity but the usage is the same)
EDIT:
doing something like:
LD_PRELOAD=caca ./ex
will not automagically preload caca lib when execve()-ing run if you're not passing it via envp execve()'s argument

If you want to use LD_PRELOAD just for program A (and not for its parent) you could load it via the shell; pass to the shell the name of the program to execute and add LD_PRELOAD to the environment.

Update
After reading the added info from the question, I'm guessing that you might have to specify a complete path, or set LD_LIBRARY_PATH as well? Since the loader is acknowledging the fact that the preload is ordered.
Otherwise, I can imagine there being a security restriction (allthough it would have to be tied to being run invoked from a login shell, which seems quite brittle to detect). Nonetheless, you may wish to try running as root (use sudo -E to keep your environment)
It would appear from this earlier question that such behaviour is the default
LD_PRELOAD affects new child even after unsetenv("LD_PRELOAD")
Have you tested it?

Related

Get pre-shebang executable path in MacOS (equivalent to getauxval(AT_EXECFN) )

For the problem described at bash - Detect if a script is being run via shebang or was specified as a command line argument - Unix & Linux Stack Exchange, we need to distinguish between cases when a script is run via shebang and as an argument to the interpreter.
An answer to that question suggests getting the pre-shebang executable name using getauxval(AT_EXECFN) -- which works, but only in Linux.
Since the Pyenv project also officially supports MacOS, we need an equivalent for that if we are to consider that solution.
I've checked Finding current executable's path without /proc/self/exe -- but both _dyld_get_image_name(0) and _NSGetExecutablePath give the post-shebang name. Here's a sample program that I used to do the checking (see the question link above on how it's used; its compilation result needs to be put in place of the python3 Bash script given in that question):
#include <stdio.h>
#include <unistd.h>
/*#include <sys/auxv.h>*/
#include <mach-o/dyld.h>
#include <sys/param.h>
#include <alloca.h>
int main(int argc, char** argv) {
//char *at_execfn = (char*)getauxval(AT_EXECFN);
//const char *at_execfn = _dyld_get_image_name(0);
char *at_execfn = (char*)alloca(MAXPATHLEN);
uint32_t at_execfn_len = MAXPATHLEN;
_NSGetExecutablePath(at_execfn,&at_execfn_len);
printf("original executable: '%s'\n",at_execfn);
for(int i=0; i<argc; i++) {
printf("'%s'\n",argv[i]);
}
execvp("python3",argv);
}
This answer is based on the following assumptions; I'm sure others will vet whether they are true, but to my understanding, they are:
Python scripts will only use the shebang if they are executed directly.
Otherwise, the first command line argument will always be python, python3, or some other variation (python3.x, etc.).
You can already get the path to the original file, which is good because you can read what the shebang says, but you don't yet know whether the shebang was used, right? Python 3.10 offers an appealing solution: sys.orig_argv, which includes all the command line arguments, not just those from the program name forward as you get with normal sys.argv.
However, I'm sure you won't be implementing a 3.10-exclusive feature into pyenv! If that is the case, you can see the older C-API Py_GetArgcArgv, whose docs simply state:
Get the original command line arguments, before Python modified them.
Either way, I think that having the file path so you can read the shebang is the first part of the puzzle. The second part is figuring out if the shebang was actually used, and I think that the answer is in the command line arguments for most cases.

run a C program at startup [Red Pitaya]

I have a C program that needs to run when I turn on my machine (Red Pitaya).
the beginning of the program presented here:
//my_test program
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "redpitaya/rp.h"
int main(int argc, char **argv){
int jj=1;
while(1) {
printf("Ready for experiment number %i\n",jj);
int i, D;
int32_t TrigDly;
and so on...
the program is executable with a run.sh file called uri_test.sh, that contains the following:
cat /opt/redpitaya/fpga/fpga_0.94.bit>/dev/xdevcfg
LD_LIBRARY_PATH=/opt/redpitaya/lib ./my_test
both files are located in a directory under /root. the program is working perfectly when run manually on PuTTY terminal-
/RedPitaya/Examples/C/Uri# ./my_test
or
/RedPitaya/Examples/C/Uri# ./uri_test.sh
I tried to follow the solution presented here :
https://askubuntu.com/questions/9853/how-can-i-make-rc-local-run-on-startup
without success.
any suggestions? Thank you.
There are several ways to have a program running at startup, and it depends upon your init subsystem (are you using systemd or a SysV-style init?).
BTW, a source program in C is not a script and you generally compile it (using gcc -Wall -Wextra -g) into some executable. In your case, you probably want to set up its rpath at build time (in particular to avoid the LD_LIBRARY_PATH madness), perhaps by passing something like -Wl,-rpath,/opt/redpitaya/lib to your linking gcc command.
Perhaps a crontab(5) entry with #reboot could be enough.
Whatever way you are starting your program at startup time, it generally is the case that its stdin, stdout, stderr streams are redirected (e.g. to /dev/null, see null(4)) or not available. So it is likely that your printf output go nowhere. You might redirect stdout in your script, and I would recommend using syslog(3) in your C program, and logger(1) in your shell script (then look also into some *.log file under /var/log/). BTW, its environment is not the same as in some interactive shell (see environ(7)...), so your program is probably failing very early (perhaps at dynamic linking time, see ld-linux.so(8), since LD_LIBRARY_PATH might not be set to what you want it to be...).
You should consider handing program arguments in your C program (perhaps with getopt_long(3)) and might perhaps have some option (e.g. --daemonize) which would call daemon(3).
You certainly should read Advanced Linux Programming or something similar.
I recommend to first be able to successfully build then run some "hello-world" like program at startup which uses syslog(3). Later on, you could improve that program to make it work with your Red Pitaya thing.

How to scp from a C program?

The following C program compiles and spits out a a directory listing of "/Users/home/tempdir" directory.
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char * argv[])
{
execl("/bin/ls", "ls", "/Users/home/tempdir");
// execl("/usr/bin/scp", "scp", "myfile.txt home#127.0.0.1:/Users/home/scpdir");
return 0;
}
This shows it is possible to execute a binary file from a C program. I am trying to figure out how to execute the "scp" binary file and input the required passphrase. How can I modify the above code to achieve a successful file transfer via scp ?
I attempted the line that is commented out above however I get the following message when it is uncommented:
usage: scp [-1246BCEpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user#]host1:]file1 ... [[user#]host2:]file2
So it's obviously trying to perform the scp without the password. How do I get around this?
While you could call scp from the command line using expect or something, you are much better off using a library if at all possible.
The general rule when developing is to prefer libraries to system executions whenever possible. It keeps your code cleaner and more portable. The exception is if you are writing a shell script.
http://www.libssh2.org/
libssh2 is a free C library that supports password authentication as well as SCP/SFTP and supports OS X.
http://curl.haxx.se/libcurl/
libcurl supports pretty much every file transfer type you could want, works on just about every platform you could want, and has bindings for pretty much every language you could want.
Short answer, I know, but you'd typically use something like Expect for automating interactive applications in this way.
It appears to me that the output you are printing is from incorrect command syntax. According to the manual page for execl (at least on Arch Linux):
The const char *arg and subsequent ellipses in the execl(), execlp(),
and execle() functions can be thought of as arg0, arg1, ..., argn.
So it would seem the correct way to call it is:
execl("/usr/bin/scp", "scp", "myfile.txt", "home#127.0.0.1:/Users/home/scpdir");
Also, if you want to avoid having to put in the password completely, SSH (which scp is built on top of) can be configured with an "authorized key," which bypasses the password. It may still ask about unknown key fingerprints, though.
An easy way to set up password-less scp access is to run (from the computer initiating the scp):
ssh-copy-id home#127.0.0.1
The above command will ask for a password once, they you shouldn't need to enter it again on subsequent ssh operations.
The following code works for me:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
execl("/usr/bin/scp", "scp", "myfile.txt", "user#IP:path/to/file", (char *)0);
return 0;
}
The following code work for me:
#include <stdio.h>
#include <unistd.h>
int main()
{
execl("/usr/bin/sshpass","sshpass","-p","typeYourPasswd","/usr/bin/scp", "scp", "/home/harekrishna/Downloads/LoRaModule/ArduinoWithMeterCode/dlmsLoraV1.ino", "harekrishna#172.29.118.18:/home/harekrishna",(char *)0);
return 0;
}
but this work for me:
#include <stdio.h>
#include <unistd.h>
int main()
{
execl("/usr/bin/sshpass","sshpass","-p","typeYourPasswd", "scp", "/home/harekrishna/Downloads/LoRaModule/ArduinoWithMeterCode/dlmsLoraV1.ino", "harekrishna#172.29.118.18:/home/harekrishna",(char *)0);
return 0;
}

Building a shell in C on Ubuntu using execvp

I'm about to write a simple shell in C on Ubuntu.
I thought about using the exevcp() function.
I'm only able to run the "ls" command, none of the other commands seems to work.
Can someone tell me why is it happening or give me an idea of a better way to build the shell?
My purpose is to build this shell; I don't understand why can't I just take the command line, and put it as it is, into the execvp() function.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc,char **argv,char **envp)
{
char* args[] = {"history" , NULL};
execvp(args[0],args);
}
Can you explain to me, please?
exec(3) can only be used to run external programs. history is a built-in in most shells, and cannot be run this way.
The execvp() system call has two arguments: the name of the program to be executed, and a pointer to a null-terminated list of strings that are the arguments to the command.
For example:
char *args[] = { "ls", "-l", "-t", "-r", ".", 0 };
execvp(args[0], args);
...report error...
You can't simply pass a whole string into execvp() and expect it to split it up; the invoking code must do that. Note that if string pointed at by the first argument to execvp() contains a slash, then the $PATH mechanism is not used to find the command. A plain name (as shown, "ls") is searched using $PATH.
You also have to remember that some commands, such as history and cd, are shell built-ins; there is no external executable that you can run to get the same effect You won't be able to run those. Other commands, such as test (aka [) are usually implemented as built-ins, but there's also usually a binary in /bin or /usr/bin that does (more or less) the same job, which you can therefore invoke.

How to use chroot function in C programming?

I know that use the command "chroot" in linux need some files or directories such as usr, bin and so on. But when I use the function chroot() in C, do I need these files?
Here is my code, which "hw.out" is a binary file which just print "Hello, world". I compiled it and run it as root, but it was failed to print "Hello, world". What else should I do? Thank you!
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int result = chroot(".");
if(result == 0)
printf("Chroot Succese.\n");
char *arrays[]={"./hw.out",NULL};
execvp("./hw.out", arrays);
return 0;
}
execvp is most likely failing, probably with ENOENT: no such file or directory, if hw.out is a dynamically linked executable.
For that to work, all the libraries required by hw.out need to be findable in the chrooted environment.
Try linking hw.out statically, and it should work. (And add error checking after execvp to see what errno is set to after the call if it returns.)
Please test that your hw.out works with command line chroot.
Perhaps hw.out is dynamically linked and is missing some libraries or ld-linux.so in the chroot directory.
Nitpicks 1, what's the point of return 0 after execvp? it never gets executed unless there is an error. I would rather have perror("can't exec"); return 1;
Nitpick 2, chroot() doesn't change working directory, although it works in your case, as you are chrooting to ".", it won't work as you expect if you later change it to chroot("somedir").
Make sure that hw.out is in the correct direct. Perhaps it is easier to have it statically linked if it is using libraries. Otherwise need to enable after chroot that it can access the dynamic libraries.

Resources