├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── CMakeLists.txt ├── README.md ├── include └── kuio.h ├── kuio.yml └── main.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: Rinnegatamante 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | # Windows image file caches 4 | Thumbs.db 5 | ehthumbs.db 6 | 7 | # Folder config file 8 | Desktop.ini 9 | 10 | # Recycle Bin used on file shares 11 | $RECYCLE.BIN/ 12 | 13 | # Windows Installer files 14 | *.cab 15 | *.msi 16 | *.msm 17 | *.msp 18 | 19 | # Windows shortcuts 20 | *.lnk 21 | 22 | # ========================= 23 | # Operating System Files 24 | # ========================= 25 | 26 | # OSX 27 | # ========================= 28 | 29 | .DS_Store 30 | .AppleDouble 31 | .LSOverride 32 | 33 | # Thumbnails 34 | ._* 35 | 36 | # Files that might appear in the root of a volume 37 | .DocumentRevisions-V100 38 | .fseventsd 39 | .Spotlight-V100 40 | .TemporaryItems 41 | .Trashes 42 | .VolumeIcon.icns 43 | 44 | # Directories potentially created on remote AFP share 45 | .AppleDB 46 | .AppleDesktop 47 | Network Trash Folder 48 | Temporary Items 49 | .apdisk 50 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) 4 | if(DEFINED ENV{VITASDK}) 5 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") 6 | else() 7 | message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") 8 | endif() 9 | endif() 10 | 11 | project(kuio) 12 | include("${VITASDK}/share/vita.cmake" REQUIRED) 13 | 14 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q -Wall -O3 -std=gnu99") 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions") 16 | 17 | include_directories( 18 | ) 19 | 20 | link_directories( 21 | ${CMAKE_CURRENT_BINARY_DIR} 22 | ) 23 | 24 | add_definitions(-DRELEASE) 25 | 26 | add_executable(kuio main.c) 27 | 28 | target_link_libraries(kuio 29 | SceFios2KernelForDriver_stub 30 | SceIofilemgrForDriver_stub 31 | SceThreadmgrForDriver_stub 32 | SceSblACMgrForDriver_stub 33 | SceSysmemForDriver_stub 34 | SceSysclibForDriver_stub 35 | ) 36 | 37 | set_target_properties(kuio 38 | PROPERTIES LINK_FLAGS "-nostdlib" 39 | COMPILE_FLAGS "-D__VITA_KERNEL__" 40 | ) 41 | 42 | add_custom_target(kuio.skprx ALL 43 | COMMAND vita-elf-create -s -e ${CMAKE_SOURCE_DIR}/kuio.yml kuio kuio.velf ${CMAKE_SOURCE_DIR}/taihen.json 44 | COMMAND vita-make-fself -c kuio.velf kuio.skprx 45 | ) 46 | 47 | add_dependencies(kuio.skprx kuio) 48 | 49 | vita_create_stubs(stubs kuio ${CMAKE_SOURCE_DIR}/kuio.yml KERNEL) 50 | 51 | install(FILES ${CMAKE_BINARY_DIR}/stubs/libkuio_stub.a DESTINATION lib) 52 | install(FILES ${CMAKE_BINARY_DIR}/stubs/libkuio_stub_weak.a DESTINATION lib) 53 | install(FILES ${CMAKE_SOURCE_DIR}/include/kuio.h DESTINATION include) 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | kuio is a lightweight kernel module for taiHen that allows user modules to access ux0:/data/ for basic I/O operations.
4 | It currently gives abstractions for these functions: 5 | 6 | *sceIoOpen* -> *kuIoOpen*
7 | *sceIoWrite* -> *kuIoWrite*
8 | *sceIoRead* -> *kuIoRead*
9 | *sceIoClose* -> *kuIoClose*
10 | *sceIoLseek* -> *kuIoLseek*
11 | *sceIoRemove* -> *kuIoRemove*
12 | *sceIoMkdir* -> *kuIoMkdir*
13 | *sceIoRmdir* -> *kuIoRmdir*
14 | *ftell* -> *kuIoTell* (kuIoLseek doesn't return position)

15 | 16 | # Credits 17 | 18 | Thanks to everyone who helped me during this journey trying to get SD access on user modules on #vitasdk and #henkaku. (noname120, xerpi, yifanlu, davee, xyz, frangarcj) 19 | -------------------------------------------------------------------------------- /include/kuio.h: -------------------------------------------------------------------------------- 1 | #ifndef _KUIO_H_ 2 | #define _KUIO_H_ 3 | 4 | /** 5 | * Open or create a file for reading or writing 6 | * 7 | * @par Example1: Open a file for reading 8 | * @code 9 | * SceUID fd; 10 | * kuIoOpen("device:/path/to/file", SCE_O_RDONLY, &fd) 11 | * if(!(fd) { 12 | * // error 13 | * } 14 | * @endcode 15 | * 16 | * @param file - Pointer to a string holding the name of the file to open 17 | * @param flags - Libc styled flags that are or'ed together 18 | * @param res - Pointer to where to save the returned file descriptor. 19 | * 20 | */ 21 | int kuIoOpen(const char *file, int flags, SceUID* res); 22 | 23 | /** 24 | * Delete a descriptor 25 | * 26 | * @code 27 | * kuIoClose(fd); 28 | * @endcode 29 | * 30 | * @param fd - File descriptor to close 31 | * 32 | */ 33 | int kuIoClose(SceUID fd); 34 | 35 | /** 36 | * Read input 37 | * 38 | * @par Example: 39 | * @code 40 | * kuIoRead(fd, data, 100); 41 | * @endcode 42 | * 43 | * @param fd - Opened file descriptor to read from 44 | * @param data - Pointer to the buffer where the read data will be placed 45 | * @param size - Size of the read in bytes 46 | * 47 | */ 48 | int kuIoRead(SceUID fd, void *data, SceSize size); 49 | 50 | /** 51 | * Write output 52 | * 53 | * @par Example: 54 | * @code 55 | * kuIoWrite(fd, data, 100); 56 | * @endcode 57 | * 58 | * @param fd - Opened file descriptor to write to 59 | * @param data - Pointer to the data to write 60 | * @param size - Size of data to write 61 | * 62 | */ 63 | int kuIoWrite(SceUID fd, const void *data, SceSize size); 64 | 65 | 66 | /** 67 | * Reposition read/write file descriptor offset 68 | * 69 | * @par Example: 70 | * @code 71 | * kuIoLseek(fd, -10, SEEK_END); 72 | * @endcode 73 | * 74 | * @param fd - Opened file descriptor with which to seek 75 | * @param offset - Relative offset from the start position given by whence 76 | * @param whence - Set to SEEK_SET to seek from the start of the file, SEEK_CUR 77 | * seek from the current position and SEEK_END to seek from the end. 78 | * 79 | */ 80 | int kuIoLseek(SceUID fd, int offset, int whence); 81 | 82 | /** 83 | * Remove directory entry 84 | * 85 | * @param file - Path to the file to remove 86 | * 87 | */ 88 | int kuIoRemove(const char *file); 89 | 90 | /** 91 | * Make a directory file 92 | * 93 | * @param dir 94 | * @param mode - Access mode. 95 | */ 96 | int kuIoMkdir(const char *dir); 97 | 98 | /** 99 | * Remove a directory file 100 | * 101 | * @param path - Removes a directory file pointed by the string path 102 | */ 103 | int kuIoRmdir(const char *path); 104 | 105 | /** 106 | * Return the position in the file 107 | * 108 | * @param fd - Opened file descriptor with which to seek 109 | * @param pos - The position in the file after the seek 110 | * 111 | */ 112 | int kuIoTell(SceUID fd, SceOff* pos); 113 | 114 | #endif -------------------------------------------------------------------------------- /kuio.yml: -------------------------------------------------------------------------------- 1 | kuio: 2 | attributes: 7 3 | version: 4 | major: 1 5 | minor: 1 6 | main: 7 | start: module_start 8 | modules: 9 | kuioLibrary: 10 | syscall: true 11 | functions: 12 | - kuIoOpen 13 | - kuIoWrite 14 | - kuIoRead 15 | - kuIoClose 16 | - kuIoLseek 17 | - kuIoMkdir 18 | - kuIoRmdir 19 | - kuIoRemove 20 | - kuIoTell -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "include/kuio.h" 11 | 12 | #define SAFE_MODE 1 13 | 14 | int kuioCpyPath(SceUID pid, char *dst, int max, const char *src){ 15 | 16 | int res = 0x80010016; 17 | char path[0x400]; 18 | 19 | if(pid == SCE_GUID_KERNEL_PROCESS_ID) 20 | goto end; 21 | 22 | SceSSize str_res = ksceKernelStrncpyFromUser(path, src, sizeof(path)); 23 | 24 | if(str_res == 0 || str_res == sizeof(path) || strncmp(path, "sdstor", 6) == 0) 25 | goto end; 26 | 27 | res = ksceFiosKernelOverlayResolveSync(pid, 0, path, dst, max); 28 | if(res < 0) 29 | goto end; 30 | 31 | #ifdef SAFE_MODE 32 | if(strncmp(dst, "ux0:/data/", 10) != 0) 33 | res = 0x80010016; 34 | #endif 35 | 36 | end: 37 | return res; 38 | } 39 | 40 | int kuIoOpen(const char *file, int flags, SceUID *res){ 41 | 42 | uint32_t state; 43 | int result; 44 | SceUID pid, fd_guid, fd_puid = 0x80010016; 45 | char path_resolved[0x400]; 46 | 47 | ENTER_SYSCALL(state); 48 | 49 | pid = ksceKernelGetProcessId(); 50 | 51 | result = kuioCpyPath(pid, path_resolved, sizeof(path_resolved), file); 52 | if(result >= 0){ 53 | int curr_al = ksceKernelSetPermission(0x40); 54 | fd_guid = ksceIoOpen(path_resolved, flags, 6); 55 | ksceKernelSetPermission(curr_al); 56 | 57 | if(fd_guid >= 0){ 58 | fd_puid = kscePUIDOpenByGUID(pid, fd_guid); 59 | result = 0; 60 | }else{ 61 | fd_puid = fd_guid; 62 | result = fd_guid; 63 | } 64 | } 65 | 66 | ksceKernelMemcpyKernelToUser(res, &fd_puid, sizeof(*res)); 67 | EXIT_SYSCALL(state); 68 | return result; 69 | } 70 | 71 | int kuIoClose(SceUID fd){ 72 | 73 | int res = 0x80010016; 74 | uint32_t state; 75 | SceUID pid, fd_guid; 76 | ENTER_SYSCALL(state); 77 | 78 | int curr_al = ksceKernelSetPermission(0x40); 79 | 80 | pid = ksceKernelGetProcessId(); 81 | if(pid == SCE_GUID_KERNEL_PROCESS_ID) 82 | goto end; 83 | 84 | fd_guid = kscePUIDtoGUID(pid, fd); 85 | if(fd_guid < 0){ 86 | res = fd_guid; 87 | goto end; 88 | } 89 | 90 | res = ksceIoClose(fd_guid); 91 | if(res >= 0){ 92 | res = kscePUIDClose(pid, fd); 93 | } 94 | 95 | end: 96 | ksceKernelSetPermission(curr_al); 97 | EXIT_SYSCALL(state); 98 | return res; 99 | } 100 | 101 | int kuIoRead(SceUID fd, void *data, SceSize size){ 102 | 103 | uint32_t state; 104 | int res = 0x80010016; 105 | SceUID pid, fd_guid; 106 | void *kernel_page = NULL; 107 | SceSize kernel_size = 0; 108 | SceUInt32 kernel_offset = 0; 109 | 110 | ENTER_SYSCALL(state); 111 | 112 | int curr_al = ksceKernelSetPermission(0x40); 113 | 114 | pid = ksceKernelGetProcessId(); 115 | if(pid == SCE_GUID_KERNEL_PROCESS_ID) 116 | goto end; 117 | 118 | fd_guid = kscePUIDtoGUID(pid, fd); 119 | if(fd_guid < 0){ 120 | res = fd_guid; 121 | goto end; 122 | } 123 | 124 | int type = (ksceSblACMgrIsGameProgram(0) != 0) ? 0x11 : 1; 125 | 126 | SceUID mapid = ksceKernelMapUserBlock("KuioUserRefer", SCE_KERNEL_MEMORY_REF_PERM_USER_W, type, data, size, &kernel_page, &kernel_size, &kernel_offset); 127 | if(mapid < 0){ 128 | res = mapid; 129 | goto end; 130 | } 131 | 132 | res = ksceIoRead(fd_guid, (void *)(((uintptr_t)kernel_page) + kernel_offset), size); 133 | 134 | ksceKernelMemBlockRelease(mapid); 135 | 136 | end: 137 | ksceKernelSetPermission(curr_al); 138 | EXIT_SYSCALL(state); 139 | return res; 140 | } 141 | 142 | int kuIoWrite(SceUID fd, const void *data, SceSize size){ 143 | 144 | uint32_t state; 145 | int res = 0x80010016; 146 | SceUID pid, fd_guid; 147 | void *kernel_page = NULL; 148 | SceSize kernel_size = 0; 149 | SceUInt32 kernel_offset = 0; 150 | 151 | ENTER_SYSCALL(state); 152 | 153 | int curr_al = ksceKernelSetPermission(0x40); 154 | 155 | pid = ksceKernelGetProcessId(); 156 | if(pid == SCE_GUID_KERNEL_PROCESS_ID) 157 | goto end; 158 | 159 | fd_guid = kscePUIDtoGUID(pid, fd); 160 | if(fd_guid < 0){ 161 | res = fd_guid; 162 | goto end; 163 | } 164 | 165 | int type = (ksceSblACMgrIsGameProgram(0) != 0) ? 0x11 : 1; 166 | 167 | SceUID mapid = ksceKernelMapUserBlock("KuioUserRefer", SCE_KERNEL_MEMORY_REF_PERM_USER_R, type, data, size, &kernel_page, &kernel_size, &kernel_offset); 168 | if(mapid < 0){ 169 | res = mapid; 170 | goto end; 171 | } 172 | 173 | res = ksceIoWrite(fd_guid, (const void *)(((uintptr_t)kernel_page) + kernel_offset), size); 174 | 175 | ksceKernelMemBlockRelease(mapid); 176 | 177 | end: 178 | ksceKernelSetPermission(curr_al); 179 | EXIT_SYSCALL(state); 180 | return res; 181 | } 182 | 183 | int kuIoLseek(SceUID fd, int offset, int whence){ 184 | 185 | uint32_t state; 186 | int res = 0x80010016; 187 | SceUID pid, fd_guid; 188 | 189 | ENTER_SYSCALL(state); 190 | 191 | int curr_al = ksceKernelSetPermission(0x40); 192 | 193 | pid = ksceKernelGetProcessId(); 194 | if(pid == SCE_GUID_KERNEL_PROCESS_ID) 195 | goto end; 196 | 197 | fd_guid = kscePUIDtoGUID(pid, fd); 198 | if(fd_guid < 0){ 199 | res = fd_guid; 200 | goto end; 201 | } 202 | 203 | SceOff seek_res = ksceIoLseek(fd_guid, (int)offset, whence); 204 | if(seek_res < 0LL) 205 | res = (int)seek_res; 206 | else 207 | res = 0; 208 | 209 | end: 210 | ksceKernelSetPermission(curr_al); 211 | EXIT_SYSCALL(state); 212 | return res; 213 | } 214 | 215 | int kuIoTell(SceUID fd, SceOff *pos){ 216 | 217 | uint32_t state; 218 | int res = 0x80010016; 219 | SceUID pid, fd_guid; 220 | 221 | ENTER_SYSCALL(state); 222 | 223 | int curr_al = ksceKernelSetPermission(0x40); 224 | 225 | pid = ksceKernelGetProcessId(); 226 | if(pid == SCE_GUID_KERNEL_PROCESS_ID) 227 | goto end; 228 | 229 | fd_guid = kscePUIDtoGUID(pid, fd); 230 | if(fd_guid < 0){ 231 | res = fd_guid; 232 | goto end; 233 | } 234 | 235 | SceOff seek_res = ksceIoLseek(fd_guid, 0, SCE_SEEK_CUR); 236 | if(seek_res < 0LL){ 237 | res = (int)seek_res; 238 | seek_res = 0LL; 239 | }else{ 240 | res = 0; 241 | } 242 | 243 | ksceKernelMemcpyToUser(pos, &seek_res, sizeof(*pos)); 244 | 245 | end: 246 | ksceKernelSetPermission(curr_al); 247 | EXIT_SYSCALL(state); 248 | return res; 249 | } 250 | 251 | int kuIoMkdir(const char* dir){ 252 | 253 | uint32_t state; 254 | int res; 255 | char path_resolved[0x400]; 256 | 257 | ENTER_SYSCALL(state); 258 | 259 | res = kuioCpyPath(ksceKernelGetProcessId(), path_resolved, sizeof(path_resolved), dir); 260 | if(res >= 0){ 261 | int curr_al = ksceKernelSetPermission(0x40); 262 | res = ksceIoMkdir(path_resolved, 6); 263 | ksceKernelSetPermission(curr_al); 264 | } 265 | 266 | EXIT_SYSCALL(state); 267 | return res; 268 | } 269 | 270 | int kuIoRemove(const char *file){ 271 | 272 | uint32_t state; 273 | int res; 274 | char path_resolved[0x400]; 275 | 276 | ENTER_SYSCALL(state); 277 | 278 | res = kuioCpyPath(ksceKernelGetProcessId(), path_resolved, sizeof(path_resolved), file); 279 | if(res >= 0){ 280 | int curr_al = ksceKernelSetPermission(0x40); 281 | res = ksceIoRemove(path_resolved); 282 | ksceKernelSetPermission(curr_al); 283 | } 284 | 285 | EXIT_SYSCALL(state); 286 | return res; 287 | } 288 | 289 | int kuIoRmdir(const char *dir){ 290 | 291 | uint32_t state; 292 | int res; 293 | char path_resolved[0x400]; 294 | 295 | ENTER_SYSCALL(state); 296 | 297 | res = kuioCpyPath(ksceKernelGetProcessId(), path_resolved, sizeof(path_resolved), dir); 298 | if(res >= 0){ 299 | int curr_al = ksceKernelSetPermission(0x40); 300 | res = ksceIoRmdir(path_resolved); 301 | ksceKernelSetPermission(curr_al); 302 | } 303 | 304 | EXIT_SYSCALL(state); 305 | return res; 306 | } 307 | 308 | void _start() __attribute__ ((weak, alias ("module_start"))); 309 | int module_start(SceSize argc, const void *args) { 310 | return SCE_KERNEL_START_SUCCESS; 311 | } 312 | --------------------------------------------------------------------------------