scanf(), Linux's shell input in handled differently, why? - c

I got the following.
my-app.c file:
char username[USERNAME_MAX_LEN] = "\0";
char password[PASSWORD_MAX_LEN] = "\0";
scanf("%s %s", username, password);
printf("username-len: %ld, password-len: %ld\n", strlen(username), strlen(password));
credentials.data file:
jdons f4$dsef35fs
So:
$ ./my-app < credentials.data
username-len: 0, password-len: 0
and:
$ cat credentials.data | ./my-app
username-len: 5, password-len: 10
Why in the both ways the input is handled differently? What is the difference to be so?
What is the right way to use scanf() to be able to handle both cases in the same way?

This line:
scanf("%s %s", username, password);
is inherently unsafe (unless you have complete control over what will appear on your program's standard input). The "%s" format says to read an arbitrarily long sequence of non-whitespace characters. However long the target array is, a sufficiently long word (say, caused by your cat sitting on the keyboard) will overflow it.
You can use a length modifier to limit the size of the input. For example (untested):
scanf("%36s %36s", username, password);
or, better:
scanf("%*s, %*s", USERNAME_MAX_LEN, username, PASSWORD_MAX_LEN, password);
But it's probably better to use fgets() to read an entire line at a time, and then use, say, sscanf() to process the line after you've read it.
And there's a possible problem in your printf call:
printf("username-len: %ld, password-len: %ld\n",
strlen(username),
strlen(password));
strlen() returns a result of type size_t, but "%ld" requires an argument of type long int. If your system supports it, you can use "%zu" to print a value of type size_t, but that's not 100% portable. Or you can convert the size_t value to, say, unsigned long:
printf("username-len: %lu, password-len: %lu\n",
(unsigned long)strlen(username),
(unsigned long)strlen(password));
It's possible, but not very likely, that that could cause non-zero size_t values to be displayed as 0.

Barankin,
Hmmm... that's an interesting behavior.
Both standard input indirection techniques work (as expected) for me...
landkrc#lasun175:/home/user/landkrc/crap
$ cat lg.c
#include <stdio.h>
#include <strings.h>
#define USERNAME_MAX_LEN 36
#define PASSWORD_MAX_LEN 36
int main(int argc, char *argv[])
{
printf("Hello, world\n");
char username[USERNAME_MAX_LEN];
char password[PASSWORD_MAX_LEN];
*username = 0;
*password = 0;
scanf("%s %s", username, password);
printf("username-len: %ld, password-len: %ld\n", strlen(username), strlen(password));
return 0;
}
landkrc#lasun175:/home/user/landkrc/crap
$ cc -V
cc: Sun C 5.8 2005/10/13
usage: cc [ options] files. Use 'cc -flags' for details
landkrc#lasun175:/home/user/landkrc/crap
$ cc -o lg lg.c
landkrc#lasun175:/home/user/landkrc/crap
$ echo '12345678 1234567890
> ' >data.txt
landkrc#lasun175:/home/user/landkrc/crap
$ lg <data.txt
username-len: 8, password-len: 10
landkrc#lasun175:/home/user/landkrc/crap
$ cat data.txt | lg
username-len: 8, password-len: 10
Maybe you just need an end-of-line-character on the end of your credentials.data file?
Cheers. Keith.

Check if your credentials.data contains a a newline character at the end? cat command appends it after the last line of file automatically.

As much as I try, I can't manage to reproduce the same problem as you. I even manually removed the end of line byte from the credentials.data file and it still works fine (as it should). What version of Linux or the shell are you running?

Related

turning an array into a variable number of arguments

In pseudo code, I want put an arbitrary number of arguments to printf depending on the length of the argv, where the argv[1] is the format string.
int main(int argc, char *argv[]) {
printf(argv[1], argv[2], ...);
}
Uses can call the program as ./prog "%s %s" a b, ./prog "%s %s %s" a b c, and so on.
Could anybody let me know how to achieve this in C?
You need a loop for this:
int main(int argc, char *argv[])
{
int i;
for (i=1;i<argc;i++) {
printf("%s", argv[i]);
}
}
Here's something I just hacked together right now, it does does minimal parsing of the string and leaves most of it up to printf. It should also work with any number of arguments. Of course, since arguments are passed as char *s through the command line, this will only work with %s and its variants (and %%, but not sure if that counts as a format specifier).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s <format string>[ <args>]\n", argv[0]);
return 1;
}
// These pointers will constantly jump from format spec. to format spec.
char *last_fmt = argv[1], *next_fmt, *next_next_fmt;
char *buf = NULL; // a buffer to hold a substring of argv[1]
unsigned i = 2; // Used to iterate over argv[2+]
while (1)
{
next_fmt = last_fmt - 2;
do
{
if ((next_fmt = strchr(next_fmt + 2, '%')) == NULL)
{
/* Your compiler may warn about this line specifically (it did for me),
but rest assured that there are indeed no format specifiers
here really, printf is just needed for printing "%%"s as "%"s */
printf(last_fmt);
return 0;
}
} while (next_fmt[1] == '%');
next_next_fmt = next_fmt - 1;
do
{
if ((next_next_fmt = strchr(next_next_fmt + 2, '%')) == NULL)
{
printf(last_fmt == argv[1] ? last_fmt : next_fmt,
argv[i]);
return 0;
}
} while (next_next_fmt[1] == '%');
buf = malloc(next_next_fmt - last_fmt + 1);
memcpy(buf, last_fmt, next_next_fmt - last_fmt);
buf[next_next_fmt - last_fmt] = '\0';
printf(buf, argv[i]);
free(buf);
++i;
last_fmt = next_next_fmt;
}
}
An example of running:
./a.out "Hello %.2s World! %s" "foo" "bar"
Hello fo World! bar
./a.out "Hello %10s World!" "foo" "bar"
Hello foo World!
./a.out "Hello %5.2s World!" "random"
Hello ra World!
./a.out
Usage: ./a.out <format string>[ <args>]
./a.out "Hello %%s World %s" "a"
Hello %s World a
./a.out "%s %s %s" "a" "b" "c"
a b c
You could build upon this yourself, but if you want to handle other format specifiers, you'll have to do actual parsing of the string. At that point, you would basically be creating another printf.
You also might be a bit worried about the use of a not-string-literal passed to printf, but this is safe. There is guaranteed to be exactly 1 format specifier in each place I use printf (except in the first do loop, but there it is guaranteed to not have any arguments).
how to achieve this in C?
C language does not have reflection. You can't "dynamically create function calls" or inspect and then change your own source code. You have to know at compilation time how many arguments you are passing to a function. While it is simple to do printf("%s %s", "a", "b) inside C language, if you pass the same data to a program that was written in C language you have parse the data and write the logic yourself.
Such parser would take the string "%s %s" find the %s sequences and replace them for the string "a" and "b" and also print the space in between, etc. That parser has to be written in C and is basically a duplication of what printf program (not printf() C function) does. You may want to read some implementations of printf program: ex. coreutils printf.c or freebsd printf.c.
This isn't a great idea to begin with, it will be super-vulnerable to all manner of exploits, typos and bugs. But if you insist, you could do a dirty hack as follows:
Assuming the format string in argv[1] is %s %s %s, then each we can divide this string length by 3 to get the number of strings. Save for the final one, which isn't followed by a trailing space. So strlen(argv[1]) + 1 then divide by 3:
#define STR_N ((strlen(argv[1])+1)/3)
Next up we can take advantage of printf ignoring trailing arguments not corresponding to the format string. So we could do printf(argv[1], argv[2], argv[3]); just fine without actually passing that many arguments, long as the format string contains the correct amount of conversion specifiers. For example:
#define ARGV_LIST \
argv[2],\
argv[3],\
argv[4],\
argv[5],\
argv[6],\
argv[7],\
argv[8],\
argv[9]\
printf(argv[1], ARGV_LIST);
Then cook up something to convert the indices and make sure that array out of bounds never occurs:
#include <stdio.h>
#include <string.h>
#define STR_N ((strlen(argv[1])+1)/3)
#define INDEX(n) (STR_N>n? (n+2) : 0)
#define ARGV_LIST \
argv[INDEX(0)],\
argv[INDEX(1)],\
argv[INDEX(2)],\
argv[INDEX(3)],\
argv[INDEX(4)],\
argv[INDEX(5)],\
argv[INDEX(6)],\
argv[INDEX(7)],\
argv[INDEX(8)],\
argv[INDEX(9)]\
int main(int argc, char *argv[])
{
printf(argv[1], ARGV_LIST);
return 0;
}
Tested in Windows with prog.exe "%s %s %s %s %s" hello this is a test gives output:
hello this is a test

C Program Won't Print Line, Already tried flush

The program is supposed to get user first name and last name and then print them as last name, first name. The program stops immediately after the second input. I tried fflush (stdout) but that didn't seem to work (I might have done it incorrectly).
#include "stdafx.h"
#include <iostream>
using namespace System;
using namespace std;
int main()
{
char First[30], Last[30];
printf("Please type in your First Name: ");
scanf("%s",&First);
fflush(stdout);
printf("Please type in your Last Name: ");
scanf("%s",&Last);
printf("%s %s", Last, First);
printf("pause");
return 0;
}
C++ version of your program :
#include <iostream>
using namespace std;
int main()
{
string first, last;
cerr << "Please type in your First Name: ";
if (! (cin >> firs))
return -1;
cerr << "Please type in your Last Name: ";
if (! (cin >> last))
return -1;
cout << last << ' ' << first << endl;
return 0;
}
Compilation and execution :
pi#raspberrypi:/tmp $ g++ -pedantic -Wextra i.cc
pi#raspberrypi:/tmp $ ./a.out
Please type in your First Name: aze
Please type in your Last Name: qsd
qsd aze
pi#raspberrypi:/tmp $
I check names was enter (no EOF), I use cerr to be sure to flush the messages without writing endl
And the C version :
#include <stdio.h>
int main()
{
char first[30], last[30];
fprintf(stderr, "Please type in your First Name: ");
if (scanf("%29s", first) != 1)
return -1;
fprintf(stderr, "Please type in your Last Name: ");
if (scanf("%29s", last) != 1)
return -1;
printf("%s %s\n", last, first);
return 0;
}
Compilation and execution:
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra i.c
pi#raspberrypi:/tmp $ ./a.out
Please type in your First Name: aze
Please type in your Last Name: qsd
qsd aze
I limit the size in the scanf to not write out of the arrays, I check scanf was able to read the names, I also use stderr to be sure to flush the message without writing '\n'
first and last are array, it is useless to use '&' in the scanf to give their address
Note these versions do not allow to enter composed names using spaces, to allow that all the line need to be read
It helps to think about the purpose of the fflush() call. There is pending data that you want the user to see so they know what to type.
Let us consider the first query (printf, scanf, flush). The printf() puts data into a buffer. The scanf() then reads the user's response. The flush() won't execute until after the user has typed something.
Those three calls are in the wrong order. I'll leave fixing that as an exercise for the reader.
Now consider the next query (printf, scanf). The printf() puts data into a buffer. The scanf() reads the user's response, but the user won't yet have seen the "... Last Name:" prompt.
Clearly there is also an error in that block. Again, I'll leave fixing that as an exercise for the reader. Hint: if you fixed the first error that should help you understand the second.
By the way, scanf() doesn't guard against overflowing the First[] and Last[] arrays. That isn't necessary to answer your original question, but I mention it because even after you have fixed the code it will remain unsafe.
The fflush(stdout); you have doesn't help because it's too early in the code as it doesn't help with flushing the latter printfs.
You could use \n to flush as well. But this may not work if your output device isn't interactive e.g. redirected to a file.
You also have another problem with scanf() format specifiers: First and Last, being arrays, decay into pointers when passed to scanf. So you are passing the wrong type of arguments to scanf - just drop the &'s from scanf calls.
So your program could simply be:
#include <stdio.h>
int main(void)
{
char First[30], Last[30];
printf("Please type in your First Name: ");
scanf("%s", First);
fflush(stdout);
printf("Please type in your Last Name: ");
scanf("%s", Last);
fflush(stdout);
printf("%s %s\n", Last, First);
fflush(stdout);
getchar();
return 0;
}
All the fflush(stdout) calls may not be necessary if you could use \n in all printf calls because you're likely using an interactive terminal.
If you are using C++, you should really be using iostream for I/O. If nothing else, scanf is a terrible, has many problems, and should be avoided.

Basic script in C (String display)

I'm trying to create a simple script on my server, basically I would like to sent a string and display it via system function...
#include <stdio.h>
int main()
{
char txt[100];
printf("Insert a text: ");
fgets(txt, 100, stdin);
system("echo %s"), txt;
return 0;
}
Rght now I'm not getting any string just "%s"
any idea why?
system("echo %s"), txt;
This isn't doing what you think; it's an expression which evaluates to txt. Since evaluating txt has no side effects, and since you're not capturing the result of the expression anywhere, adding , txt after the system call essentially does nothing. See this question for some information on the "comma"-operator in C.
Moreover, system doesn't support the use of printf-style format specifiers, so the %s in your string literal doesn't have any special meaning; it's just going to be echoed exactly as written, as you've seen. If you want to construct a command at runtime for use with system, you will have to do so with sprintf or similar.
The prototype to system() is:
int system(const char * command);
From man 3 system:
executes the shell command specified in command
From this we can safely assume s refers to a C-"string".
So prepare the string using for example snprintf():
char s[1024];
snprintf(s, 1024 -1, "echo %s", txt); /* -1 for the C-"string"'s 0-terminator */
Then pass it:
system(s);
Instead of system("echo %s"), txt; try this:
printf("%s", txt);
the system statement will not format the output, like printf.
suggest using:
#include <stdio.h>
#include <stdlib.h> // system()
#include <string.h> // strcpy(), strcat()
#define BUF_LEN (100)
int main()
{
char output[10+BUF_LEN] = "echo ";
char txt[BUF_LEN] = {'\0'};
printf("Insert a text: ");
fgets(txt, BUF_LEN, stdin);
strcat( output, txt );
system( output );
return 0;
}
The above code works very nicely, however;
do not include any command separators, semicolons, or other characters that would be interpreted by the shell in the input string.

How to read data from unknown input type (filestream or stdin) C

I wanted to know how to read data from an unknown source of input, meaning I don't know if the user is going to just type a sentence or is he going to give me some text file.
I've tried using fscanf since I've read it is meant for unformatted input type
this is my code, Im suppose to get some type of input(file or just a sentence (echo bla bla bla) and "int" and print only the "int" first words. The program should be used for piping meaning the command would look like that :
There are 2 ways to ways of using the program:
1.echo "blabla" | myprog 2 (number of words to print)
2.cat input.txt | myprog 2 (number of words to print)
The problematic line is line 16, I tried using fscanf
Thanks!
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <string.h>
4 #include <stdlib.h>
5
6
7 int main(int argc, char *argv[]){
8 char *words[32];
9 int numofwords = atoi(argv[2]);
10 int i=0;
11 int len;
12 char *word = malloc (32 * sizeof(char));
13 char c;
14 while (i<=numofwords){
15 if ((c = getc (argv[1])) != EOF){
16 fscanf(argv[1],"%s",&word);
17 len = strlen (word);
18 words[i] = malloc ((len+1) * sizeof(char));
19 i++
20 }
21 printf(words[i]);
22 }
23 return 0;
24 }
25
May be I am correctly understood your need.
I am not rectifying your code but writing my own.
Below is simple code that read from console: code: main.c
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
if(argc!=2){
printf("\n wrong number of argument\n");
exit(1);
}
int numofwords = atoi(argv[1]);
char buffer[128];
//printf("%d\n",numofwords);
while(numofwords--){
scanf("%s",buffer);
printf("%s\n",buffer);
}
return 0;
}
How does it works:
~$ gcc main.c -o main
execute:
:~$ ./main
wrong number of argument
:~$ ./main 2
grijesh
grijesh
yourname
yourname
:~$
I hope its understood to you. the program simply read (scan) from console and print out to console. The while loop runs for number of time you pass on command line input.
Now, A text file dumy.txt a input file:
:~$ cat dumy.txt
yourname
myname
hisname
hername
:~$
Now see what you want to achieve through you code:
:~$ cat dumy.txt | ./main 2
yourname
myname
:~$
If you want to pass through echo :
:~$ echo $'one\ntwo\nthree' | ./main 2
one
two
:~$
Is this you want?
If yes:
What you miss understood that:
[your code]
(mistake 1,2)
Your fsacnf is wrong in two ways:
fscanf(argv[1],"%s",&word);
First argument is argv[1] is char* that is wrong you need to pass FILE* type. As explained in Mr. Oli Charlesworth's answer.
Second you still need to read from stdin. | operator redirects the output from first command to second command.
(mistake 3, 4, 5, 6)
By sending echo "blabla" you are just sending a single sting you need to do something like I did (I added \n in echo string for new line also my echo string start with $ so it not print as raw string). echo so that you can read from code according to second argument that is argv[1] not argv[2]. So in your code following line is wrong too.
int numofwords = atoi(argv[2]);
I corrected this line in my code.
and i is initialised to zero i = 0 , in while loop condition is <=, I think it should be <.
(mistake 7)
The way you run your code is wrong echo "blabla" | myprog 2 your program not know as mygrog you have to pass complete path. like I did ./main, ./ means current directory.
this just my view about your question. Read also the answer in comment given by Mr. William Pursell.
Let me know if you have other doubts.
I'm not sure exactly what your question is, but I think it may be "how do I treat a file input and console input the same?".
Are you aware that stdin (the standard input) is already a FILE *? So that means you can pass it as the first argument to fscanf, just like a "normal" file:
FILE *normal_file = fopen(...);
// Read from normal file
fscanf(normal_file, "%d", &x);
// Read from stdin
fscanf(stdin, "%d", &y);
An alternate choice would be pass the file name as an argument instead.
echo "blabla" | myprog 2 (number of words to print)
myprog 2 input.txt (number of words to print)
Then you'd:
if argc == 1, then proceed normally and process stdin (stdin is a FILE*) with fgetc, or another alternate f* method.
If argc == 2, then get the filename from argv[1], use fopen(filename) to get a FILE* and process it.
Otherwise, you would do what Oli said and get the first word, then either fopen and pass to your processing function, or fseek(stdin, SEEK_SET, 0) then pass stdin to your processing function.

Direct Parameter Access not working, using printf in C

I am doing some computer security research, and I am trying to learn about string format vulnerabilities. I am running a program that contains this code:
char buf[1<<5];
strncpy(buf, data, sizeof(buf));
buf[sizeof(buf)-1]='\0';
fprintf(stderr, buf);
When I feed the program the argument "%08x.%08x.%08x.%08x.%08x" (which gets read into the "data" variable), I get the output:
00000020.b7fd7560.08048b09.00000019.78383025
I understand that each hex number is popped off the stack, and the "78383025" comes from the buffer itself. So there are 4 words--16 bytes--that I have to pop off before I get to the start of my buffer.
When I give the argument `perl -e 'print "\x2a\xf9\xff\xbf%08x.%08x.%08x.%08x_%s_";'`, the %s part prints the string located at memory address 0xbffff92a.
Now, I'd like to do this using direct parameter access. If I feed the program the argument `perl -e 'print "\x2a\xf9\xff\xbf%16$s";'`, I should expect the program to do the same thing as above. But all the program prints is the four characters at the start of the buffer. So, what gives??? Am I using the wrong syntax for DPA???
I am using Ubuntu 9.04, 32-bit by the way.
Here is some compilable code, not guaranteed to produce the same results though:
#include <stdio.h>
void run(const char* data) {
char buf[1<<5];
strncpy(buf, data, sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
fprintf(stderr, buf);
}
int main(int argc, char* argv[]) {
run(argv[1]);
return 0;
}
%16$s refers to the 16-th argument after the format string, and tells printf to interpret it as a char* and display it as a string.
You seem to be using it as a means to skip 16 bytes before getting the string though, which is not exactly the same thing.
Since you want the 5-th argument, try something more like this format string :
"\x2a\xf9\xff\xbf%5$s"
Since you're using perl -e 'print "...";' to pass the data, you will have to escape the $ character. Ie. :
./a.out `perl -e 'print "\x2a\xf9\xff\xbf%5\\\$s";'`

Resources