mirror of
https://github.com/zeldaret/st
synced 2026-09-03 01:54:28 -04:00
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:
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user