#define _GNU_SOURCE
#include <linux/init.h>
#include <linux/socket.h>
#include <linux/module.h>
#include <linux/kernel.h> 
#include <linux/errno.h> 
#include <linux/types.h>
#include <linux/unistd.h>
#include <asm/cacheflush.h>  
#include <asm/page.h>  
#include <asm/current.h>
#include <linux/sched.h>
#include <linux/kallsyms.h>
#include <linux/syscalls.h>
#include <asm/string.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("mancha <mancha1@hush.com>");
MODULE_DESCRIPTION("disable x32 recvmmsg()");

unsigned long **syscall_table;

#define __NR_x32_recvmmsg 537

asmlinkage int (*orig_recvmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec *timeout);

static unsigned long **aquire_syscall_table(void)
{
  unsigned long int offset = PAGE_OFFSET;
  unsigned long **sct;

  while (offset < ULLONG_MAX) {
    sct = (unsigned long **)offset;
    if (sct[__NR_close] == (unsigned long *) sys_close) 
      return sct;
    offset += sizeof(void *);
  }
  printk(KERN_ALERT "Unable to get syscall table\n");
  return NULL;
}

void set_addr_rw(long unsigned int _addr)
{
    unsigned int level;
    pte_t *pte = lookup_address(_addr, &level);
    if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
}

void set_addr_ro(long unsigned int _addr)
{
    unsigned int level;
    pte_t *pte = lookup_address(_addr, &level);
    pte->pte = pte->pte &~_PAGE_RW;
}

asmlinkage int norecvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec *timeout) {

    printk(KERN_ALERT "x32 recvmmsg call intercepted\n");
    return -1;
}

static int __init init_recvmmsg(void) {

    if(!(syscall_table = aquire_syscall_table())) {
      printk(KERN_INFO "Unable to acquire syscall table\n");
      return -1;
    }
    printk(KERN_ALERT "x32 recvmmsg disabled\n");
    set_addr_rw((unsigned long)syscall_table);
    orig_recvmmsg = (void*)syscall_table[__NR_x32_recvmmsg];
    syscall_table[__NR_x32_recvmmsg] = (unsigned long*)norecvmmsg;  
    return 0;
}

static void __exit exit_recvmmsg(void) {

    syscall_table[__NR_x32_recvmmsg] = (unsigned long*)orig_recvmmsg;  
    set_addr_ro((unsigned long)syscall_table);
    printk(KERN_ALERT "x32 recvmmsg restored\n");
}

module_init(init_recvmmsg);
module_exit(exit_recvmmsg);
