Enhancing JSoS and a writeup of the JSoS Module Dumper
Part 1: How to add functionality to JSoS
Part 2: The Module Dumper explained
Part 1: How to add functionality to JSoS
As many of you are aware, @AmatCama recently unveiled a brand new webkit toolkit for the playstation vita refereed to as “Javascript on Steroids.” The system makes use of some very clever use of setjmp and longjmp to “cleanly” execute ROP and return to Javascript. I won’t go deeply into the mechanics of the process here, as Amat himself has very nicely written up the entire process, on his blog.
As a result of this, we can cleanly call functions from sony modules, while leaving javascript execution intact. However, its not quite as simple as that. As you can see from “samples.js” in the original release, there’s a bit more involved. You can think of it such that a javascript “wrapper” is required, to configure input and output, and to provide meaningful context in the case that multiple function calls are required for a single proceedure.
API.js contains the offsets and names for functions within the different modules. Currently only libc, SceNet, and LibKernel are supported, but with a bit of (very easy) work far more can be added.
I will go through the required “stuff” that goes into a wrapper, based on samples.js/list_dir(dirname):
function list_dir(dirname)
{
var scekernel = libraries.SceLibKernel.functions;
var dirname_a = allocate_memory(0x20);
var dirlist = allocate_memory(0x1000);
mymemcpy(dirname_a, dirname, dirname.length);
var fd = scekernel.sceIoDopen(dirname_a);
fd = Int(fd);
if(fd < 0){ logdbg("sceIoDopen() failed"); return; } logdbg("Listing: " + dirname); while (scekernel.sceIoDread(fd, dirlist) > 0){
myprintf(dirlist + 0x58);
}
logdbg("-\n");
}
The first line grabs the functions listed inside the libraries object (established in sploit.js via rop.js if you’re interested) and stores it in a local variable for ease of access. The next two lines allocate memory required by the function, and store the address of that memory in the two variables, dirname_a and dirlist. Mymemcpy places the name of the directory into the buffer at dirname_a. Next, the wrapper calls sceIoOpen with dirname_a (which remember, is a pointer, not the actual string). That function gets stored into an untyped JS var value as FD. To prevent javascript weirdness, the variable FD then gets forced to Int type, in order to allow for the fail check in the if block. Next, scekernel.sceIoDread is called with the directory handle FD and the pointer dirlist, which populates the memory at dirlist with strings of the directory. In this particular implementation, so long as the function returns a number > than 0 (<=0 indicates no more files to read) it will print the path.
So you can see, in order to call an SCE function, you have to allocate memory for it, and pass it pointers, as well as handle the return values and provide meaningful context. Some functions might also take integer values, or other data types.
I would propose that instead of printing within function wrappers, they should either return a pointer to the data object or return the data converted to a javascript object, depending on context. Most likely a pointer would be better as it provides more consistency.
Part 2: The Module Dumper explained
A few days ago, myself and Major Tom released an expansion to JSoS which automatically dumps modules from the vita. To do this, we looked at UVLoader’s resolve code. In particular, two functions and two data structures were interesting (fields removed from both structures for readability) :
RESOLVE_STUB(sceKernelGetModuleList, 0x2EF2581F);
RESOLVE_STUB(sceKernelGetModuleInfo, 0x36585DAF);
typedef struct segment_info
{
void *vaddr; // address in memory
u32_t memsz; // size in memory
} segment_info_t;
typedef struct loaded_module_info
{
char module_name[28];
segment_info_t segments[4];
} loaded_module_info_t;
Source: UVLoader
From looking at the behavior of UVLoader I was able to determine that sceKernelGetModuleList returns an array of integer-esque values which are UIDs for each module. On its own, that wouldn’t be very useful but we also have sceKernelGetModuleInfo, which takes a UID and propagates an array with pointers to the structure defined loaded_module_info, which contains the segment info, which contains the address and size of each segment of the module in memory. So that’s a bit of a mouthful, but its the simplest way to get at this information. Lets take a look at how its implemented!
function sceKernelGetModuleInfo_caller(caller){
return function (UID) {
var SIZE_OF_MODINFO = 440;
var m_mod_info_a = allocate_memory(SIZE_OF_MODINFO*4);
var return_code = caller(UID,m_mod_info_a);
var result = [return_code,m_mod_info_a];
return result
}
}
function sceKernelGetModuleList_caller(caller){
return function () {
var MAX_LOADED_MODS = 128;
var num_loaded = MAX_LOADED_MODS;
var modlist_a = allocate_memory(MAX_LOADED_MODS*4);
var num_loaded_a = allocate_memory(0x4);
aspace32[num_loaded_a/4] = num_loaded;
var return_code = caller(0xFF,modlist_a,num_loaded_a);
var result = [return_code,modlist_a,num_loaded_a];
return result
}
}
The code is very similar to that found in uvloader (not above, but take a look at the source). In both, global constants are established, then necessary buffers are created with proper sizes, then caller (passed by the calling function) is… ahem… called, with the proper input. The whole thing occurs inside of a function which is actually returned to the javascript context in sploit.js:
var sceKernelGetModuleList = sceKernelGetModuleList_caller(caller(syscall_a+16,libraries)); var sceKernelGetModuleInfo = sceKernelGetModuleInfo_caller(caller(syscall_a+32,libraries));
syscall_a+16 and +32 is actually the address in memory of sceKernelGetModuleList and sceKernelGetModuleInfo respectively, which Major Tom and I were able to determine with a bit of brute forcing made necessary by NID poisoning and syscall randomization.
Then all that is left to do is call sceKernelGetModuleList, walk over the returned array with a for loop, call sceKernelGetModuleInfo on each UID, and parse the output to get segment addresses and length. The end result?

Hopefully this will help you understand a bit more about how to work with the JSoS framework!
Sources:
Yifan’s UVLoader: link
Amat’s JSoS “Akai” framework: link
My Github: link
good job guys keep going!!!!
Nicely explained BBalling1. Great job to all the devs poking around the PS Vita.
Хакеры занимающиеся взломом Виты,так держать молодцы большие!!
Umm….
What?
Your mom
is HOT! 😀
but like really really HOT!!!!!!
What a noob, not having any idea what was written.
Пачаны привет всем с гбх от ашана)надеюсь виту взломают)
Ashen,привет!Ashen,я тоже ,на это надеюсь!
Русские сила! скажем нет Санкциям от ЕС!
Go hack the system!!!
“NID poisoning and syscall randomization”
I heard these words somewhere, and I think someone was going to explain them in part 3 of… something?
Also, that “mouthful” bit wasn’t very digestible for newbie programmers such as myself, but that’s okay as long as better ones get the idea.
Thanks for posting, it’s an interesting read.
good job
The source links are interchanged (UVLoader and the second one)
Cool.
Nice work gentlemen.
First thing: Bballng1(Codelion)
You a hard worker, you accomplished so much within a short period of time. Your work( n Snake) turned into honey n all the bee’s(devs) came rushing. Davee and amat we goin to see magic from those two
Secondly: I wonder what’s Commander Snake n Master Codestation have been in the kitchen hahaha anyway to all the devs in wololo all best in cracking the sweet nut
This is absolutely amazing. In no way would I ever be able to achieve something like this for myself with my limited programming knowledge. But having said that I fully intend to study and try to keep up with how this webkit vulnerability progresses into greater things so that when we do get to the stage of an “unlocked” vita, I can have a better understanding of how the insides of it work.
What I’m trying to say is please do not stop these writeup’s. They’re immensely helpful.
Thanks Wololo.
Usage is, I don’t know