I'm trying to make a custom system call for my OS course following these instructions from the link bellow:
https://uwnthesis.wordpress.com/2016/12/26/basics-of-making-a-rootkit-from-syscall-to-hook/
My kernel's version is 5.3.9.
This is my code so far:
My .c file (located in /usr/srclinux-5.3.9):
#include <linux/syscalls.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/string.h>
#include "pname.h"
asmlinkage long sys_process_name(char* process_name){
/*placeholder to print full string to tty*/
char name[32];
strcpy(name, process_name);
/*tasklist struct to use*/
struct task_struct *task;
/*tty struct*/
struct tty_struct *my_tty;
/*get current tty*/
my_tty = get_current_tty();
/*<sched.h> library method that iterates through list of processes from task_struct defined above*/
for_each_process(task){
printk("Task name: %s.\n",task->comm);
printk("Task ID: %ld.\n",task->pid);
printk("Task for compare: %s.\n", name);
printk("\n");
/*compares the current process name (defined in task->comm) to the passed in name*/
if(strcmp(task->comm, name) == 0){
printk("Process Found!\n");
/*convert to string and put into name[]*/
sprintf(name, "PID = %ld\n", (long)task_pid_nr(task));
/*show result to user that called the syscall*/
(my_tty->driver->ops->write) (my_tty, name, strlen(name)+1);
}
}
return 0;
}
My .h file:
asmlinkage long sys_process_name(char*process_name);
My Makefile:
obj-y := pname.o
I have included this system call to my syscalls_64.tbl and syscalls.h.
After successfully compiled the code above I tried this code to test the syscall testPname.c:
#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <string.h>
int main(){
char name[32];
puts("Enter process to find");
scanf("%s",name);
strtok(name, "\n");
long int status = syscall(335, name); //syscall number 335 and passing in the string.}
printf("System call returned %ld\n", status);
return 0;
}
But when I put a bunch of printk into the pname.c file and notice that "char* process_name" was never pass from my testPname to the syscall, so the strcmp was never reached. I've tried browsing for a way to pass the parameter to the system call but haven't been able to get there.
Related
I want to read file from kernel module which contains some parameters
I used the following source code
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h> // Needed by filp
#include <linux/rbtree.h>
#include <linux/time.h>
#include <linux/atomic.h>
#include <linux/proc_fs.h>
#include <linux/jiffies.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/inetdevice.h>
#include <linux/if_ether.h>
int init_module(void)
{
// Create variables
struct file *f;
char buf[128];
mm_segment_t fs;
int i;
unsigned long long offset = 0;
// Init the buffer with 0
for(i=0;i<128;i++)
buf[i] = 0;
// To see in /var/log/messages that the module is operating
// I am using Fedora and for the test I have chosen following file
// Obviously it is much smaller than the 128 bytes, but hell with it =)
f = filp_open("/etc/lsb-release", O_RDONLY, 0);
if(f == NULL)
printk(KERN_ALERT "filp_open error!!.\n");
else{
// Get current segment descriptor
fs = get_fs();
// Set segment descriptor associated to kernel space
set_fs(get_ds());
// Read the file
f->f_op->read(f, buf, 128, &f->f_pos);
// Restore segment descriptor
set_fs(fs);
// See what we read from file
printk(KERN_INFO "buf:%s\n",buf);
}
filp_close(f,NULL);
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "My module is unloaded\n");
}
But this function crash. I debug it and I found that f->f_op->read is NULL
My kernel version is 4.15 and my ubuntu is 16
What I am missing?
Why f->f_op->read is NULL ?
How to read some parameter from user space in the load of module ? I think better to use file. If so, How to read file in kernel ?
I am trying to some get Linux kernel version information by calling uname system call, but I am getting a compiler error saying ‘struct utsname’ has no member named ‘domainname’
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/utsname.h>
#define _GNU_SOURCE
int main(void) {
struct utsname buffer;
errno = 0;
if (uname(&buffer) != 0) {
perror("uname");
exit(EXIT_FAILURE);
}
printf("system name = %s\n", buffer.sysname);
printf("node name = %s\n", buffer.nodename);
printf("release = %s\n", buffer.release);
printf("version = %s\n", buffer.version);
printf("machine = %s\n", buffer.machine);
#ifdef _GNU_SOURCE
printf("domain name = %s\n", buffer.domainname);
#endif
return EXIT_SUCCESS;
}
according to https://linux.die.net/man/2/uname struct utsname is
struct utsname {
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined
network" */
char release[]; /* Operating system release (e.g., "2.6.28") */
char version[]; /* Operating system version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};
I am not sure what I missed here
From man feature_test_macros:
NOTE: In order to be effective, a feature test macro must be defined
before including any header files
It's:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/utsname.h>
I tried many guides and for some reason, my syscall isn't even returning a 0 as it should. Where am I going wrong? Here are my steps to creating one:
inside "/linux/" folder:
I created a folder called "mycall" with files mycall.c, mycall.h, & Makefile
mycall.c:
#include <linux/kernel.h>
#include <linux/init.h>
#include <sched.h>
#include <syscalls.h>
#include <linux/unistd.h>
#include "mycall.h"
asmlinkage long sys_mysyscall(int *id, int username, int *size)
{
printk("hello");
return 0;
}
mycall.h:
asmlinkage long sys_mysyscall(int *id, int username, int *size);
Makefile:
obj-y := mycall.o
then I went into all of the places where I THOUGHT i should declare the syscall.
// inside of /linux/include/linux/syscalls.h
asmlinkage long sys_mysyscall(int __user *myid, int username, int __user *size);
then to
// inside of /linux/arch/arm/tools/syscall.tbl
398 common mysyscall sys_mysyscall
finally i added
// inside of /linux/Makefile
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypt/ block/ mycall/
thats my set up. then the final thing I do is make a userspace in /linux directory
userspace.c:
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <pwd.h>
int main ()
{
int id = 0;
int username = 7;
int size = 2;
int ret_val = syscall(398, &id, username, &size);
printf("%d\n", ret_val);
return 0;
}
the return value is -1 so this doesn't work. please let me know what I'm doing wrong. I have read several implementation guides.
ERRNO is 38 . Function not implemented.
I'm trying to write a linux driver. The kernel version is 2.4.18 and the distribution is Red Hat linux 8.0.
The code of my driver is:
#define LINUX
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/fs.h>
#include <asm-i386/semaphore.h>
#include "rng.h"
#include <linux/random.h>
#include <linux/slab.h>
#define DEVICE_NAME "rng"
#define BUF_LEN 80
static int major;
int init_module();
void cleanup_module();
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
struct file_operations my_fops = {
open: device_open,
release: device_release,
};
/* Init and Cleanup */
int init_module() {
SET_MODULE_OWNER(&my_fops);
major = register_chrdev(0, DEVICE_NAME, &my_fops);
return 0;
}
void cleanup_module() {
int ret = unregister_chrdev(major, DEVICE_NAME);
if (ret < 0)
printk("Error in unregister_chrdev: %d\n", ret);
}
static int device_open(struct inode *inode, struct file *file) {
file->f_op=&my_fops;
return 1;
}
static int device_release(struct inode *inode, struct file *file) {
return 0;
}
And the code I'm using in order to test my driver is:
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int openTest() {
int game1 = open("/dev/game1", O_RDONLY); // SEGMENTATION FAULT
int retValue=1;
close(game1);
return retValue;
}
int main() {
int res;
if (openTest() < 1) {
fprintf(stderr, "open didnt work\n");
return -1;
}
fprintf(stderr, "everything works :)\n");
return 0;
}
In the code above, I'm getting a segmentation fault when I'm trying to open the device. Can somebody explain to me why I'm getting this segmentation fault? I really don't understand.
Thanks a lot!
In Linux kernel land, it is convention to return a 0 (zero) when there are no errors. Your device_open() routine is hardcoded to return a 1 (one), which may be causing your segfault.
This Linux Device Drivers book may be helpful to you. The linked edition is written for kernel 2.0.x - 2.4.x, so the information should be appropriate for the dusty and ancient kernel you are using.
This line seems to be wrong file->f_op=&my_fops;
Basically when writing a linux driver operations are setup at build time itself.
I'd like to know if it is somehow possible to run system("pwd") on the current DIR. So for example let's have this folder structure:
example
>test
>>file
>test2
>>file3
>>file4
And with opendir() and readdir() I'll get to file3, and I want to use system("pwd") to get the path ..../example/test2/file3. Is this somehow possible, or will pwd return the path to main.c all the time?
Simply opening and reading directories does not change the current working directory. However, changing directory in your program will.
for reference,
#include <unistd.h>
#include <stdio.h>
int main() {
char cwd[1024];
chdir("/path/to/change/directory/to");
getcwd(cwd, sizeof(cwd));
printf("Current working dir: %s\n", cwd);
}
For POSIX systems I found three solutions:
Get value from an environment variables "PWD"
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#define IS_POSIX 1
#else
#define IS_POSIX 0
#endif
int main (int argv, char **argc)
{
if (IS_POSIX == 1) {
puts("Path info by use environment variable PWD:");
printf("\tWorkdir: %s\n", getenv("PWD"));
printf("\tFilepath: %s/%s\n", getenv("PWD"), __FILE__);
}
return 0;
}
Result:
Path info by use environment variable PWD:
Workdir: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils
Filepath: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils/main.c
Use getcwd()
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#define IS_POSIX 1
#include <unistd.h>
#else
#define IS_POSIX 0
#endif
int main (int argv, char **argc)
{
if (IS_POSIX == 1) {
char cwd[1024];
getcwd(cwd, sizeof(cwd));
puts("Path info by use getcwd():");
printf("\tWorkdir: %s\n", cwd);
printf("\tFilepath: %s/%s\n", cwd, __FILE__);
}
return 0;
}
Result
Path info by use getcwd():
Workdir: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils
Filepath: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils/main.c
Execute system command "pwd" and read output
#ifdef __unix__
#define IS_POSIX 1
#define _BSD_SOURCE
#else
#define IS_POSIX 0
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argv, char **argc)
{
if (IS_POSIX == 1) {
char buffer[500];
FILE *output;
// read output of a command
output = popen("/bin/pwd", "r");
char *pwd = fgets(buffer, sizeof(buffer), output);
// strip '\n' on ending of a line
pwd = strtok(pwd, "\n");
puts("Path info by execute shell command 'pwd':");
printf("\tWorkdir: %s\n", pwd);
printf("\tFilepath: %s/%s\n", pwd, __FILE__);
}
return 0;
}
Result:
Path info by execute shell command 'pwd':
Workdir: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils
Filepath: /media/setivolkylany/WorkDisk/Programming/Projects/c-utils/main.c
You can use chdir(2) to change dir from C, then system("pwd"); will give you what ever directory you chdir'ed to.
The C-equvivalent of the pwd-command is getcwd(3).
When you use system(...) call with Windows and Linux it just executes one command. It is possible to do the same using file with commands (you can create it with C code), but my oppinion is, that you should use nftw() to get dirrectories and after that use opendir()/readdir().
How to not hardcode the path length with pathconf
I believe this is the correct way to do it:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
long n;
char *buf;
n = pathconf(".", _PC_PATH_MAX);
assert(n != -1);
buf = malloc(n * sizeof(*buf));
assert(buf);
if (getcwd(buf, n) == NULL) {
perror("getcwd");
exit(EXIT_FAILURE);
} else {
printf("%s\n", buf);
}
free(buf);
return EXIT_SUCCESS;
}
GitHub upstream.
Compile and run:
gcc -Wall -Wextra -std=c11 -pedantic-errors -o getcwd.out getcwd.c
./getcwd.out
POSIX describes _PC_PATH_MAX it as:
The value returned for the variable {PATH_MAX} indicates the longest relative pathname that could be given if the specified directory is the process' current working directory. A process may not always be able to generate a name that long and use it if a subdirectory in the pathname crosses into a more restrictive file system.
Tested on Ubuntu 18.10, n == 4096 in this implementation.