How to print to console (Linux) without the standard library (libc) - c

I'm not using the standard library, since my target x86 Linux distro is very limited.
#include <unistd.h>
void _start () {
const char msg[] = "Hello world";
write( STDOUT_FILENO, msg, sizeof( msg ) - 1 );
}
I want to print text to console, but I can't, is there any other way to do this.
The code above wont work because it depends on standard library
gcc Test.cpp -o Test -nostdlib

If you don't have libc, then you need to craft a write() system call from scratch to write to the standard output.
See this resource for the details: http://weeb.ddns.net/0/programming/c_without_standard_library_linux.txt
Code example from the above link:
void* syscall5(
void* number,
void* arg1,
void* arg2,
void* arg3,
void* arg4,
void* arg5
);
typedef unsigned long int uintptr; /* size_t */
typedef long int intptr; /* ssize_t */
static
intptr write(int fd, void const* data, uintptr nbytes)
{
return (intptr)
syscall5(
(void*)1, /* SYS_write */
(void*)(intptr)fd,
(void*)data,
(void*)nbytes,
0, /* ignored */
0 /* ignored */
);
}
int main(int argc, char* argv[])
{
write(1, "hello\n", 6);
return 0;
}

Related

Redirect stdout of another process in Arm Linux

There is a process (not process that I wrote) in Arm Linux that write the stdout for /dev/console that unaccessible to me.
How can I redirect that stdout to file ,so I can watch this process output?
Of course I have root on this Arm Linux and I can compile code .
I would write a minimal dynamic library that interposes open(), handling open("/dev/console", ...) separately. Then, run the other binary with LD_PRELOAD environment variable set to point to that library.
At minimum, something like the following libnoconsole.c should work:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#ifndef OLD_PATH
#define OLD_PATH "/dev/console"
#endif
#ifndef NEW_PATH
#define NEW_PATH "/tmp/log"
#endif
static int (*original_open)(const char *, int, ...) = NULL;
static int (*original_openat)(int, const char *, int, ...) = NULL;
static int match(const char *a, const char *b)
{
while (*a == *b)
if (!*a) {
return 1;
} else {
a++;
b++;
}
return 0;
}
int open(const char *pathname, int flags, ...)
{
if (!original_open)
original_open = dlsym(RTLD_NEXT, "open");
if (!original_open) {
errno = ENOSYS;
return -1;
}
if (match(pathname, OLD_PATH))
return original_open(NEW_PATH, O_RDWR | O_CREAT, 0600);
if (flags & (O_CREAT | O_TMPFILE)) {
va_list args;
mode_t mode;
va_start(args, flags);
mode = va_arg(args, mode_t);
va_end(args);
return original_open(pathname, flags, mode);
} else
return original_open(pathname, flags);
}
int openat(int dirfd, const char *pathname, int flags, ...)
{
if (!original_openat)
original_openat = dlsym(RTLD_NEXT, "openat");
if (!original_openat) {
errno = ENOSYS;
return -1;
}
if (match(pathname, OLD_PATH))
return original_openat(AT_FDCWD, NEW_PATH, O_RDWR | O_CREAT, 0600);
if (flags & (O_CREAT | O_TMPFILE)) {
va_list args;
mode_t mode;
va_start(args, flags);
mode = va_arg(args, mode_t);
va_end(args);
return original_openat(dirfd, pathname, flags, mode);
} else
return original_openat(dirfd, pathname, flags);
}
Compile it using
gcc -Wall -O2 -c libnoconsole.c
gcc -Wall -O2 -shared -Wl,-soname,libnoconsole.so libnoconsole.o -ldl -o libnoconsole.so
and run your program via
env LD_PRELOAD=$PWD/libnoconsole.so the-other-program
and it should save its output to /tmp/log instead of /dev/console.
The reason for above using the silly match() instead of !strcmp() is that strcmp() wasn't always an async-signal safe function, whereas open() and openat() are: it's a small wart to avoid potential issues.

Arguments in syscall intercept using loadable kernel module seem to be broken

First post so I apologize for the possibly low quality explanation.
I was trying to write a loadable kernel module that does nothing but intercept syscalls to SYS_open, print the arguments to KERN_INFO and then forward the arguments to the real syscall.
The forwarding part seems to be working just fine, but I'm having issues with the printing, arguments seem to be broken, from the syscall interceptor function's perspective.
Following are the pointer to the real open syscall as well as the interceptor definition.
asmlinkage int (*real_open) (const char __user *, int, umode_t);
asmlinkage int fake_open(const char __user *filename, int flags, umode_t mode)
{
printk(KERN_INFO "interceptor: open() with flags = %d\n", flags);
return real_open(filename, flags, mode);
}
This is the syscall I'm testing:
syscall(SYS_open, argv[1], 3187236);
Which leads to the following call, according to strace:
open("test", O_RDONLY|O_TRUNC|__O_SYNC|O_LARGEFILE|O_PATH|FASYNC|0x24) = -1 ENOENT (No such file or directory)
And the information printed by the interceptor:
[18191.407899] interceptor: open() with flags = 0
As you can see, the flags argument is equal to 0, even though I passed 3187236 as flags.
What's even weirder, the real open syscall seems to have no issue in dealing with the arguments.
Any kind of help is appreciated since I'm pretty much stuck here.
Here's the full module code in case it's of any help:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/futex.h>
#include <linux/highmem.h>
#include <asm/unistd.h>
#include <linux/slab.h>
/*
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
*/
unsigned long long *sys_call_table = (unsigned long long*) 0xffffffffaf800260; //sudo cat /proc/kallsyms | grep sys_call_table (/boot/System.map)
asmlinkage int (*real_open) (const char __user *, int, umode_t);
asmlinkage int fake_open(const char __user *filename, int flags, umode_t mode)
{
printk("interceptor: open() with flags = %d\n", flags);
return real_open(filename, flags, mode);
}
//make the memory page writable
int make_rw(unsigned long long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
if(pte->pte & ~_PAGE_RW)
pte->pte |= _PAGE_RW;
return 0;
}
//make the memory page read only
int make_ro(unsigned long long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;
return 0;
}
static int __init init(void)
{
printk(KERN_INFO "Attempting to install hook.\n");
make_rw((unsigned long long) sys_call_table);
real_open = (void*) sys_call_table[__NR_open];
sys_call_table[__NR_open] = (unsigned long long) fake_open;
make_ro((unsigned long long) sys_call_table);
return 0; //no error
}
static void __exit clean(void)
{
printk(KERN_INFO "Uninstalling hook.\n");
make_rw((unsigned long long) sys_call_table);
sys_call_table[__NR_open] = (unsigned long long) real_open;
make_ro((unsigned long long) sys_call_table);
}
module_init(init);
module_exit(clean);
MODULE_LICENSE("GPL");
UPDATE:
Kernel version 4.17 and up requires parameters to be passed through a pt_regs struct. Previous code was good up to 4.16.
asmlinkage long (*real_open) (const struct pt_regs *);
asmlinkage long fake_open(const struct pt_regs *regs)
{
printk("interceptor: open() with flags = %ld\n", regs->si);
return real_open(regs);
}
More information: https://github.com/milabs/khook/issues/3
Thanks for everyone who contributed in the comments!

Uneven behavior in different system calls hooking

I am working on a project in which i have hooked the system open call. When a user attempts to open a file i want sys_open to block the action if the current task (pid or tgid that "black listed" ) has potential to leak the file out of the host.
Any ways, the hooking itself worked fine on sys_read and sys_write (I have some printk inside the fake function as an indicator).
but, when i try the hooking on the sys_open function, nothing is printed out - means that the override not succeeded.
I printed out the address of the sys call before and after the override so this might not be the issue.
I am confused about what can cause that uneven behavior when hooking different functions.
will be glad for some input here.
thanks !
dmesg output examples:
when hooked write -
...
[ 2989.500485] in my write ...
[ 2989.500585] in my write ...
when hooked open, noting printed, but here some "debug" output -
[ 890.709696] address found 00000000103d42f6
[ 890.709697] Address before - 0000000006d29c3a
[ 890.709698] Address after - 00000000a5117c6a
[ 948.533339] BYE !!!
using lubuntu vm (kernel v 4.15.0.20).
here is the source code:
#include <linux/init.h> // Macros used to mark up functions e.g., __init __exit
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
#include <asm/cacheflush.h>
#include <linux/set_memory.h>
#include <linux/cred.h>
#include <linux/user.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ABC");
MODULE_VERSION("0.1");
asmlinkage long (*original_call)( char __user *filename, int flags, umode_t mode); // for read or write: (unsigned int fd, char __user *buf, size_t count);
asmlinkage long my_sys_READ(unsigned int fd, char __user *buf, size_t count);
asmlinkage long my_sys_WRITE(unsigned int fd, char __user *buf, size_t count);
asmlinkage long my_sys_OPEN( char __user *filename, int flags, umode_t mode);
unsigned long* find_sys_call_table(void);
void set_page_rw( unsigned long addr);
void set_page_ro( unsigned long addr);
const struct cred *_cred = NULL ;
struct user_struct *user =NULL ;
unsigned long* sys_call_table = NULL;
void set_page_rw(unsigned long addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
}
void set_page_ro( unsigned long addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
pte->pte = pte->pte &~_PAGE_RW;
}
/*
asmlinkage long my_sys_READ(unsigned int fd, char __user *buf, size_t count)
{
//_cred = current_cred();
user = get_current_user();
if( (int)(*user).uid.val == uid )
{
printk(KERN_ALERT"in my read ... hacked !");
return original_call(fd, buf, count);
}
printk(KERN_ALERT"in my read ... hacked !");
return original_call(fd, buf, count);
}
asmlinkage long my_sys_WRITE(unsigned int fd, char __user *buf, size_t count)
{
//_cred = current_cred();
user = get_current_user();
if( (int)(*user).uid.val == uid )
{
printk(KERN_ALERT"in my write ... hacked !");
return original_call(fd, buf, count);
}
printk(KERN_ALERT"in my write ... hacked !");
return original_call(fd, buf, count);
}
*/
asmlinkage long my_sys_OPEN( char __user *filename, int flags, umode_t mode)
{
printk(KERN_ALERT"in my open ... hacked !");
return original_call(filename, flags, mode);
}
unsigned long* find_sys_call_table(void)
{
return (unsigned long *)kallsyms_lookup_name("sys_call_table");
}
int init_module()
{
printk(KERN_ALERT "I'm dangerous. I hope you did a ");
printk(KERN_ALERT "sync before you insmod'ed me.\n");
sys_call_table = find_sys_call_table();
printk(KERN_INFO"address found %p \n",sys_call_table);
original_call = (void *)sys_call_table[__NR_open];
set_page_rw((unsigned long)sys_call_table);
printk(KERN_INFO" Address before - %p", (void *)sys_call_table[__NR_open]);
sys_call_table[__NR_open] = (unsigned long)my_sys_OPEN;
printk(KERN_INFO" Address after - %p", (void *)sys_call_table[__NR_open]);
return 0;
}
/*
* Cleanup − unregister the appropriate file from /proc
*/
void cleanup_module()
{
/*
* Return the system call back to normal
*/
if (sys_call_table[__NR_open] != (unsigned long)my_sys_OPEN) {
printk(KERN_ALERT "Somebody else also played with the ");
printk(KERN_ALERT "open system call\n");
}
printk(KERN_ALERT "BYE !!!\n");
sys_call_table[__NR_open] = (unsigned long)original_call;
}
Your Ubuntu version is based on glibc 2.27.
glibc version 2.26 switched to implementing open with openat:
commit b41152d716ee9c5ba34495a54e64ea2b732139b5
Author: Adhemerval Zanella <adhemerval.zanella#linaro.org>
Date: Fri Nov 11 15:00:03 2016 -0200
Consolidate Linux open implementation
This patch consolidates the open Linux syscall implementation on
sysdeps/unix/sysv/linux/open{64}.c. The changes are:
1. Remove open{64} from auto-generation syscalls.list.
2. Add a new open{64}.c implementation. For architectures that
define __OFF_T_MATCHES_OFF64_T the default open64 will create
alias to required open symbols.
3. Use __NR_openat as default syscall for open{64}.
You will have to hook openat in addition to open as a result.
Note that the Linux kernel provides a proper interface for this, in the form of the fanotify interface. If you use that, you will not have to worry about such details.

Redefining fputc function when using arm-none-eabi toolchain

We have an STM32 C/C++ project and we have redefined the weak fputc(int, FILE *) function in order to redirect printf output to some UART channel.
Up until now, we were building the project under IAR with the IAR compiler. The logging through UART was working fine.
We have now switched to the arm-none-eabi toolchain and are building the project with g++. But it looks like the redefinition of the fputc function is not linked anymore and so the UART logging is not working.
How can I force the use of the redefined function by printf?
The arm-none-eabi- toolchain is using newlib, which lets you redefine _write(int fd, const void *buf, size_t count) instead, all stdio output functions would then use that interface. fd==1 would correspond to stdout, fd==2 to stderr. You must provide a few more stub functions, like
void _exit(int) {
while(1)
;
}
etc. Most of them are trivial, but printf() requires a working _sbrk() too, because it uses malloc() internally.
Just had the same problem converting project from Keil to Cube. Found this elegant STM32 printf retarget to UART solution on GitHub (and please forgive the copy-paste):
/*# 7- Retarget printf to UART (std library and toolchain dependent) #########*/
#if defined(__GNUC__)
int _write(int fd, char * ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
return len;
}
#elif defined (__ICCARM__)
#include "LowLevelIOInterface.h"
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
HAL_UART_Transmit(&huart1, (uint8_t *) buffer, size, HAL_MAX_DELAY);
return size;
}
#elif defined (__CC_ARM)
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
#endif
// OR:
// Add syscalls.c with GCC
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* #brief Retargets the C library printf function to the USART.
* #param None
* #retval None
*/
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
This may or may not work in your particular case, but you get the Idea...
In my case, I had to prepend function type with extern "C" :
extern "C" int _write(int fd, char * ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
return len;
}

Segmentation fault from hook on open()

I am trying to create a hook on the system function open(). I've done this along the following lines.
I created a wrapper library with the following:
extern int mocked_open(const char* fn, int flags, va_list args);
int open(const char* fn, int flags, ...)
{
int r = -1;
va_list args;
va_start(args, flags);
r = mocked_open(fn, flags, args);
va_end(args);
return r;
}
I compile this into libwrapper.so, which I load using LD_PRELOAD.
The implementation of mocked_open() is as follows (I use the CPPUtest framework):
int mocked_open(const char* fn, int flags, va_list args)
{
if (strncmp(fn, test_device_id, 11) == 0)
{
return mock().actualCall("open").returnValue().getIntValue();
}
else
{
int r = -1;
int (*my_open)(const char*, int, ...);
void* fptr = dlsym(RTLD_NEXT, "open");
memcpy(&my_open, &fptr, sizeof(my_open));
if (flags & O_CREAT)
{
r = my_open(fn, flags, va_arg(args, mode_t));
}
else
{
r = my_open(fn, flags);
}
return r;
}
}
The test_device_id is a simple string ("test_device"), which I hope is not used elsewhere.
During running the tests the executable crashes with a segmentation fault. I've traced this down to the GCC profiling functionality, which wants to open/create a bunch of .gcda files and calls open() for this.
After some debugging with strace (per suggestion below), I found that the line r = my_open(fn, flags, va_arg(args, mode_t)); is indeed the culprit. It is being called recursively, or so it seems: I see a lot of calls to this line, without the function returning. Then a segfault. The file being opened is the corresponding .gcda file (for profiling). In fact, the segfault only occurs with profiling enabled.
Try this
typedef int (*OpenFunction)(const char* fn, int flags, ...);
then
OpenFunction function;
void **pointer;
pointer = (void **)&function;
*pointer = dlsym(RTLD_NEXT, "open");
this is a complete working example
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
typedef int (*OpenFunction)(const char* fn, int flags, ...);
int main(int argc, char **argv)
{
OpenFunction function;
void *dl;
int fd;
void **pointer;
if (argc < 2)
return -1;
pointer = (void **)&function;
*pointer = dlsym(RTLD_NEXT, "open");
fd = function(argv[1], O_RDONLY);
if (fd != -1)
{
printf("file opened succesfully\n");
close(fd);
}
else
{
printf("%s: cannot open the file\n", strerror(errno));
}
return 0;
}
When you compile with gcov profiling enabled, the compiler inserts extra code into your functions, to keep track of which code has been executed. In rough pseudocode, that inserted code will do (among other things):
if (!output_file_has_been_opened) {
fd = open(output_filename, ...);
check_ok(fd);
output_file_has_been_opened = TRUE;
track_coverage();
}
... so if the output file hasn't yet been successfully opened (as at the start of your program), it will attempt to open it. Unfortunately, in this case that will call your mocked open() function - which has the same inserted code; since the file still hasn't been successfully opened, and since the gcov code isn't aware that there's something unusual going on, it will attempt the open() call again - and this is what's causing the recursion (and eventual segfault, once the stack is exhausted).
You are passing va_list incorrectly. Try following hope it helps
r = my_open(fn, flags, args);
For more info
http://c-faq.com/varargs/handoff.html

Resources