x86_64 inline assembly function call - c

I tried to use inline assembly to call the function with three arguments, but it fails with Segmentation fault.
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <assert.h>
#include <stdlib.h>
void callee_test3(char* dest, char* src, size_t srclen)
{
printf("%s %s %li\n", dest, src, srclen);
};
int main()
{
char* dest = "test";
char* src = "src";
size_t srclen = 4;
asm(
"movq %[arg1], %%rdi\n\t"
"movq %[arg2], %%rsi\n\t"
"movq %[arg3], %%rdx\n\t"
"call *%[callee]"
:
:[arg1]"r"((u_int64_t)(dest)),
[arg2]"r"((u_int64_t)(src)),
[arg3]"r"((u_int64_t)(srclen)),
[callee]"r"(callee_test3)
:"cc"
);
return 0;
}
I can call a function with two arguments, but when I added into three arguments, it just failed. Tried to use gdb to trace where the code break it shows :
Breakpoint 1, main () at test.c:24
24 char* dest = "test";
(gdb) next
25 char* src = "src";
(gdb)
26 size_t srclen = 4;
(gdb)
34 :[arg1]"r"((u_int64_t)(dest)),
(gdb)
35 [arg2]"r"((u_int64_t)(src)),
(gdb)
28 asm(
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554872 in ?? ()
For some reasons, the third argument didn't store into the register. My assumption is I'm calling the wrong register to store the third argument. But I couldn't find the resource about that.

Related

scanf calling malloc without free

I was trying to replace the standard library functions malloc and free with my own versions to detect memory leak. I did this through dynamic linking:
// malloc.c
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef __GLIBC__
#error glibc must be used.
#endif
extern void *__libc_malloc(size_t size);
extern void *__libc_free(void *ptr);
void *malloc(size_t size) {
const char msg[] = "malloc called.\n";
void *ptr = __libc_malloc(size);
if (ptr != NULL)
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
return ptr;
}
void free(void *ptr) {
const char msg[] = "free called.\n";
if (ptr != NULL)
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
__libc_free(ptr);
}
Compiled with
gcc -shared malloc.c -o libmalloc.so -fpic
Then I wrote another C program:
// hello.c
#include <stdio.h>
#include <stdlib.h>
int compare_less(const void *a_, const void *b_) {
const int a = *(const int *)a_, b = *(const int *)b_;
return a < b ? -1 : (a == b ? 0 : 1);
}
int main() {
int n;
scanf("%d", &n);
int *a = malloc(sizeof(int) * n);
for (int i = 0; i != n; ++i) {
scanf("%d", a + i);
}
qsort(a, n, sizeof(int), &compare_less);
for (int i = 0; i < n - 1; ++i)
printf("%d, ", a[i]);
printf("%d\n", a[n - 1]);
free(a);
return 0;
}
and link them by
gcc hello.c -o hello -L. -lmalloc
When I run ./hello, I found that malloc called. was printed three times: during the first call to scanf, during the direct call to malloc, and during the second call to scanf. However free called. was only printed once.
I added a breakpoint at malloc and GDB backtrace shows the following:
(gdb) bt
#0 malloc (size=1024) at malloc.c:12
#1 0x00007ffff7df2c24 in __GI__IO_file_doallocate (fp=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/filedoalloc.c:101
#2 0x00007ffff7e01d60 in __GI__IO_doallocbuf (fp=fp#entry=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/libioP.h:947
#3 0x00007ffff7e00d5c in _IO_new_file_underflow (fp=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/fileops.c:485
#4 0x00007ffff7e01e16 in __GI__IO_default_uflow (fp=0x7ffff7f8daa0 <_IO_2_1_stdin_>) at ./libio/libioP.h:947
#5 0x00007ffff7dd7150 in __vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr#entry=0x7fffffffdfb0, mode_flags=mode_flags#entry=2)
at ./stdio-common/vfscanf-internal.c:628
#6 0x00007ffff7dd61c2 in __isoc99_scanf (format=<optimized out>) at ./stdio-common/isoc99_scanf.c:30
#7 0x000055555555525e in main () at hello.c:11
It seems that scanf calls malloc to allocate memory for its buffer, but I did not see scanf calling free. However, valgrind shows that all heap blocks were freed, so that memory must have been freed somehow.
My questions:
How is the memory allocated by scanf deallocated?
If I want to record the calls to malloc and free from users' code only (i.e. ignoring those from standard libraries like scanf), what should I do?

How to mprotect the data section?

I want to mprotect the data section. The following program will not run correctly. I understand the first argument of mprotect() should be aligned. But how to get an aligned memory address for the data section?
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
char s[] = "Hello World!";
int main() {
if(mprotect(s, strlen(s) + 1, PROT_EXEC) == -1) {
perror("mprotect()");
return 1;
}
}
$ ./mprotect_prog
mprotect(): Invalid argument
EDIT: I use the following code to get the page size.
{
builtin printf %s '#define PAGESIZE '
getconf PAGESIZE
} > pagesize.h
Then the C code is changed to the following.
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
#include "pagesize.h"
char s[] __attribute__((aligned(PAGESIZE))) = "Hello World!";
int main() {
if(mprotect(s, strlen(s) + 1, PROT_EXEC) == -1) {
perror("mprotect()");
return 1;
}
}
Then, I get a segmentation fault. Can anybody reproduce this error? What is wrong with it?
$ ./mprotect_prog
Segmentation fault
EDIT2: I have to add the following line below the 's' line to make sure s occupies a whole page on its own. Then, the program works.
char r[] __attribute__((aligned(PAGESIZE))) = "Hello World!";
{
builtin printf %s '#define PAGESIZE '
getconf PAGESIZE
} > pagesize.h
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
#include "pagesize.h"
char s[] __attribute__((aligned(PAGESIZE))) = "Hello World!";
char r[] __attribute__((aligned(PAGESIZE))) = "Hello World!";
int main() {
if(mprotect(s, strlen(s) + 1, PROT_EXEC) == -1) {
perror("mprotect()");
return 1;
}
}

Error with address for jmp in inline assembly

What I do is
Get address of ExitProcess
Make space for opcode
Modify opcode in the space
Execute modified opcode by __asm__ ("jmp %%ecx"::"c"(opcode));
Here is my code:
#include <windows.h>
#include <stdio.h>
int main()
{
char addr[4];
*(int*)addr = GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitProcess");
//push 0 == 0x6a 0x00
//call ExitProcess == 0xe8 0xd8 0x79 0xa2 0x75
char opcode[400] = {0x6a, 0x00, 0xe8,addr[0], addr[1],addr[2],addr[3]};
__asm__ ("jmp %%ecx" ::"c"(opcode));
//never be here
printf("never get here");
getchar();
return 0;
}
I expect program to exit normally, but the program terminates with a segmentation fault.
It seems that it jumps to somewhere, but not to the location I want it to jump.
How can I fix that?
Setting aside the odd thing you are trying to do...
Your problem is the opcode e8 is a relative jump. So you need to account for the address you are storing it at. Something like this maybe:
Update: Per taeyun, account for length of x.
#include <windows.h>
#include <stdio.h>
#pragma pack(1)
struct mfoo {
unsigned char x[3] = {0x6a, 0x00, 0xe8};
void *addr;
} foo;
int main()
{
unsigned char *a = (unsigned char *)GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitProcess");
foo.addr = (void *)(a - sizeof(foo) - (unsigned char *)foo.x);
__asm__ ("jmp *%%ecx" ::"c"(&foo));
//never be here
printf("never get here");
getchar();
return 0;
}

Inline assembly in C program on x86_64 linux

I've built a short program written on C and inline assembly on my linux x86_64. It is supposed to write a string to stdout. I found it in an article on the internet:
int write_call( int fd, const char * str, int len ){
long __res;
__asm__ volatile ( "int $0x80":
"=a" (__res):"0"(__NR_write),"b"((long)(fd)),"c"((long)(str)),"d"((long)(len)) );
return (int) __res;
}
void do_write( void ){
char * str = "Paragon output string.\n";
int len = strlen( str ), n;
printf( "string for write length = %d\n", len );
n = write_call( 1, str, len );
printf( "write return : %d\n", n );
}
int main( int argc, char * argv[] ){
do_write();
return EXIT_SUCCESS;
}
But as I run it, it works incorrectly, making output
"write return : -14"
If I build and run it on 32-bit linux it does what is expected.
After some research I fount out that instruction "int $0x80" is a x86 instruction and truncates arguments in registers if called on x86_64.
But I couldn't find a proper substitution of "int $0x80" for x86_64 architecture. I have zero experience in assembly.
What should I put instead of "int $0x80" to receive expected result?
For amd64, you need to use "syscall" - and use different registers - instead of "int 0x80":
http://cs.lmu.edu/~ray/notes/linuxsyscalls/
http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64
http://crypto.stanford.edu/~blynn/rop/
Here's a good example:
How to invoke a system call via sysenter in inline assembly (x86/amd64 linux)?
#include <unistd.h>
int main(void)
{
const char hello[] = "Hello World!\n";
const size_t hello_size = sizeof(hello);
ssize_t ret;
asm volatile
(
"movl $1, %%eax\n\t"
"movl $1, %%edi\n\t"
"movq %1, %%rsi\n\t"
"movl %2, %%edx\n\t"
"syscall"
: "=a"(ret)
: "g"(hello), "g"(hello_size)
: "%rdi", "%rsi", "%rdx", "%rcx", "%r11"
);
return 0;

Why I can't print out environment variables in gdb?

#include <unistd.h>
#include <stdio.h>
extern char **environ;
int main(int argc, char *argv[]) {
int i = 0;
while(environ[i]) {
printf("%s\n", environ[i++]);
}
return 0;
}
Here's my ops:
(gdb) n
8 printf("%s\n", environ[i++]);
(gdb) p environ[i]
Cannot access memory at address 0x0
(gdb) n
LOGNAME=root
7 while(environ[i]) {
As you can see,printf can print out environ[i],but p environ[i] gives me Cannot access memory at address 0x0,why?
gdb resolves the wrong environ symbol. I don't know why though. See below as to why.
But you can test it. Change the program to:
#include <unistd.h>
#include <stdio.h>
extern char **environ;
int main(int argc, char *argv[]) {
int i = 0;
printf("%p\n", &environ);
while(environ[i]) {
printf("%s\n", environ[i++]);
}
return 0;
}
Now let's run this in the debugger.
(gdb) n
7 printf("%p\n", &environ);
(gdb) n
0x8049760
8 while(environ[i]) {
(gdb) p &environ
$1 = (char ***) 0x46328da0
(gdb)
So. The actual program has, during its linking, resolved environ to the address 0x8049760.
When gdb wants to access the environ symbol, it resolves to 0x46328da0, which is different.
Edit.
It seems your environ symbol is actually linked to the environ##GLIBC_2.0 symbol.
In gdb write this:
(gdb) p environ
And hit the tab key (twice), it'll autocomplete the symbols. Which yields:
(gdb) p environ
environ environ##GLIBC_2.0
environ##GLIBC_2.0 is the one actually linked to the extern char **environ
Printing this yields the same address as the program sees, 0x8049760:
(gdb) p &'environ##GLIBC_2.0'
$9 = ( *) 0x8049760
(gdb) p ((char**)'environ##GLIBC_2.0')[i]
$10 = 0xbffff6ad "XDG_SESSION_ID=1"
So, at one point glibc deprecated the environ symbol, and added a newer version
Environment variables are accessed in C/C++ using the function getenv() defined in stdlib.h. However, using the envp parameter of the main function you can use the following example to iterate over environment variables.
#include <stdio.h>
int main(int argc, char *argv[], char *envp[])
{
char **next = envp;
while (*next)
{
printf("%s\n", *next);
next++;
}
return 0;
}
Tested on a Mac
Like grundprinzip said, use getenv(sz) and remember to null-check the return value
Alternatively,
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[], char*[] environ) {
int i = 0;
while(environ[i]) {
printf("%s\n", environ[i++]);
}
return 0;
}
Probably the process under debugging is started with
execve(binary, NULL, NULL);
and the extern char **environ gets that 2nd NULL even though there's an environment available.
With a little change, your program works both standalone and under gdb.
/* #include <unistd.h> */ /* no more environ */
#include <stdio.h>
/* extern char **environ; */ /* no more environ */
int main(int argc, char *argv[]) {
int i = 0;
char **ptr = argv + argc + 1; /* points to environment, in Un*x */
while(ptr[i]) {
printf("%s\n", ptr[i++]);
}
return 0;
}
Why, and how, that NULL gets converted to the proper value inside gdb I have no idea.
There's nothing wrong with your code. I tried it on my machine and it printed the environment as expected. You should not need to use getenv().
Are you running this application from the terminal? If not, you should be. Other means of executing an application might be calling your binary without passing it the environment.
From the terminal what is your output when you run "env"? It should output the same list as your program. It does on my machine.

Resources