Update dsd to v0.12.0; dsprot OK (#135)

* `Actor::mType` field

* Remove fake symbol `data_027e0254`

* `dsd format`

* eur: Enable dsprot decomp

* jp: Enable dsprot decomp

* dsprot OK

* `dsd format`

* Use new `weak` attribute

* Add relocations for exception table link-time constants

* Add exception table symbols

* Bump dsd to v0.12.0

* `#define FALSE 1` ???
Why did I make it 1?

* Truncate `data_ov001_020c27a8`
That data belongs to dsprot
This commit is contained in:
Aetias
2026-08-15 01:07:14 +02:00
committed by GitHub
parent ce24ddfc4e
commit 521100caed
104 changed files with 4547 additions and 2014 deletions
+152
View File
@@ -0,0 +1,152 @@
/* No dedicated header */
#include "dsprot.h"
#include "dsprot/encoding_constants.h"
#include "dsprot/encryptor.h"
#include "dsprot/integrity.h"
#include "dsprot/mac_owner.h"
#include "dsprot/rom_test.h"
// Functions to be encrypted (cannot be called directly)
u32 DetectFlashcart(void *callback);
u32 DetectNotFlashcart(void *callback);
u32 DetectEmulator(void *callback);
u32 DetectNotEmulator(void *callback);
u32 DetectDummy(void *callback);
u32 DetectNotDummy(void *callback);
#define DSP_OBFS_OFFSET (0x320)
#define FUNC_QUEUE_END (0)
typedef u32 (*TaskFunc)(void);
typedef void (*CallbackFunc)(void);
enum {
EXPECT_FALSE,
EXPECT_TRUE
};
// This was likely not originally an inline, but an inline is able to match here nicely
static inline u32 dsprotMain(u32 *func_queue, int expected_result, void *callback) {
BOOL ret;
BOOL func_result;
s32 compare_sum;
u32 i;
s32 func_result_sum;
// These two bit arrays must be signed to match
compare_sum = 0;
func_result_sum = 0;
for (i = 0; func_queue[i] != FUNC_QUEUE_END; i++) {
func_result = ((TaskFunc) (func_queue[i] - ENC_VAL_1 - DSP_OBFS_OFFSET))() != 0;
func_result_sum += func_result;
func_result_sum <<= 1;
compare_sum += TRUE;
compare_sum <<= 1;
}
if (expected_result == EXPECT_TRUE) {
ret = (func_result_sum >> 1) == (compare_sum >> 1);
} else {
ret = ((func_result_sum & compare_sum) >> 1) != 0;
}
if (callback != NULL && ret) {
((CallbackFunc) callback)();
}
return (u32) ret;
}
u32 DetectFlashcart(void *callback) {
u32 func_queue[32];
func_queue[2] = FUNC_QUEUE_END;
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_ROMTest_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
return dsprotMain(&func_queue[0], EXPECT_FALSE, callback);
}
u32 DetectNotFlashcart(void *callback) {
u32 func_queue[32];
func_queue[2] = FUNC_QUEUE_END;
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_ROMTest_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
return dsprotMain(&func_queue[0], EXPECT_TRUE, callback);
}
u32 DetectEmulator(void *callback) {
u32 func_queue[32];
func_queue[2] = FUNC_QUEUE_END;
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_MACOwner_IsBad, ENC_VAL_1) + DSP_OBFS_OFFSET;
return dsprotMain(&func_queue[0], EXPECT_FALSE, callback);
}
u32 DetectNotEmulator(void *callback) {
u32 func_queue[32];
func_queue[2] = FUNC_QUEUE_END;
func_queue[0] = ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
func_queue[1] = ADDR_PLUS_ADDEND(RunEncrypted_Integrity_MACOwner_IsGood, ENC_VAL_1) + DSP_OBFS_OFFSET;
return dsprotMain(&func_queue[0], EXPECT_TRUE, callback);
}
u32 DetectDummy(void *callback) {
u32 func_queue[32];
// Prevent optimization of the function queue processing
*(u32 *) &func_queue[0] = FUNC_QUEUE_END;
return dsprotMain(&func_queue[0], EXPECT_FALSE, callback);
}
u32 DetectNotDummy(void *callback) {
u32 func_queue[32];
// Prevent optimization of the function queue processing
*(u32 *) &func_queue[0] = FUNC_QUEUE_END;
return dsprotMain(&func_queue[0], EXPECT_TRUE, callback);
}
// DECRYPTION_WRAPPER(DetectFlashcart, 0xac, 0x2e8b, DSProt_)
// DECRYPTION_WRAPPER(DetectNotFlashcart, 0xac, 0x2e8b, DSProt_)
// DECRYPTION_WRAPPER(DetectEmulator, 0xac, 0x2e8b, DSProt_)
// DECRYPTION_WRAPPER(DetectNotEmulator, 0xac, 0x2e8b, DSProt_)
// DECRYPTION_WRAPPER(DetectDummy, 0x94, 0x2e8b, DSProt_)
// DECRYPTION_WRAPPER(DetectNotDummy, 0x94, 0x2e8b, DSProt_)
// // clang-format off
// asm void NitroStaticInit() {
// stmdb sp!, {lr};
// add r0, pc, #0x4;
// bl Encryptor_DecodeFunctionTable;
// ldmia sp!, {pc};
// dcd DSProt_DetectFlashcart + ENC_VAL_1
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
// dcd DSProt_DetectNotFlashcart + ENC_VAL_1
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
// dcd DSProt_DetectEmulator + ENC_VAL_1
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
// dcd DSProt_DetectNotEmulator + ENC_VAL_1
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
// dcd DSProt_DetectDummy + ENC_VAL_1
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
// dcd DSProt_DetectNotDummy + ENC_VAL_1
// dcd DSProt_BSS + 0x68 + ENC_VAL_1
// dcd 0
// dcd 0
// dcd DSProt_Garbage + 0x1400
// }
// // clang-format on
+137
View File
@@ -0,0 +1,137 @@
#include "dsprot/encryptor.h"
#include <nitro/os/cache.h>
#include "dsprot/bss.h"
#include "dsprot/encoding_constants.h"
#include "dsprot/rc4.h"
#define ROTL(x, a) ((a) == 0 ? (x) : (((x) << (a)) | ((x) >> (32 - (a)))))
static void clearDataAndInstructionCache(void *start_addr, u32 num_bytes);
static void clearDataAndInstructionCache(void *start_addr, u32 num_bytes) {
DC_FlushRange(start_addr, num_bytes);
IC_InvalidateRange(start_addr, num_bytes);
}
u32 Encryptor_CategorizeInstruction(u32 instruction) {
u8 opcode;
opcode = instruction >> INS_OPCODE_SHIFT;
// Branch instruction
if ((opcode & 0x0E) == 0x0A) {
// BLX immediate type
if ((opcode & 0xF0) == 0xF0) {
return INS_TYPE_BLXIMM;
}
// Link bit
if (opcode & INS_OPCODE_LINKBIT) {
return INS_TYPE_BL;
} else {
return INS_TYPE_B;
}
}
return INS_TYPE_OTHER;
}
void Encryptor_DecodeFunctionTable(FuncInfo *functions) {
u32 *addr;
u32 size;
u32 *end_addr;
if (functions == NULL) {
return;
}
for (; functions->obfs_addr != 0; functions++) {
addr = (u32 *) functions->obfs_addr;
size = functions->obfs_size - (u32) &DSProt_BSS - ENC_VAL_1;
if (addr == NULL) {
break;
}
addr = (u32 *) ((u32) addr - ENC_VAL_1);
end_addr = addr + (size / 4);
for (; addr < end_addr; addr++) {
switch (Encryptor_CategorizeInstruction(*addr)) {
case INS_TYPE_BLXIMM:
case INS_TYPE_BL: {
u32 opcode = (*addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
u32 operands = ((*addr & INS_OPERANDS_MASK) - ENC_VAL_1) & INS_OPERANDS_MASK;
*addr = opcode | operands;
} break;
case INS_TYPE_B: {
u32 opcode = (*addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
u32 operands = ((*addr & INS_OPERANDS_MASK) - ENC_VAL_2) & INS_OPERANDS_MASK;
*addr = opcode | operands;
} break;
default: {
u8 *addr_bytes = (u8 *) addr;
*addr = (addr_bytes[0] ^ ENC_BYTE_A) | ((addr_bytes[1] ^ ENC_BYTE_B) << 8) |
((addr_bytes[2] ^ ENC_BYTE_C) << 16) | ((addr_bytes[3] ^ ENC_BYTE_D) << 24);
} break;
}
}
clearDataAndInstructionCache((void *) (functions->obfs_addr - ENC_VAL_1), size);
}
}
static inline void expandRC4Key(u32 seed_key, u32 size, u32 *expanded_key) {
expanded_key[0] = ROTL(seed_key, 0) ^ size;
expanded_key[1] = ROTL(seed_key, 8) ^ size;
expanded_key[2] = ROTL(seed_key, 16) ^ size;
expanded_key[3] = ROTL(seed_key, 24) ^ size;
}
void *Encryptor_DecryptFunction(u32 key, u32 func_addr, u32 size) {
u32 expanded_key[4];
void *func_ptr;
// Deobfuscate arguments
func_ptr = (void *) func_addr;
func_ptr -= ENC_VAL_1;
size -= (u32) &DSProt_BSS + ENC_VAL_1;
key -= (u32) &DSProt_BSS + ENC_VAL_1;
expandRC4Key(key, size, &expanded_key[0]);
RC4_InitAndDecryptInstructions(&expanded_key[0], func_ptr, func_ptr, size);
clearDataAndInstructionCache(func_ptr, size);
return func_ptr;
}
u32 Encryptor_EncryptFunction(u32 key, u32 func_addr, u32 size) {
u32 expanded_key[4];
void *func_ptr;
// Deobfuscate arguments
func_ptr = (void *) func_addr;
func_ptr -= ENC_VAL_1;
size -= (u32) &DSProt_BSS + ENC_VAL_1;
key -= (u32) &DSProt_BSS + ENC_VAL_1;
// Change key
key += func_addr >> 20;
expandRC4Key(key, size, &expanded_key[0]);
RC4_InitAndEncryptInstructions(&expanded_key[0], func_ptr, func_ptr, size);
clearDataAndInstructionCache(func_ptr, size);
// Re-obfuscate key
key += (u32) &DSProt_BSS + ENC_VAL_1;
return key;
}
+8
View File
@@ -0,0 +1,8 @@
#include "dsprot/bss.h"
#include "dsprot/garbage.h"
u8 DSProt_BSS[4];
const u32 DSProt_Garbage[6] = {
0xebaa0114, 0x40064eb7, 0x5f013696, 0xe5211f83, 0xe7ef335b, 0xe84b197c,
};
+120
View File
@@ -0,0 +1,120 @@
#include "dsprot/integrity.h"
#include "dsprot/encoding_constants.h"
#include "dsprot/mac_owner.h"
#include "dsprot/rom_test.h"
// Functions to be encrypted (cannot be called directly)
u32 Integrity_MACOwner_IsBad(void);
u32 Integrity_MACOwner_IsGood(void);
u32 Integrity_ROMTest_IsBad(void);
u32 Integrity_ROMTest_IsGood(void);
#define INTEGRITY_OBFS_OFFSET (ENC_VAL_1 & ~0xFFF)
// This was likely not originally an inline, but an inline is able to match here nicely
static inline u32 checkDecryptionWrapper(u8 *addr, u32 match_ret, u32 mismatch_ret) {
u32 offset;
addr += INTEGRITY_OBFS_OFFSET;
offset = ENC_VAL_1 - INTEGRITY_OBFS_OFFSET;
// The bytes checked here are from the `run_encrypted_func` macro defined in asm_macro.inc:
// e92d00f0 stmfd sp!, {r4-r7}
// e92d000f stmfd sp!, {r0-r3}
// e8bd00f0 ldmfd sp!, {r4-r7}
// e59f103c ldr r1, [pc, #60]
if (addr[offset++] != 0xF0) {
return mismatch_ret;
}
if (addr[offset++] != 0x00) {
return mismatch_ret;
}
if (addr[offset++] != 0x2D) {
return mismatch_ret;
}
if (addr[offset++] != 0xE9) {
return mismatch_ret;
}
if (addr[offset++] != 0x0F) {
return mismatch_ret;
}
if (addr[offset++] != 0x00) {
return mismatch_ret;
}
if (addr[offset++] != 0x2D) {
return mismatch_ret;
}
if (addr[offset++] != 0xE9) {
return mismatch_ret;
}
if (addr[offset++] != 0xF0) {
return mismatch_ret;
}
if (addr[offset++] != 0x00) {
return mismatch_ret;
}
if (addr[offset++] != 0xBD) {
return mismatch_ret;
}
if (addr[offset++] != 0xE8) {
return mismatch_ret;
}
if (addr[offset++] != 0x60) {
return mismatch_ret;
}
if (addr[offset++] != 0x10) {
return mismatch_ret;
}
if (addr[offset++] != 0x9F) {
return mismatch_ret;
}
if (addr[offset++] != 0xE5) {
return mismatch_ret;
}
return match_ret;
}
u32 Integrity_MACOwner_IsBad(void) {
u8 *addr;
u32 ret;
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsBad, ENC_VAL_1) - (ENC_VAL_1 * 2);
ret = (u32) addr + 1;
return checkDecryptionWrapper(addr, 0, ret);
}
u32 Integrity_MACOwner_IsGood(void) {
u8 *addr;
u32 ret;
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_MACOwner_IsGood, ENC_VAL_1) - (ENC_VAL_1 * 2);
ret = (u32) addr;
return checkDecryptionWrapper(addr, ret + 1, 0);
}
u32 Integrity_ROMTest_IsBad(void) {
u8 *addr;
u32 ret;
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsBad, ENC_VAL_1) - (ENC_VAL_1 * 2);
ret = (u32) addr + 1;
return checkDecryptionWrapper(addr, 0, ret);
}
u32 Integrity_ROMTest_IsGood(void) {
u8 *addr;
u32 ret;
addr = (u8 *) ADDR_PLUS_ADDEND(RunEncrypted_ROMTest_IsGood, ENC_VAL_1) - (ENC_VAL_1 * 2);
ret = (u32) addr;
return checkDecryptionWrapper(addr, ret + 1, 0);
}
+55
View File
@@ -0,0 +1,55 @@
#include "dsprot/mac_owner.h"
#include <nitro/os.h>
// Functions to be encrypted (cannot be called directly)
u32 MACOwner_IsBad(void);
u32 MACOwner_IsGood(void);
#define MAC_ADDR_SIZE (6)
#define ENC_MAC_ADDR_BYTE (0xFF)
static const u8 bad_mac_addr[MAC_ADDR_SIZE] = {0x00 ^ ENC_MAC_ADDR_BYTE, 0x09 ^ ENC_MAC_ADDR_BYTE, 0xBF ^ ENC_MAC_ADDR_BYTE,
0x00 ^ ENC_MAC_ADDR_BYTE, 0x00 ^ ENC_MAC_ADDR_BYTE, 0x31 ^ ENC_MAC_ADDR_BYTE};
static inline u32 testMACOwner(u32 pass_ret, u32 fail_ret) {
u8 mac_addr[MAC_ADDR_SIZE];
OSOwnerInfo owner_info;
int i;
u32 ret;
OS_func_0176(&mac_addr[0]);
for (i = 0; i < MAC_ADDR_SIZE; i++) {
if (bad_mac_addr[i] != (mac_addr[i] ^ ENC_MAC_ADDR_BYTE)) {
break;
}
}
OS_func_0177(&owner_info);
if (i == MAC_ADDR_SIZE && owner_info.birthday.month == 1 && owner_info.birthday.day == 1 &&
owner_info.nickNameLength == 0) {
ret = fail_ret;
goto EXIT;
}
for (i = 0; i < MAC_ADDR_SIZE; i++) {
if (mac_addr[i] != 0x00) {
ret = pass_ret;
goto EXIT;
}
}
ret = fail_ret;
EXIT:
return ret;
}
u32 MACOwner_IsBad(void) {
return testMACOwner(0, 1);
}
u32 MACOwner_IsGood(void) {
return testMACOwner(1, 0);
}
+210
View File
@@ -0,0 +1,210 @@
#include "dsprot/rc4.h"
#include "dsprot/encoding_constants.h"
#include "dsprot/encryptor.h"
#define RC4_KEY_SIZE (16)
typedef struct {
int i;
int j;
u8 S[256];
} RC4_Ctx;
// Functions to be encoded (cannot be static)
void RC4_Init(RC4_Ctx *ctx, const void *key, u32 key_len);
u8 RC4_Byte(RC4_Ctx *ctx);
u32 RC4_InitSBox(u8 *sbox);
u32 RC4_EncryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size);
u32 RC4_DecryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size);
void RC4_Init(RC4_Ctx *ctx, const void *key, u32 key_len) {
u8 tmp1;
u8 tmp2;
int Ki;
u8 Si;
int i;
int j;
Ki = 0;
Si = 0;
ctx->i = 0;
ctx->j = 0;
for (j = 0; j < 256; j++) {
ctx->S[j] = j;
}
// Modification to RC4: i = 255 -> 0, instead of 0 -> 255
for (i = 255; i >= 0; i--) {
tmp1 = ctx->S[i];
Si = Si + ((u8 *) key)[Ki] + tmp1;
tmp2 = ctx->S[Si];
ctx->S[Si] = tmp1;
ctx->S[i] = tmp2;
Ki++;
if (Ki >= key_len) {
Ki = 0;
}
}
}
u8 RC4_Byte(RC4_Ctx *ctx) {
u8 i;
u8 ival;
u8 j;
u8 jval;
i = ctx->i + 1;
ival = ctx->S[i];
j = ival + ctx->j;
jval = ctx->S[j];
ctx->i = i;
ctx->j = j;
ctx->S[j] = ival;
ctx->S[i] = jval;
return ctx->S[(ival + jval) & 0xFF];
}
u32 RC4_InitSBox(u8 *sbox) {
int i;
for (i = 0; i < 256; i++) {
sbox[i] = (u8) i ^ ENC_SBOX_XOR;
}
return 0;
}
u32 RC4_EncryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size) {
u8 sbox[256];
ulong offset;
u8 *src_bytes;
u8 *dst_bytes;
if (size & 3) {
return -1;
}
src_bytes = (u8 *) src;
dst_bytes = (u8 *) dst;
RC4_InitSBox(&sbox[0]);
for (offset = 0; offset < size; offset += 4) {
switch (Encryptor_CategorizeInstruction(*(u32 *) (src_bytes + offset))) {
case INS_TYPE_BLXIMM:
case INS_TYPE_BL: {
u32 opcode;
u32 operands;
u32 *src_addr = (u32 *) (src_bytes + offset);
u32 *dst_addr = (u32 *) (dst_bytes + offset);
*dst_addr = *src_addr;
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
operands = ((*dst_addr & INS_OPERANDS_MASK) + ENC_VAL_2) & INS_OPERANDS_MASK;
*dst_addr = opcode | operands;
} break;
case INS_TYPE_B: {
u32 opcode;
u32 operands;
u32 *src_addr = (u32 *) (src_bytes + offset);
u32 *dst_addr = (u32 *) (dst_bytes + offset);
*dst_addr = *src_addr;
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
operands = ((*dst_addr & INS_OPERANDS_MASK) + ENC_VAL_1) & INS_OPERANDS_MASK;
*dst_addr = opcode | operands;
} break;
default:
dst_bytes[offset] = src_bytes[offset] ^ RC4_Byte(ctx);
dst_bytes[offset + 1] = src_bytes[offset + 1] ^ RC4_Byte(ctx);
dst_bytes[offset + 2] = sbox[src_bytes[offset + 2]];
dst_bytes[offset + 3] = src_bytes[offset + 3];
break;
}
}
return 0;
}
u32 RC4_DecryptInstructions(RC4_Ctx *ctx, void *src, void *dst, u32 size) {
u8 sbox[256];
ulong offset;
u8 *src_bytes;
u8 *dst_bytes;
if (size & 3) {
return -1;
}
src_bytes = (u8 *) src;
dst_bytes = (u8 *) dst;
RC4_InitSBox(&sbox[0]);
for (offset = 0; offset < size; offset += 4) {
switch (Encryptor_CategorizeInstruction(*(u32 *) (src_bytes + offset))) {
case INS_TYPE_BLXIMM:
case INS_TYPE_BL: {
u32 opcode;
u32 operands;
u32 *src_addr = (u32 *) (src_bytes + offset);
u32 *dst_addr = (u32 *) (dst_bytes + offset);
*dst_addr = *src_addr;
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
operands = ((*dst_addr & INS_OPERANDS_MASK) - ENC_VAL_1) & INS_OPERANDS_MASK;
*dst_addr = opcode | operands;
} break;
case INS_TYPE_B: {
u32 opcode;
u32 operands;
u32 *src_addr = (u32 *) (src_bytes + offset);
u32 *dst_addr = (u32 *) (dst_bytes + offset);
*dst_addr = *src_addr;
opcode = (*dst_addr & INS_OPCODE_MASK) ^ (INS_OPCODE_LINKBIT << INS_OPCODE_SHIFT);
operands = ((*dst_addr & INS_OPERANDS_MASK) - ENC_VAL_2) & INS_OPERANDS_MASK;
*dst_addr = opcode | operands;
} break;
default:
dst_bytes[offset] = src_bytes[offset] ^ RC4_Byte(ctx);
dst_bytes[offset + 1] = src_bytes[offset + 1] ^ RC4_Byte(ctx);
dst_bytes[offset + 2] = sbox[src_bytes[offset + 2]];
dst_bytes[offset + 3] = src_bytes[offset + 3];
break;
}
}
return 0;
}
u32 RC4_InitAndEncryptInstructions(void *key, void *dst, void *src, u32 size) {
RC4_Ctx ctx;
RC4_Init(&ctx, key, RC4_KEY_SIZE);
// Must coerce output to -1 or 0 like this to match
return RC4_EncryptInstructions(&ctx, dst, src, size) == -1 ? -1 : 0;
}
u32 RC4_InitAndDecryptInstructions(void *key, void *dst, void *src, u32 size) {
RC4_Ctx ctx;
RC4_Init(&ctx, key, RC4_KEY_SIZE);
// Must coerce output to -1 or 0 like this to match
return RC4_DecryptInstructions(&ctx, dst, src, size) == -1 ? -1 : 0;
}
+67
View File
@@ -0,0 +1,67 @@
#include "dsprot/rom_test.h"
#include "dsprot/rom_util.h"
#include <nitro/card.h>
// Functions to be encrypted (cannot be called directly)
u32 ROMTest_IsBad(void);
u32 ROMTest_IsGood(void);
#define ROM_BLOCK_SIZE CARD_ROM_PAGE_SIZE
static inline u32 testROM(u32 pass_ret, u32 fail_ret) {
// Extra CRC entry is required to match
u32 crcs[7];
u8 rom_buf[ROM_BLOCK_SIZE];
int i;
u32 rom_addr;
u32 ret;
rom_addr = 0x1000;
for (i = 0; i < 6; i++) {
RunEncrypted_ROMUtil_Read(&rom_buf[0], rom_addr, ROM_BLOCK_SIZE);
crcs[i] = RunEncrypted_ROMUtil_CRC32(&rom_buf[0], ROM_BLOCK_SIZE);
if (i == 2) {
// Has to be like this to match
rom_addr = 1;
rom_addr <<= 15;
} else {
rom_addr += ROM_BLOCK_SIZE;
}
}
// Checking the ROM reading results were as expected:
// 0 == 1 == 2 == 3
// 3 != 4 and 3 != 5
for (i = 0; i < 3; i++) {
if (crcs[i] != crcs[3]) {
ret = fail_ret;
goto EXIT;
}
}
if (crcs[3] == crcs[4] && crcs[3] == crcs[5]) {
ret = fail_ret;
} else {
ret = pass_ret;
}
EXIT:
// Erasing read buffer
for (i = 0; i < ROM_BLOCK_SIZE; i++) {
rom_buf[i] = 0;
}
return ret;
}
u32 ROMTest_IsBad(void) {
return testROM(0, 1);
}
u32 ROMTest_IsGood(void) {
return testROM(1, 0);
}
+150
View File
@@ -0,0 +1,150 @@
#include "dsprot/rom_util.h"
#include <nitro/card.h>
#include <nitro/os.h>
#include <nitro/reg.h>
// Functions to be encrypted (cannot be called directly)
void ROMUtil_Read(void *dest, u32 addr, s32 num_bytes);
u32 ROMUtil_CRC32(void *buf, u32 size);
void ROMUtil_Read(void *dest, u32 addr, s32 num_bytes) {
u8 buffer[8];
vu8 *vnull;
u32 register_base_1;
vu8 *register_base_2;
u32 card_ctrl_13;
s32 addr_offset;
u16 lock_id;
u16 ext_mem_register_val_original;
u32 output;
s32 card_ctrl_cmd;
int i;
lock_id = OS_GetLockID();
CARD_func_0010(lock_id);
// Alias for volatile null pointer
vnull = NULL;
// Alias for register base (0x04000000)
register_base_1 = 1;
register_base_1 <<= 26;
// Another alias for register base (0x04000000)
register_base_2 = (vu8 *) REG_BASE;
// External memory control register (0x04000204)
// Save value to rewrite later
ext_mem_register_val_original = REG_EXMEM_CNT;
// Set current processor accessing the gamecard bus to the ARM9
OS_SetNdsSlotAccess(OS_CPU_ARM9);
// Obfuscated, create address 0x027FFE60
// This is offset 0x60 in the ROM header: port 0x040001A4 / setting for normal commands
card_ctrl_13 = 5;
card_ctrl_13 <<= 18;
card_ctrl_13 -= 13;
// Detect if the system is in DSi mode. If so, change the address to 0x02FFFE60
if (*(vu8 *) (register_base_1 + REG_A9ROM_OFFSET) & 1) {
card_ctrl_13 |= 0x40000;
}
card_ctrl_13 <<= 5;
// Read port setting and set page read flags
card_ctrl_cmd = (*(vs32 *) card_ctrl_13 & ~CARD_CTL_CMD_MASK) | (CARD_CTL_CMD_PAGE | CARD_CTL_READ | CARD_CTL_START);
// Calculate offset to round back to nearest 0x200-byte block.
// E.G. if we want to read starting from 0x1208, we actually need to
// request the block at 0x1200 and then ignore the first 8 bytes of the result.
// This would set `addr_offset` to -8.
addr_offset = 0 - (addr & (CARD_ROM_PAGE_SIZE - 1));
// Wait for card to not be busy
while (*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) & CARD_CTL_START) {
continue;
}
// Write enable flag to card ROM and SPI control register
*(vu8 *) (register_base_1 + REG_CARD_AUX_SPI_CNT_OFFSET + 1) = CARDMST_ENABLE;
// Read 8-byte command out from gamecard bus, write this back later
for (i = 0; i < 8; i++) {
buffer[i] = *(vnull + _MAIN_REG_BASE + REG_CARD_CMD_OFFSET + i);
}
addr += addr_offset;
while (addr_offset < num_bytes) {
// Read a 0x200-byte data block from ROM
// Write 8-byte command to registers
// B7XXXXXXXX000000 -> 0x200-byte encrypted data read from address XXXXXXXX
register_base_2[REG_CARD_CMD_OFFSET + 0] = CARD_CMD_ENCRYPTED_READ;
register_base_2[REG_CARD_CMD_OFFSET + 1] = addr >> 24;
register_base_2[REG_CARD_CMD_OFFSET + 2] = addr >> 16;
register_base_2[REG_CARD_CMD_OFFSET + 3] = addr >> 8;
register_base_2[REG_CARD_CMD_OFFSET + 4] = addr;
register_base_2[REG_CARD_CMD_OFFSET + 5] = 0x00;
register_base_2[REG_CARD_CMD_OFFSET + 6] = 0x00;
register_base_2[REG_CARD_CMD_OFFSET + 7] = 0x00;
// Submit command
*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) = card_ctrl_cmd;
// Copy the output into the destination buffer, within the bounds of num_bytes
// (Must read the output out of the I/O register regardless)
do {
if (*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) & CARD_DATA_READY) {
output = *(vu32 *) (register_base_1 + REG_CARD_DATA_OFFSET);
if (addr_offset >= 0 && addr_offset < num_bytes) {
*(u32 *) ((u32) dest + addr_offset) = output;
}
addr_offset += 4;
}
} while (*(vu32 *) (register_base_1 + REG_CARD_CNT_OFFSET) & CARD_CTL_START);
// Advance address to next block
addr += CARD_ROM_PAGE_SIZE;
}
// Done reading, restore everything how it was before
// Write original command back to gamecard bus
for (i = 0; i < 8; i++) {
*(vnull + _MAIN_REG_BASE + REG_CARD_CMD_OFFSET + i) = buffer[i];
}
// Write original value back to to external memory control register
*(vu16 *) (register_base_1 + REG_EXMEM_CNT_OFFSET) = ext_mem_register_val_original;
CARD_func_0011(lock_id);
OS_func_0178(lock_id);
}
u32 ROMUtil_CRC32(void *buf, u32 size) {
int i;
u32 crc;
u32 poly;
u8 *byte_ptr;
byte_ptr = (u8 *) buf;
crc = 0xFFFFFFFF;
poly = 0xEDB88320;
while (size-- != 0) {
crc ^= *byte_ptr++;
for (i = 0; i < 8; i++) {
if (crc & 1) {
crc = (crc >> 1);
} else {
crc = poly ^ (crc >> 1);
}
}
}
return ~crc;
}