changing command line arguments - c

I am writing a C program. It takes its arguments from commandLine. I want to change the commandLine arguments in the code. As they are defined as "const char *", I can not change them using "strcpy", "memcpy", ... Also, you know, I can not just change their type from "const char *" to "char *".
Is there any way to change them?
Thank you so much in advance.
Best regards,
Shadi.

According to C99 §5.1.2.2.1/1, the signature for main is
int main(int argc, char *argv[]) { /* ... */ }
So you are allowed to remove the const. Just don't cause a buffer overrun by strcpying in longer strings than the original arguments, or attempting to install more arguments than originally passed.
Everyone else is basically right that you should create a copy instead.

No, you are not allowed to modify those. However, there's no rule against copying them to a new buffer and using those instead.

This may be beside the point, but you can't change anything with neither strcpy() nor memcpy(); the suffix 'cpy' is short for copy (unsurprisingly.)
Regarding changing the argv pointer, you could of course change the pointer, but why? If you want the command line arguments to be something other than what's given, just ignore them, and use whatever values you prefer. Also note that argv is a parameter and thus local to main(); if it is needed elsewhere, you have to either pass it as a parameter, or save it as a global variable.

It doesn't matter if you change the const char * to char * of main function.
Only one thing you must care about is buffer overrunning of strcpy.
According to ELF spec(see figure 3-31 stack layout), http://refspecs.linuxbase.org/elf/abi386-4.pdf, function parameter area is followed by environment value area.
argv is array of pointers to each parameter, and environ is also array of pointers to each environment var=value string.
So layout is like below.
argv[0] --> 1st parameter
argv[1] --> 2nd parameter
argv[2] --> 3rd parameter
...
NULL
--------------------------------------- memory boundary between param and env
environ[0] --> 1st env_var=env_value
environ[1] --> 2nd env_var=env_value
environ[2] --> 3rd env_var=env_value
...
NULL
So if you copy very long string to argv[0], it can overrun the boundary between param and env.
In this case, you must move argv and environ memory area to another heap space like below.
I extracted below example code from Postgresql open source code. ps_status.c)
char ** new_environ;
char ** new_argv;
char ** org_argv;
int i;
new_argv = (char **) malloc((argc + 1) * sizeof(char *));
for (i = 0; i < argc; i++)
new_argv[i] = strdup(argv[i]);
new_argv[argc] = NULL;
org_argv = argv;
argv = new_argv;
new_environ = (char **) malloc((i + 1) * sizeof(char *));
for (i = 0; environ[i] != NULL; i++)
new_environ[i] = strdup(environ[i]);
new_environ[i] = NULL;
environ = new_environ;
Then, you can use getopt, getenv, etc.
They will read the values from newly allocated memory area.
In addition, if you change original parameter(org_argv[0]), you can manipulate the output name of your process in ps command. (also in top, htop utility)
See below.
$> ps -ef | grep my_proc
dplee 10855 1 0 7월28 pts/2 00:00:16 my_proc i can manipulate this name

Related

C program Changing argv programmatically gives heap corruption error after exit of program only in debug mode in Visual Studio. How to resolve?

I have a large legacy program in which programmatically argv parameter is changed programmatically in program init followed by logic that parses the parameters.
In Release mode the program terminates gracefully.
In Debug mode, the program does all the required computation and gives the correct output. But upon exit gives Heap corruption error:
Error Message:
Microsoft Visual C++ Runtime Library
Debug Error!
Program:
...sers\AMD\source\repos\ArgvOverflow\x64\Debug\ArgvOverflow.exe
HEAP CORRUPTION DETECTED: after CRT block (#62) at 0x00000259566FDC90.
CRT detected that the application wrote to memory after end of heap
buffer.
Memory allocated at
minkernel\crts\ucrt\src\appcrt\startup\argv_parsing.cpp(285).
(Press Retry to debug the application)
Abort Retry Ignore
Code:
#include <stdio.h>
int main(int argc, char **argv)
{
argc = 12;
argv[1] = "str1";
argv[2] = "str2";
argv[3] = "str3";
argv[4] = "str4";
argv[5] = "str5";
argv[6] = "str6";
argv[7] = "str7";
argv[8] = "str8";
argv[9] = "str9";
argv[10] = "str10";
argv[11] = "str11";
printf("Hello world\n");
return 0;
}
I have read several posts about modifying argv, where they claim that such modifications are legal according to C standards. I also tried the suggestion to have the line
argv[argc] = NULL;
This is not solving the problem.
It is legal to modify the characters of the argument strings. C11 5.1.2.2.1p2
The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.
It is still not allowed to access the array out of bounds, argv will have only argc + 1 elements, not as many as you try to stuff in there.
You cannot mess around with argv like this. argv is not your's, it has been allocated and filled during program start according to the command line.
If you invoke your program like this:
program a b c
then argc is 4, argv[0] points to "program", argv[1] points to "a" etc.
argv[argc] is NULL
But accessing argv[5] and beyond is undefined behaviour because you access an array out of bounds.
This SO article may help too:
How dangerous is it to access an array out of bounds?.
To solve your problem: don't access arrays out of bounds.
You are allowed to modify argc and argv, but that doesn't mean that C suddenly handles (re)allocation of these variables for you. argv will be an array of type char* argv[argc];. It will contain as many pointers as argc says that it contains, no more, no less. Similarly, the length of each string pointed at by argv[i] is as long as what the caller passed.
Example:
myprog.exe foo
This means that argc == 2 and argv will have the length 2. This cannot be changed by your program.
argv[0] will point at a modifiable string "myprog.exe", size 10+1 = 11 bytes. You can change the contents but not store anything longer than 11 bytes there.
argv[1] will point at the string "foo", size 3+1 = 4 bytes. You can change the contents but not store anything longer than 4 bytes there.
(Curiosity: it is perfectly fine and arguably the most correct way to define argv as a VLA like this:
int main (int argc, char* argv[argc]), because argv decays into a char** anyway.)
That all being said, modifying argc and argv, although allowed by the C standard, is horribly bad practice. Don't do this. Instead you should use a local variable and let it refer to argv where needed. Example:
int main (int argc, char* argv[])
{
const char* argstr [12] =
{
"str0",
"str1",
"str2",
"str3",
"str4",
"str5",
"str6",
"str7",
"str8",
"str9",
"str10",
"str11",
};
for(int i=0; i<argc; i++)
{
argstr[i] = argv[i];
}
/* always use argstr here, never argv */
return 0;
}
To solve this, I created a separate char** variable, and used that variable in the code to solve the problem.
Here is what the new code looked like:
#include <stdio.h>
int main(int argc, char **argv)
{
int nargc = 12;
char **nargv;
nargv = malloc(sizeof(char*)*nargc);
nargv[0] = malloc(1 + strlen(argv[0]));
strcpy(nargv[0], argv[0]);
nargv[1] = malloc(1 + strlen("srt1"));
strcpy(nargv[1], "srt1");
nargv[2] = malloc(1 + strlen("srt2"));
strcpy(nargv[2], "srt2");
nargv[3] = malloc(1 + strlen("srt3"));
strcpy(nargv[3], "srt3");
nargv[4] = malloc(1 + strlen("srt4"));
strcpy(nargv[4], "srt4");
nargv[5] = malloc(1 + strlen("srt5"));
strcpy(nargv[5], "srt5");
nargv[6] = malloc(1 + strlen("srt6"));
strcpy(nargv[6], "srt6");
nargv[7] = malloc(1 + strlen("srt7"));
strcpy(nargv[7], "srt7");
nargv[8] = malloc(1 + strlen("srt8"));
strcpy(nargv[8], "srt8");
nargv[9] = malloc(1 + strlen("srt9"));
strcpy(nargv[9], "srt9");
nargv[10] = malloc(1 + strlen("srt10"));
strcpy(nargv[10], "srt10");
nargv[11] = malloc(1 + strlen("srt11"));
strcpy(nargv[11], "srt11");
/* Useful code */
free(nargv[11]);
free(nargv[10]);
free(nargv[9]);
free(nargv[8]);
free(nargv[7]);
free(nargv[6]);
free(nargv[5]);
free(nargv[4]);
free(nargv[3]);
free(nargv[2]);
free(nargv[1]);
free(nargv[0]);
free(nargv);
printf("Hello world\n");
return 0;
}
It is totally plausible to have this kind of setup when doing debug.
One work around which worked for me was to set up a dummy argument list when calling the program, then repopulate argv with the desired data once you get into main.
myprog.exe dummy dummy dummy dummy dummy dummy dummy dummydummy dummydummy dummy
and inside your code
int main (int argc , char* argv[]){
argc = 12;
argv[1] = "str1";
argv[2] = "str2";
argv[3] = "str3";
argv[4] = "str4";
argv[5] = "str5";
argv[6] = "str6";
argv[7] = "str7";
argv[8] = "str8";
argv[9] = "str9";
argv[10] = "str10";
argv[11] = "str11";
printf("Hello world\n");
return 0;
}

Not printing outside of if statement

I am making my own version of make. I allow for some arguments to be passed via command line.
I am working on it in chunks. I am setting the fileName right now
I have the following code
int main(int argc, char *argv[]) {
char *fileName = NULL;
char fileFlag[2];
strcpy(fileFlag, "-f");
printf("%s\n", fileFlag);
if (argc == 1) {
fileName = (char *) malloc(sizeof("Makefile"));
strcpy(fileName, "Makefile");
printf("One arg %s\n", fileName);
}
printf("\n%s", fileName);
return 0;
}
When it runs I get the following output.
-f
One arg Makefile
Shouldn't it print another line?
The first problem is
strcpy(fileFlag, "-f");
fileFlag is one element short of the required space, you need to have 3 element-array to store "-f" along with the terminating null. In this case, as per the strcpy() behaviour,
The strcpy function copies the string pointed to by s2 (including the terminating null
character) into the array pointed to by s1. [...]
So, including the null-character, the size of the source is 3, so is needed for the destination also. Otherwise, you'll be accessing out of bound memory which invokes undefined behavior.
That said,
please see this discussion on why not to cast the return value of malloc() and family in C..
Always check for the success of malloc() before using the returned pointer.

Proper use of Char**

I am new to C and came across a Char**, for example in the getline function. I have found several topics regarding this type but none of it explained how to actually use it. I understood the differences between Char[], Char* and Char**, but how can I access the content stored in a Char**?
Could someone explain that to me? Thanks in advance!! :)
So, for example i am trying to use the getline function to extract single lines from a file and to store them:
FILE *fp = fopen(myfile,"r");
size_t fsize;
char **string;
ssize_t bytes_read =0;
while ((bytes_read = getline(string, &fsize, fp))>0) {
// How to handle the content of string now? Is every line from the File stored in the Char** now?
}
Almost always when a function asks for a char** or a ** in general, you're expected to give it the address of a pointer variable. In this case the address of a char*. The address of operator is &, thus you should call getline like this:
char *string = NULL;
size_t size = 0;
while ((bytes_read = getline(&string, &fsize, fp))>0) {
// use string here...
}
free(string);
There are of course exceptions to the rule of ** functions wanting an address of, but getline isn't one of them.
In the case of getline it requires the memory address at which to store the memory address of the first character in the line you've read.
I.e. it needs a pointer to a pointer or a char**.
Your string will be stored in *string.
Of course you can get this information from the documentation :).

input directory as command line arguement in c

i am building a program that copies directories to each other, but i can only get it to work hard coded.
i want the directory to be input by the user via a command line argument.
i have used
char SrcPath[] = argv[1];
however when i hard code it like
char SrcPath[] = "home/user/Desktop/cat"; it works fine.
but this doesn't work, can anybody explain why / suggest a way to do this?
and is there a special way that directories have to be input when used in a CLI?
Making the change to char *SrcPath = argv[1]; should work. Pointers and arrays are different things!
argv[] is an array of char pointers, therefore when you use argv[1] you are obtaining the second item in the array which is a char pointer. As James suggested if you want to store the value of argv[1] which is a memory address you have to use a identical type which in this case is a char *.
If you need to save the directory path for any sort of processing or manipulation, you will need to do something like this to store the command line argument inside a char array.
char srcPath[100];
int i;
for (i = 0; argv[1][i] != '\0'; i++) {
srcPath[i] = argv[1][i];
}
array names are pointers so using the subscript [] dereferences it. The same can be said for char arrays which is what is stored inside of argv[]

what is causing this segmentation fault? example from book

I am going through a book and I tried running this example but I receive a segmentation fault - gdb says it's when it sets argv[0] = filename;
this code is copied/pasted straight from book's downloadable code samples.
#include <unistd.h>
int main() {
char filename[] = "/bin/sh\x00";
char **argv, **envp; // arrays that contain char pointers
argv[0] = filename; // only argument is filename - segmentation fault here
argv[1] = 0; // null terminate the argument array
envp[0] = 0; // null terminate the environment array
execve(filename, argv, envp);
}
Edit: The book is Hacking: The Art of Exploitation by Jon Erickson, which has VERY good reviews. This specific example is used as the first tutorial on converting C into machine code in the shellcode section, specifically it is exec_shell.c and can be downloaded from http://nostarch.com/hacking2/htm . I imagine some context around the use of this code was necessary in order to avoid some of the negative comments below, sorry for leaving details out, and thanks for the help.
It obviously isn’t a very good book. The problem is that neither argv nor envp are initialized, so when you write to argv[0], you’re trying to overwrite some random location in memory.
Try something like this:
#include <unistd.h>
int main() {
char *filename = "/bin/sh";
char *argv[2], *envp[1];
argv[0] = filename;
argv[1] = 0;
envp[0] = 0;
execve(filename, argv, envp);
}
This alternative initializes argv and envp on the stack, with enough space to contain two pointers and one pointer respectively.
In the code above, I’ve made one additional change to repair an additional common (but, in this case, harmless) misunderstanding. The \x00 that was at the end of "/bin/sh\x00" is redundant, since in C static strings are implicitly null-terminated. "/bin/sh\x00" is a string terminated by two nulls.
Alternatively, as pointed out by caf, here is a more compact example with exactly equivalent meaning:
#include <unistd.h>
int main() {
char *filename = "/bin/sh";
char *argv[2] = { filename, 0 };
char *envp[1] = { 0 };
execve(filename, argv, envp);
}
You never allocate the "arrays of pointers" meant to go in argv and envp! What book is it, that omits such crucial steps?!
Either add argv = malloc(2 * sizeof(char*)) (and similarly for envp) before you start assigning to argv[0] and friends, or change argv's and envp's declarations to be arrays of pointers rather than pointers to pointers (the latter's quite a feasible approach, in this specific case, since you do know exactly how many pointers you need in each at the time you're writing the code -- dynamic allocation is therefore somewhat supererogatory;-).
char **argv
argv is pointing to a memory location which you are not allowed to access/write to. It is something that is better known as a wild pointer.
Looks like you need to get a better book! In this code argv is a pointer with no storage allocated to it and pointing at random memory (or probably NULL). When you dereference it with argv[0] = ..., your code ends up trying to write to random memory. Your variable declaration should be something more like:
char *argv[3], *envp[1];
I have no idea where did you get this book, but it obviously sucks. argv is an uninitialized pointer, it holds a random address. Hence accessing it will most probably lead to the access violation.
Before using such multi-level pointers, I recommend reading up on dynamic memmory allocation in C.
Whenever you use pointers, you must also think whether you need to allocate space for the data that the pointers are going to point to (as also the pointers themselves, for multi-level pointers).
For example,
char **bar;
here, bar allocates space for 1 pointer-to-pointer, ie. enough space to store one address. This is not very useful without any additional data allocation.
In reality, you should be doing:
char **bar = calloc( 2 , sizeof(char *) );
here, bar allocates space for 1 pointer-to-pointer, ie. again, space to store one address as bar, AND 2 consecutive locations for storing 2 more pointers, namely bar[0] & bar1.
char bar[0]= calloc( 10 , sizeof(char) );
here, bar[0] allocates space for storing a string of size 10 - 1 (for \0 at end).
Now, if you do a string copy:
strcpy(bar[0],"Hello!");
the final memory map comes to: (addresses in circles, contents in blocks)
Many of the people here are on the right track, but missing some of the numerous problems here.
#include <unistd.h>
int main() {
char filename[] = "/bin/sh\x00";
char **argv, **envp; // arrays that contain char pointers
argv[0] = filename; // only argument is filename - segmentation fault here
argv[1] = 0; // null terminate the argument array
envp[0] = 0; // null terminate the environment array
execve(filename, argv, envp);
}
The problems here are:
1. The pointer array of character strings is never initialized. Pointers take up space too, and thus an array of pointers needs to use malloc in c.
2. Each character pointer in your pointer array needs its own malloc statement before use.
Here is the working code, with printouts to show you what is going on:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
unsigned int i=0;
char filename[] = "/bin/sh\x00";
char **argv; // arrays that contain char pointers
argv=(char **)malloc(sizeof(char*));
argv[0]=(char *)malloc(strlen(filename)*sizeof(char));
strcpy(argv[0],filename);
printf("Arg 0 is %u chars long...\n",strlen(argv[0]));
printf("Arg 0 is ");
while (argv[0][i] != '\0') {
printf("%c",argv[0][i]);
i++;
}
printf("!\n");
free(argv[0]);
}

Resources