How can I get argv from "struct linux_binprm"? - linux

I want to extract all argv from an existing struct linux_binprm. On kernel 3.4, I tried this piece of code: http://www.mail-archive.com/kernelnewbies#nl.linux.org/msg00278.html in do_excve_common, but it doesn't work. It returns (null). What is the problem and how can I get ALL the arguments in a char * string?

. If you want to get the full command line before the binary loader executing in do_execve_common(), you can try following:
there is one argument *argv in the function do_execve_common() parameter table, why bother to get the argv from "struct linux_binprm"? You can use the *argv directly with following codes. In the do_execve_common(), insert some codes as following:
argc = count(argv, MAX_ARG_STRINGS);
i = 0;
while (i < argc)
{
const char __user *str;
int len;
ret = -EFAULT;
str = get_user_arg_ptr(argv, i);
if (IS_ERR(str))
goto out;
len = strnlen_user(str, MAX_ARG_STRLEN);
if (!len)
goto out;
//copy the str to kernel temporary storage
//NOTE: tmp[] is a string array,
// the memory should have been allocated already for strings storage,
// each string is ended with \0
memcpy(tmp[i], str, len)
}
After executing these codes, I think the argv strings will be all saved in tmp[] array.
. While if you want to get the full command line after binary loader executing, I think at this time the argument page has been setup correctly, then you can try following approach to get the full command line:
There is a function proc_pid_cmdline() in ./fs/proc/base.c file, you can re-use most codes in proc_pid_cmdline() function to get the full command line from the argument page.

Related

(epbf) Unable to use string.h apis on string that probe read from kernel

When I tried to do some string operations (strlen, strcpy and strtok) today, I found it is unable to use those string.h apis on string probe read from kernel.
It will raise unknown opcode error on python bcc/bpf and raise libbpf: failed to find BTF for extern 'strlen' on libbpf.
A pseudo code I tried as follows:
u64 ptr = PT_REGS_PARMX(regs);
char str1[10] = {};
char str2[10] = "test";
bpf_probe_read_kernel(str1, sizeof(str1), (const void *) ptr);
u64 len = strlen(str1); // error will raise here
len = strlen(str2); // but this is ok if string not read from kernel
Although strlen I could implement in:
u64 len = 0;
for(len; len < sizeof(str1); len++){
if (str1[len] == '\0') break;
}
I still wonder that why it is unable to use string.h apis
and how could make it able to use.
You can't call arbitrary kernel functions from BPF bytecode.
The reason it works for str2 is because it's a constant and the compiler therefore optimizes it to 4 without needing to call strlen.
If you need to compute the length of a string, you need to implement strlen() yourself or to copy one of the kernel's implementations. Note that in general, it is not recommended to perform computation on strings in BPF; that's a job better left to the userspace counterpart.

Can exec*'s argv contain a value of 0 in multiple places?

I was reading that after exec creates a new process,
argv is an array of argument strings, with argv[argc] == 0
What happens if one of the other values within the array argv happens to be 0? Will the number of arguments (argc) be incorrectly calculated when the child process runs?
I read this on page 34 of the ABI of AMD64 (https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf).
The execve system call (which is used by all exec* functions) has an argument of the form char *const argv[]. The kernel calculates argc by iterating over the supplied argv as follows:
static int count(struct user_arg_ptr argv, int max)
{
int i = 0;
if (argv.ptr.native != NULL) {
for (;;) {
const char __user *p = get_user_arg_ptr(argv, i);
if (!p)
break;
if (IS_ERR(p))
return -EFAULT;
if (i >= max)
return -E2BIG;
++i;
if (fatal_signal_pending(current))
return -ERESTARTNOHAND;
cond_resched();
}
}
return i;
}
The function get_user_arg_ptr essentially calculates an index into the argv array and returns the pointer stored at that index. The loop breaks under four conditions, two of them are pertinent to your question:
On the first NULL seen in the argv array. If there are other pointers following the first NULL in argv, they are ignored. Having more than one NULL smells like a bug in the program that constructed argv.
When the number of pointers is larger than or equal to MAX_ARG_STRINGS, which is defined as 0x7FFFFFFF. In this case, the system call fails.
The value of i returned is assigned to argc when get_user_arg_ptr returns.
Another case where the terminating NULL in argv matters is when the application itself uses argv as follows:
for(char **p = argv; *p != NULL; ++p)
{
// ...
}
It's part of the Linux ABI that argv terminates with NULL, so such code is legal and portable across all Linux implementations. By the way, this code is legal too on Windows. Therefore, argc is provided for convenience only.
In addition, both the C and C++ standards state in 5.1.2.2.1 and 3.6.1, respectively, that if argc is larger than zero, then all values in argv[0] through argv[argc-1] shall be non-null pointers to null-terminated strings. Also argv[argc] must be null and that argc is non-negative. See also this answer.

"Stack around the variable was corrupted" error

I'm trying to read a char value using fgets as follows:
int main() {
char m_cityCharCount;
// Input the number of cities
fgets(&m_cityCharCount, 4, stdin);
return 0;
}
Visual Studio returns this error after the code is executed - Stack around the variable m_cityCharCount was corrupted
Is there something I can do about it?
m_cityCharCount is a char, it can hold one char at the most, but you are telling fgets it is 4 bytes buffer. Even if you input nothing but hit the enter key, fgets will store the new line AND the null terminator to the buffer, which of cause is a serious problem. You need a bigger buffer to do fgets:
char str[4096];
fgets(str, sizeof str, stdin);
First parameter of fgets() is pointer on buffer (size of it should be great or equals than second parameter. But sizeof(char) == 1)
int main() {
char m_cityCharCount[4];
// Input the number of cities
fgets(m_cityCharCount, 4, stdin);
return 0;
}

VC++ read variable length char*

I'm trying to read a variable length char* from the user input. I want to be able to specify the length of the string to read when the function is called;
char *get_char(char *message, unsigned int size) {
bool correct = false;
char *value = (char*)calloc(size+1, sizeof(char));
cout << message;
while(!correct) {
int control = scanf_s("%s", value);
if (control == 1)
correct = true;
else
cout << "Enter a correct value!" <<endl
<< message;
while(cin.get() != '\n');
}
return value;
}
So, upon running the program and trying to enter a string, I get a memory access violation, so I figured something has gone wrong when accessing the allocated space. My first idea was it went wrong because the size of the scanned char * is not specified within scanf(), but it doesn't work with correct length strings either. Even if I give the calloc a size of 1000 and try to enter one character, the program crashes.
What did I do wrong?
You have to specify the size of value to scanf_s:
int control = scanf_s("%s", value, size);
does the trick.
See the documentation of scanf_s for an example of how to use the function:
Unlike scanf and wscanf, scanf_s and wscanf_s require the buffer size to be specified for all input parameters of type c, C, s, S, or [. The buffer size is passed as an additional parameter immediately following the pointer to the buffer or variable.
I omit the rest of the MSDN description here because in the example they're providing, they use scanf instead of scanf_s what is quite irritating...

CreateProcess does not return extected value

I want to send the double quote character to my CreateProcess function. How can I do the correct way? I want to send all of this characters: "%h"
CreateProcess(L"C:\\identify -format ",L"\"%h\" trustedsnapshot.png",0,0,TRUE,NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,0,0,&sInfo,&pInfo);
note: "identify" is an Imagemagick program.
Here is the full code:
int ExecuteExternalFile()
{
SECURITY_ATTRIBUTES secattr;
ZeroMemory(&secattr,sizeof(secattr));
secattr.nLength = sizeof(secattr);
secattr.bInheritHandle = TRUE;
HANDLE rPipe, wPipe;
//Create pipes to write and read data
CreatePipe(&rPipe,&wPipe,&secattr,0);
STARTUPINFO sInfo;
ZeroMemory(&sInfo,sizeof(sInfo));
PROCESS_INFORMATION pInfo;
ZeroMemory(&pInfo,sizeof(pInfo));
sInfo.cb=sizeof(sInfo);
sInfo.dwFlags=STARTF_USESTDHANDLES;
sInfo.hStdInput=NULL;
sInfo.hStdOutput=wPipe;
sInfo.hStdError=wPipe;
CreateProcess(L"C:\\identify",L" -format \"%h\" trustedsnapshot.png",0,0,TRUE,NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,0,0,&sInfo,&pInfo);
CloseHandle(wPipe);
char buf[100];
DWORD reDword;
CString m_csOutput,csTemp;
BOOL res;
do
{
res=::ReadFile(rPipe,buf,100,&reDword,0);
csTemp=buf;
m_csOutput+=csTemp.Left(reDword);
}while(res);
//return m_csOutput;
float fvar;
//fvar = atof((const char *)(LPCTSTR)(m_csOutput)); ori
//fvar=atof((LPCTSTR)m_csOutput);
fvar = _tstof(m_csOutput);
const size_t len = 256;
wchar_t buffer[len] = {};
_snwprintf(buffer, len - 1, L"%d", fvar);
MessageBox(NULL, buffer, L"test print createprocess value", MB_OK);
return fvar;
}
I need this function to return the integer value from the CreateProcess.
The way you wrote it should work OK, or do you have any problems with it?
Your problem is not the quotes. There are two problems with the way you pass parameters to CreateProcess. The first is that the command line passed in the second argument should include the name of the command (that is, it should include the value for "argv[0]"), the second is that the redirection (> testt.txt) is not handled by the CreateProcess API,. Unless c:\identify expects such arguemnts, you should not include this in the command line.

Resources