From 18fa074d21b91122e28905dc515e581de2a602b6 Mon Sep 17 00:00:00 2001 From: n1481 Date: Fri, 13 May 2011 05:09:53 +0000 Subject: [PATCH] =?UTF-8?q?srl=E3=81=AE=E6=94=B9=E7=AB=84=E6=A4=9C?= =?UTF-8?q?=E5=87=BA=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=84=E3=83=BC=E3=83=AB=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlToolsRED@557 7061adef-622a-194b-ae81-725974e89856 --- build/tools/TamperDetectorForSrl/Readme.txt | 15 + build/tools/TamperDetectorForSrl/banner.h | 110 +++++ build/tools/TamperDetectorForSrl/checker.cpp | 446 ++++++++++++++++++ build/tools/TamperDetectorForSrl/checker.h | 64 +++ build/tools/TamperDetectorForSrl/entry.cpp | 305 ++++++++++++ build/tools/TamperDetectorForSrl/entry.h | 83 ++++ build/tools/TamperDetectorForSrl/main.cpp | 168 +++++++ .../TamperDetectorForSrl/nitro_romheader.h | 316 +++++++++++++ .../tools/TamperDetectorForSrl/searcharg.cpp | 85 ++++ build/tools/TamperDetectorForSrl/searcharg.h | 25 + .../TamperDetectorForSrl/tamperdetector.exe | Bin 0 -> 157273 bytes build/tools/TamperDetectorForSrl/types.h | 13 + 12 files changed, 1630 insertions(+) create mode 100644 build/tools/TamperDetectorForSrl/Readme.txt create mode 100644 build/tools/TamperDetectorForSrl/banner.h create mode 100644 build/tools/TamperDetectorForSrl/checker.cpp create mode 100644 build/tools/TamperDetectorForSrl/checker.h create mode 100644 build/tools/TamperDetectorForSrl/entry.cpp create mode 100644 build/tools/TamperDetectorForSrl/entry.h create mode 100644 build/tools/TamperDetectorForSrl/main.cpp create mode 100644 build/tools/TamperDetectorForSrl/nitro_romheader.h create mode 100644 build/tools/TamperDetectorForSrl/searcharg.cpp create mode 100644 build/tools/TamperDetectorForSrl/searcharg.h create mode 100755 build/tools/TamperDetectorForSrl/tamperdetector.exe create mode 100644 build/tools/TamperDetectorForSrl/types.h diff --git a/build/tools/TamperDetectorForSrl/Readme.txt b/build/tools/TamperDetectorForSrl/Readme.txt new file mode 100644 index 0000000..d37a739 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/Readme.txt @@ -0,0 +1,15 @@ + +例) +./tamperdetector.exe --g ./srl/GENUINE_AAAA.srl --m ./srl/MAGICON_AAAA.srl + + +効果) +ヘッダ改竄状況、ファイル改竄状況の順に表示されます。 + +オフセットやサイズが改竄されている場合 +(双方のsrlファイル上でチェック対象のファイルが存在しているアドレスが + 異なるケース)にも対応しています。 + +サイズが改竄されている場合は小さいほうのサイズでチェックされます。 + +最後に srlファイル中の BMPファイルを全て PC上のカレントパスにエクスポートします。 diff --git a/build/tools/TamperDetectorForSrl/banner.h b/build/tools/TamperDetectorForSrl/banner.h new file mode 100644 index 0000000..433d355 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/banner.h @@ -0,0 +1,110 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - makebanner + File: banner.h + + Copyright 2003-2008 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + *---------------------------------------------------------------------------*/ +#ifndef TWL_OS_COMMON_BANNER_H_ +#define TWL_OS_COMMON_BANNER_H_ + +// define data--------------------------------------------- + +#define BANNER_IMAGE_SIZE (32 * 32 / (8/4)) +#define BANNER_PLTT_SIZE (16 * 2) +#define BANNER_LANG_LENGTH 128 +#define BANNER_LANG_SIZE (BANNER_LANG_LENGTH * 2) + +#define BANNER_VER_NTR_MIN 1 // NTRバナーver.MIN. +#define BANNER_VER_NTR_MAX 3 // NTRバナーver.MAX.(smaller than UCHAR_MAX) +#define BANNER_VER_TWL_MIN 3 // TWLバナーver.MIN. +#define BANNER_VER_TWL_MAX 3 // TWLバナーver.MAX.(smaller than UCHAR_MAX) + +#define BANNER_CHINESE_SUPPORT_VER 2 // 中国語サポートver. +#define BANNER_KOREAN_SUPPORT_VER 3 // 韓国語サポートver. + +#define BANNER_LANG_NUM_RSV 8 // 言語予約領域数 +#define BANNER_ANIME_PATTERN_NUM 8 // バナーアニメパターン数 +#define BANNER_ANIME_CONTROL_INFO_NUM 64 // バナーアニメコントロール情報数 + + +typedef enum { + BANNER_PRIO_JAPANESE = 0, + BANNER_PRIO_ENGLISH, + BANNER_PRIO_FRENCH, + BANNER_PRIO_GERMAN, + BANNER_PRIO_ITALIAN, + BANNER_PRIO_SPANISH, + BANNER_PRIO_CHINESE, + BANNER_PRIO_KOREAN, + BANNER_LANG_NUM, + + BANNER_LANG_NUM_V1 = BANNER_PRIO_CHINESE, + BANNER_LANG_NUM_V2 = BANNER_PRIO_KOREAN - BANNER_PRIO_CHINESE, + BANNER_LANG_NUM_V3 = BANNER_LANG_NUM - BANNER_PRIO_KOREAN +}BannerLanguagePriorityIdx; + + +// プラットフォームコード +typedef enum { + BANNER_PLATFORM_NTR = 0, + BANNER_PLATFORM_TWL = 1, + + BANNER_PLATFORM_MAX = 2 +}BannerPlatformCode; + + +// バナーヘッダ +typedef struct { + u8 version; + u8 platform; // 上記BannerPlatformCodeで指定 + u16 crc16_v1; + u16 crc16_v2; + u16 crc16_v3; + u16 crc16_anime; + u8 reserved_B[ 22 ]; +}BannerHeader; + + +// バナーver.1 ボディ +typedef struct { + u8 image[ BANNER_IMAGE_SIZE ]; + u8 pltt[ BANNER_PLTT_SIZE ]; + u16 gameName[ BANNER_LANG_NUM_V1 ][ BANNER_LANG_LENGTH ]; +}BannerFileV1; + + +// バナーver.2 ボディ追加分 +typedef struct { + u16 gameName[ BANNER_LANG_NUM_V2 ][ BANNER_LANG_LENGTH ]; +}BannerFileV2; + + +// バナーver.3 ボディ追加分 +typedef struct { + u16 gameName[ BANNER_LANG_NUM_V3 ][ BANNER_LANG_LENGTH ]; +}BannerFileV3; + + +// バナーgameName 言語拡張予約領域 +typedef struct { + u16 gameName[ BANNER_LANG_NUM_RSV ][ BANNER_LANG_LENGTH ]; +}BannerFileRsv; + + +// NTRバナーファイル構造体 +typedef struct { + BannerHeader h; + BannerFileV1 v1; + BannerFileV2 v2; + BannerFileV3 v3; +}NTRBannerFile; + + +#endif //TWL_OS_COMMON_BANNER_H_ diff --git a/build/tools/TamperDetectorForSrl/checker.cpp b/build/tools/TamperDetectorForSrl/checker.cpp new file mode 100644 index 0000000..d5cef47 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/checker.cpp @@ -0,0 +1,446 @@ + +#include +#include "checker.h" +#include "nitro_romheader.h" + +extern Entry gEntry; +extern Entry mEntry; + +void Checker::Initialize( FILE* myGfp, FILE* myMfp, void* myGbuf, void* myMbuf, u32 size) +{ + gfp = myGfp; + mfp = myMfp; + gBuf = myGbuf; + mBuf = myMbuf; + buffer_size = size; + + gEntry.Initialize(); + mEntry.Initialize(); +} + +bool Checker::LoadHeader( void* gHeaderBuf, void* mHeaderBuf) +{ + size_t readed; + + fseek( gfp, 0, SEEK_SET); + readed = fread( gHeaderBuf, sizeof(RomHeader), 1, gfp); + if( readed == 1) + { + fseek( mfp, 0, SEEK_SET); + readed = fread( mHeaderBuf, sizeof(RomHeader), 1, mfp); + if( readed == sizeof(RomHeader)) + { + return true; + } + } + return false; +} + +bool Checker::Diff( u32 g_offset, u32 g_size, u32 m_offset, u32 m_size, bool isDataOnly, PrintLevel print_enable) +{ + long nowgfp, nowmfp; + int result = 0; + u32 check_size, rest_size; + int i, loop_num; + bool function_result = true; + + check_size = (g_size < m_size)? g_size : m_size; + rest_size = check_size; + + if( !isDataOnly) + { + /* 指定アドレスとサイズのチェック */ + if( g_offset == m_offset) + { + if( (print_enable)&&(print_enable < PRINT_LEVEL_2)) { + printf( " offset:0x%x(改竄されていない)\n", g_offset); + } + } + else + { + function_result = false; + if( print_enable) { + printf( " offset:0x%x ---> offset:0x%x(改竄されている)\n", g_offset, m_offset); + } + } + + if( g_size == m_size) + { + if( (print_enable)&&(print_enable < PRINT_LEVEL_2)) { + printf( " size:0x%x(改竄されていない)\n", g_size); + } + } + else + { + function_result = false; + if( print_enable) { + printf( " size:0x%x ---> size:0x%x(改竄されている)\n", g_size, m_size); + } + } + } + + bool filled = true; + int totalResult = 0; + int j; + + nowgfp = ftell( gfp); + nowmfp = ftell( mfp); + /* メモリ内容のチェック(サイズが異なる場合は小さいサイズで) */ + fseek( gfp, g_offset, SEEK_SET); + fseek( mfp, m_offset, SEEK_SET); + + if( rest_size > buffer_size) + { + loop_num = (rest_size / buffer_size); + for( i=0; ibanner_offset), SEEK_SET); + fseek( mfp, (u32)(mHeaderBuf->banner_offset), SEEK_SET); + + fread( &gBannerHeader, sizeof(BannerHeader), 1, gfp); + fread( &mBannerHeader, sizeof(BannerHeader), 1, mfp); + + printf( "------- Banner Header -------\n"); + Diff( (u32)(gHeaderBuf->banner_offset), sizeof(BannerHeader), + (u32)(mHeaderBuf->banner_offset), sizeof(BannerHeader), + false, PRINT_LEVEL_1); + if( (((gBannerHeader.version) < 1) || ((gBannerHeader.version) > 3)) || + (((mBannerHeader.version) < 1) || ((mBannerHeader.version) > 3))) + { + printf( " invalid banner version!\n"); + return; + } + printf( "------- Banner Body -------\n"); + Diff( (u32)(gHeaderBuf->banner_offset) + sizeof(BannerHeader), banner_size[gBannerHeader.version], + (u32)(mHeaderBuf->banner_offset) + sizeof(BannerHeader), banner_size[mBannerHeader.version], + false, PRINT_LEVEL_1); +} + + +void Checker::AnalyzeFNT( RomHeader* headerBuf, FILE* fp, Entry* entry, PrintLevel print_enable) +{ + int i; + ROM_FNTDir currentDir; + MyDirEntry tmpDirEntry; + MyDirEntry* pDirEntry; + + // ルートディレクトリの情報を読む + fseek( fp, (u32)(headerBuf->fnt_offset), SEEK_SET); + fread( ¤tDir, sizeof(ROM_FNTDir), 1, fp); + + // ディレクトリテーブル全体を読む + fseek( fp, (u32)(headerBuf->fnt_offset), SEEK_SET); + fread( &fntBuf, sizeof(ROM_FNTDir) * currentDir.parent_id, 1, fp); + + // ルートディレクトリのparent_idは総ディレクトリ数を表す + for( i=0; iInitializeEntry( &tmpDirEntry); + if( i == 0) + { + if( print_enable) { + printf( "------- dir_id : 0xf000 (root) -------\n"); + } + tmpDirEntry.self_id = 0xF000; + } + else + { + if( print_enable) { + printf( "------- dir_id : 0x%x (child dir of 0x%x) -------\n", (0xF000 + i), fntBuf[i].parent_id); + } + tmpDirEntry.self_id = (0xF000 + i); + tmpDirEntry.parent_id = fntBuf[i].parent_id; + } + if( !entry->FindDirEntry( tmpDirEntry.self_id)) + { // 見つからなかったら追加 + pDirEntry = (MyDirEntry*)malloc( sizeof(MyDirEntry)); + entry->CopyEntry( pDirEntry, &tmpDirEntry); + entry->addDirEntry( pDirEntry); + } + + FindEntry( fntBuf[i].entry_start, + fntBuf[i].entry_file_id, + headerBuf, fp, entry, tmpDirEntry.self_id, + print_enable); + } +} + +void Checker::FindEntry( u32 fnt_offset, u16 entry_id, RomHeader* headerBuf, FILE* fp, Entry* entry, u16 parent_id, PrintLevel print_enable) +{ + EntryInfo entryInfo; + char entryNames[FILE_NAME_LENGTH]; + u16 dir_id; + MyDirEntry* dirEntry; + MyFileEntry* fileEntry; + + fseek( fp, (u32)(headerBuf->fnt_offset) + fnt_offset, SEEK_SET); + while( 1) + { + fread( &entryInfo, sizeof(char), 1, fp); + if( (entryInfo.entry_type == 0) && (entryInfo.entry_name_length == 0)) // 終端チェック + { + break; + } + fread( entryNames, entryInfo.entry_name_length, 1, fp); + entryNames[entryInfo.entry_name_length] = '\0'; + if( entryInfo.entry_type == 0) // ファイル + { + if( print_enable) { + printf( "- %s(file_id:0x%d)\n", entryNames, entry_id); + } + /* パス解析用 */ + fileEntry = (MyFileEntry*)malloc( sizeof(MyFileEntry)); + entry->InitializeEntry( fileEntry); + fileEntry->self_id = entry_id; + fileEntry->parent_id = parent_id; + entry->SetName( fileEntry, entryNames, entryInfo.entry_name_length); + entry->addFileEntry( fileEntry); + + FindAllocation( entry_id, headerBuf, fp, entry, print_enable); + entry_id++; + } + else // ディレクトリ + { + fread( &dir_id, sizeof(u16), 1, fp); + if( print_enable) { + printf( "- [%s](dir_id:0x%x)\n", entryNames, dir_id); + } + /* パス解析用 */ + dirEntry = entry->FindDirEntry( dir_id); + if( !dirEntry) + { // 見つからなかったら追加 + dirEntry = (MyDirEntry*)malloc( sizeof(MyDirEntry)); + entry->InitializeEntry( dirEntry); + dirEntry->self_id = dir_id; + dirEntry->parent_id = parent_id; + entry->addDirEntry( dirEntry); + } + entry->SetName( dirEntry, entryNames, entryInfo.entry_name_length); + } + } +} + +void Checker::FindAllocation( u16 entry_id, RomHeader* headerBuf, FILE* fp, Entry* entry, PrintLevel print_enable) +{ + ROM_FAT currentRomFat; + long nowfp; + nowfp = ftell( fp); + + fseek( fp, + (u32)(headerBuf->fat_offset) + (sizeof(ROM_FAT) * entry_id), + SEEK_SET); + fread( ¤tRomFat, sizeof(ROM_FAT), 1, fp); + if( print_enable) { + printf( " fat top:0x%x, bottom:0x%x, len:0x%x\n", + (u32)(currentRomFat.top), (u32)(currentRomFat.bottom), + (u32)(currentRomFat.bottom) - (u32)(currentRomFat.top)); + } + Diff( (u32)(currentRomFat.top), (u32)(currentRomFat.bottom) - (u32)(currentRomFat.top), + (u32)(currentRomFat.top), (u32)(currentRomFat.bottom) - (u32)(currentRomFat.top), + true, print_enable); + + /* パス解析用 */ + MyFileEntry* fileEntry = entry->FindFileEntry( entry_id); + fileEntry->top = (u32)(currentRomFat.top); + fileEntry->bottom = (u32)(currentRomFat.bottom); + + // ファイルポインタを戻す + fseek( fp, nowfp, SEEK_SET); +} + + +/* ROM内のBMPファイルを全て切り出して出力する */ +#define BMP_BUFFER_SIZE (512*1024) +u32 tmpBuf[BMP_BUFFER_SIZE / 4]; +void Checker::ExportGenuineBmpFiles( Entry* gEntry, PrintLevel print_enable) +{ + int i; + MyFileEntry *currentEntry = gEntry->fileEntry; + MyFileEntry *hisEntry; +// u32* tmpBuf = (u32*)malloc( BMP_BUFFER_SIZE); + u32 file_size, rest_size; + int loop_num; + FILE* fp; + + while( currentEntry) + { + if( currentEntry->name_length > 4) + { + if( memcmp( ¤tEntry->name[currentEntry->name_length - 4], ".bmp", 4) == 0) + { + file_size = (currentEntry->bottom - currentEntry->top); + rest_size = file_size; + + loop_num = file_size / BMP_BUFFER_SIZE; + fp = fopen( currentEntry->name, "w"); + + fseek( gfp, currentEntry->top, SEEK_SET); + if( rest_size > BMP_BUFFER_SIZE) + { + for( i=0; ifull_path_name); + } + currentEntry = (MyFileEntry*)(currentEntry->next); + }; +// free( tmpBuf); +} + +/* ディレクトリとファイルをチェックする */ +void Checker::CheckAllEntries( Entry* gEntry, Entry* mEntry) +{ + { + MyDirEntry *currentEntry = gEntry->dirEntry; + MyDirEntry *hisEntry; + + printf( "------- directory check -------\n"); + while( currentEntry) + { + printf( "- %s", currentEntry->full_path_name); + + hisEntry = mEntry->FindDirEntry( currentEntry->full_path_name); + if( hisEntry) + { + printf( "\n"); + } + else + { + printf( " --->(存在していない)\n"); + } + + currentEntry = (MyDirEntry*)(currentEntry->next); + } + + } + + MyFileEntry *currentEntry = gEntry->fileEntry; + MyFileEntry *hisEntry; + printf( "------- file check -------\n"); + while( currentEntry) + { + printf( "- %s", currentEntry->full_path_name); + + hisEntry = mEntry->FindFileEntry( currentEntry->full_path_name); + if( hisEntry) + { + printf( "\n"); + if( Diff( currentEntry->top, (currentEntry->bottom - currentEntry->top), + hisEntry->top, (hisEntry->bottom - hisEntry->top), + false, PRINT_LEVEL_1) == false) + { + printf( "\n"); + } + } + else + { + printf( " --->(存在していない)\n"); + } + + currentEntry = (MyFileEntry*)(currentEntry->next); + } +} + + +void Checker::Finalize( void) +{ +} + diff --git a/build/tools/TamperDetectorForSrl/checker.h b/build/tools/TamperDetectorForSrl/checker.h new file mode 100644 index 0000000..307fa41 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/checker.h @@ -0,0 +1,64 @@ + +#ifndef CHECKER_H_ +#define CHECKER_H_ + + +#include +#include +#include "types.h" +#include "nitro_romheader.h" +#include "banner.h" +#include "entry.h" + +typedef struct +{ + u8 entry_name_length:7; // ファイル名の長さ (0-127) + u8 entry_type :1; // ファイルエントリの場合は 0 +} EntryInfo; + +typedef enum +{ + PRINT_LEVEL_0 = 0, + PRINT_LEVEL_1, + PRINT_LEVEL_2 +} PrintLevel; + + +class Checker +{ + private: + bool initialized; + FILE* gfp; + FILE* mfp; + void* gBuf; + void* mBuf; + u32 buffer_size; + ROM_FNTDir fntBuf[4096]; + void* dirTableBuf; + + public: + void Initialize( FILE* myGfp, FILE* myMfp, void* myGbuf, void* myMbuf, u32 size); + + /* ヘッダを読むだけ */ + bool LoadHeader( void* gHeaderBuf, void* mHeaderBuf); + + /* ROMの特定領域に差がないかどうか調べる */ + bool Diff( u32 g_offset, u32 g_size, u32 m_offset, u32 m_size, bool isDataOnly, PrintLevel print_enable); + + void Finalize( void); + + /* ROMのバナー領域に対して Diff をかける */ + void AnalyzeBanner( RomHeader* gHeaderBuf, RomHeader* mHeaderBuf); + + /* FNT と FAT を解析して、各ファイルに対して Diff をかける */ + void AnalyzeFNT( RomHeader* headerBuf, FILE* fp, Entry* entry, PrintLevel print_enable); + void FindEntry( u32 fnt_offset, u16 entry_id, RomHeader* headerBuf, FILE* fp, Entry* entry, u16 parent_id, PrintLevel print_enable); + void FindAllocation( u16 entry_id, RomHeader* headerBuf, FILE* fp, Entry* entry, PrintLevel print_enable); + + void CheckAllEntries( Entry* gEntry, Entry* mEntry); + + /* ROM内のBMPファイルを全て切り出して出力する */ + void ExportGenuineBmpFiles( Entry* gEntry, PrintLevel print_enable); +}; + +#endif diff --git a/build/tools/TamperDetectorForSrl/entry.cpp b/build/tools/TamperDetectorForSrl/entry.cpp new file mode 100644 index 0000000..6e0182d --- /dev/null +++ b/build/tools/TamperDetectorForSrl/entry.cpp @@ -0,0 +1,305 @@ +#include +#include "entry.h" +#include "nitro_romheader.h" + + +//void Entry::Initialize( void) +void Entry::Initialize( void) +{ + dirEntry = NULL; + fileEntry = NULL; +} + +void Entry::InitializeEntry( MyDirEntry* myDirEntry) +{ + myDirEntry->name = NULL; + myDirEntry->full_path_name = NULL; + myDirEntry->name_length = 0; + myDirEntry->full_path_name_length = 0; + myDirEntry->self_id = 0xFFFF; + myDirEntry->parent_id = 0; + myDirEntry->parent = NULL; + myDirEntry->next = NULL; +} + +void Entry::InitializeEntry( MyFileEntry* myFileEntry) +{ + myFileEntry->name = NULL; + myFileEntry->full_path_name = NULL; + myFileEntry->name_length = 0; + myFileEntry->full_path_name_length = 0; + myFileEntry->top = 0; + myFileEntry->bottom = 0; + myFileEntry->self_id = 0xFFFF; + myFileEntry->parent_id = 0; + myFileEntry->parent = NULL; + myFileEntry->next = NULL; +} + +void Entry::CopyEntry( MyDirEntry* dest, MyDirEntry* src) +{ + memcpy( dest, src, sizeof(MyFileEntry)); +} + +void Entry::SetName( MyDirEntry* myDirEntry, char* fname, u16 len) +{ + myDirEntry->name = (char*)malloc( sizeof(MyDirEntry)); + memset( myDirEntry->name, 0, len+1); + memcpy( myDirEntry->name, fname, len); + + myDirEntry->name_length = len; +} + +void Entry::SetName( MyFileEntry* myFileEntry, char* fname, u16 len) +{ + myFileEntry->name = (char*)malloc( FILE_NAME_LENGTH); + memset( myFileEntry->name, 0, len+1); + memcpy( myFileEntry->name, fname, len); + + myFileEntry->name_length = len; +} + +void Entry::addDirEntry( MyDirEntry* myDirEntry) +{ + MyDirEntry *currentEntry = dirEntry; + if( !currentEntry) + { + dirEntry = myDirEntry; + return; + } + while( currentEntry->next) + { + currentEntry = (MyDirEntry*)(currentEntry->next); + } + currentEntry->next = myDirEntry; + myDirEntry->next = NULL; +} + + +void Entry::addFileEntry( MyFileEntry* myFileEntry) +{ + MyFileEntry *currentEntry = fileEntry; + if( !currentEntry) + { + fileEntry = myFileEntry; + return; + } + while( currentEntry->next) + { + currentEntry = (MyFileEntry*)(currentEntry->next); + } + currentEntry->next = myFileEntry; + myFileEntry->next = NULL; +} + + +MyDirEntry* Entry::FindDirEntry( u16 id) +{ + MyDirEntry *currentEntry = dirEntry; + while( currentEntry) + { + if( currentEntry->self_id == id) + { + return currentEntry; + } +// printf( "%s, 0x%x, 0x%x\n", __FUNCTION__, currentEntry->self_id, id); + currentEntry = (MyDirEntry*)(currentEntry->next); + } + return NULL; +} + + +MyFileEntry* Entry::FindFileEntry( u16 id) +{ + MyFileEntry *currentEntry = fileEntry; + while( currentEntry) + { + if( currentEntry->self_id == id) + { + return currentEntry; + } +// printf( "%s, 0x%x, 0x%x\n", __FUNCTION__, currentEntry->self_id, id); + currentEntry = (MyFileEntry*)(currentEntry->next); + } + return NULL; +} + + +MyDirEntry* Entry::FindDirEntry( char* my_full_path_name) +{ + MyDirEntry *currentEntry = dirEntry; + while( currentEntry) + { + if( strcmp( currentEntry->full_path_name, my_full_path_name) == 0) + { + return currentEntry; + } + currentEntry = (MyDirEntry*)(currentEntry->next); + } + return NULL; +} + + +MyFileEntry* Entry::FindFileEntry( char* my_full_path_name) +{ + MyFileEntry *currentEntry = fileEntry; + while( currentEntry) + { + if( strcmp( currentEntry->full_path_name, my_full_path_name) == 0) + { + return currentEntry; + } + currentEntry = (MyFileEntry*)(currentEntry->next); + } + return NULL; +} + + +void Entry::AutoSetFullPath( void) +{ + MyDirEntry *currentDirEntry = dirEntry; + while( currentDirEntry) + { + SetFullPath( currentDirEntry); + currentDirEntry = (MyDirEntry*)(currentDirEntry->next); + } + + MyFileEntry *currentFileEntry = fileEntry; + while( currentFileEntry) + { + SetFullPath( currentFileEntry); + currentFileEntry = (MyFileEntry*)(currentFileEntry->next); + } +} + +void Entry::SetFullPath( MyDirEntry *targetDirEntry) +{ + char tmp_name[256]; + u16 total_length; + + MyDirEntry *currentDirEntry = targetDirEntry; + total_length = 0; + tmp_name[255] = '\0'; + while( currentDirEntry) + { + total_length += (currentDirEntry->name_length); + memcpy( &(tmp_name[255 - total_length]), currentDirEntry->name, currentDirEntry->name_length); + if( currentDirEntry->self_id != 0xF000) // rootディレクトリでなければ + { + total_length++; // '/'のぶん + tmp_name[255 - total_length] = '/'; + } + currentDirEntry = (MyDirEntry*)(currentDirEntry->parent); + } + targetDirEntry->full_path_name = (char*)malloc( total_length + 1); + targetDirEntry->full_path_name_length = total_length; + memset( targetDirEntry->full_path_name, 0, total_length + 1); + memcpy( targetDirEntry->full_path_name, &(tmp_name[255 - total_length]), total_length); +} + +void Entry::SetFullPath( MyFileEntry *targetFileEntry) +{ + char tmp_name[256]; + u16 total_length; + + MyFileEntry *currentFileEntry = targetFileEntry; + MyDirEntry *parentDirEntry; + total_length = 0; + tmp_name[255] = '\0'; + { + total_length += (currentFileEntry->name_length); + memcpy( &(tmp_name[255 - total_length]), currentFileEntry->name, currentFileEntry->name_length); + total_length++; // '/'のぶん + tmp_name[255 - total_length] = '/'; + + parentDirEntry = (MyDirEntry*)(currentFileEntry->parent); + if( parentDirEntry) + { + total_length += parentDirEntry->full_path_name_length; + memcpy( &(tmp_name[255 - total_length]), parentDirEntry->full_path_name, parentDirEntry->full_path_name_length); + } + } + targetFileEntry->full_path_name = (char*)malloc( total_length + 1); + memset( targetFileEntry->full_path_name, 0, total_length + 1); + memcpy( targetFileEntry->full_path_name, &(tmp_name[255 - total_length]), total_length); +} + +/* parent リンクを繋げる */ +void Entry::FollowParent( void) +{ + MyDirEntry *currentDirEntry = dirEntry; + while( currentDirEntry) + { + currentDirEntry->parent = FindDirEntry( currentDirEntry->parent_id); + currentDirEntry = (MyDirEntry*)(currentDirEntry->next); + } + + MyFileEntry *currentFileEntry = fileEntry; + while( currentFileEntry) + { + currentFileEntry->parent = FindDirEntry( currentFileEntry->parent_id); +// printf( "%s, 0x%x, 0x%x\n", __FUNCTION__, currentFileEntry->parent_id, (u32)currentFileEntry->parent); + currentFileEntry = (MyFileEntry*)(currentFileEntry->next); + } +} + + +void Entry::PrintAllDirEntry( void) +{ + MyDirEntry *currentEntry = dirEntry; + printf( "------- all directories are -------\n"); + while( currentEntry) + { +// printf( "directory : %s\n", currentEntry->name); + printf( "fullpath : %s\n", currentEntry->full_path_name); +// printf( "id : 0x%x\n", currentEntry->self_id); +// printf( "parent id : 0x%x\n", currentEntry->parent_id); + currentEntry = (MyDirEntry*)(currentEntry->next); + } +} + +void Entry::PrintAllFileEntry( void) +{ + MyFileEntry *currentEntry = fileEntry; + printf( "------- all files are -------\n"); + while( currentEntry) + { +// printf( "file : %s\n", currentEntry->name); + printf( "fullpath : %s\n", currentEntry->full_path_name); +// printf( "id : 0x%x\n", currentEntry->self_id); +// printf( "parent id : 0x%x\n", currentEntry->parent_id); + currentEntry = (MyFileEntry*)(currentEntry->next); + } +} + +/* +void Entry::CheckAllFiles( Checker* checker, MyFileEntry *anotherFileEntry) +{ + MyFileEntry *currentEntry = fileEntry; + MyFileEntry *hisEntry; + while( currentEntry) + { + printf( "- %s", currentEntry->full_path_name); + + hisEntry = FindFileEntry( currentEntry->full_path_name); + if( hisEntry) + { + printf( " --->(存在している)\n"); + checker.Diff( currentEntry->top, (currentEntry->bottom - currentEntry->top), + hisEntry->top, (hisEntry->bottom - hisEntry->top), + false, true); + } + else + { + printf( " --->(存在していない)\n"); + } + } +}*/ + + +/* フルパスをセットする(事前にFollowParent()を済ませておくことが必要) */ +void Entry::Finalize() +{ +} + + diff --git a/build/tools/TamperDetectorForSrl/entry.h b/build/tools/TamperDetectorForSrl/entry.h new file mode 100644 index 0000000..beb7234 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/entry.h @@ -0,0 +1,83 @@ +#ifndef ENTRY_H_ +#define ENTRY_H_ + + +#include +#include +#include "types.h" +#include "nitro_romheader.h" +#include "banner.h" +//#include "checker.h" + +typedef struct +{ + char* name; + char* full_path_name; + u8 name_length; + u8 full_path_name_length; + u16 self_id; + u16 parent_id; + void* parent; + void* next; +} MyDirEntry; + +typedef struct +{ + char* name; + char* full_path_name; + u8 name_length; + u8 full_path_name_length; + u32 top; + u32 bottom; + u16 self_id; + u16 parent_id; + void* parent; + void* next; +} MyFileEntry; + + +class Entry +{ +// private: + public: + MyDirEntry* dirEntry; + MyFileEntry* fileEntry; + + public: + void Initialize( void); + + void InitializeEntry( MyDirEntry* myDirEntry); + void InitializeEntry( MyFileEntry* myFileEntry); + + void CopyEntry( MyDirEntry* dest, MyDirEntry* src); + + void SetName( MyDirEntry* myDirEntry, char* fname, u16 len); + void SetName( MyFileEntry* myDirEntry, char* fname, u16 len); + + void addDirEntry( MyDirEntry* myDirEntry); + void addFileEntry( MyFileEntry* myFileEntry); + + MyDirEntry* FindDirEntry( u16 id); + MyFileEntry* FindFileEntry( u16 id); + + MyDirEntry* FindDirEntry( char* my_full_path_name); + MyFileEntry* FindFileEntry( char* my_full_path_name); + + /* parent リンクを繋げる */ + void FollowParent( void); + + /* フルパスをセットする */ + void AutoSetFullPath( void); + void SetFullPath( MyDirEntry *targetDirEntry); + void SetFullPath( MyFileEntry *targetFileEntry); + + void PrintAllDirEntry( void); + void PrintAllFileEntry( void); + +// void CheckAllFiles( Checker* checker, MyFileEntry *anotherFileEntry); + + void Finalize(); +}; + + +#endif diff --git a/build/tools/TamperDetectorForSrl/main.cpp b/build/tools/TamperDetectorForSrl/main.cpp new file mode 100644 index 0000000..5df253a --- /dev/null +++ b/build/tools/TamperDetectorForSrl/main.cpp @@ -0,0 +1,168 @@ + +#include +#include +#include "types.h" +#include "searcharg.h" +#include "nitro_romheader.h" +#include "checker.h" + + +extern char* output_fname; +extern char* genuine_fname; +extern char* magicon_fname; + +#define BUFFER_SIZE (0x4000) + +Entry gEntry; +Entry mEntry; + +RomHeader gHeaderBuf; +RomHeader mHeaderBuf; +char gBuf[BUFFER_SIZE]; +char mBuf[BUFFER_SIZE]; + + +/*--- 型のビット数をチェック ---*/ +bool int_bits(void) +{ + int count = 0; + unsigned short usi = ~0U; + unsigned long uli = ~0U; + + while (uli) { + if (uli & 1U) count++; + uli >>= 1; + } + if( count != 32) + { + printf("ERROR! unsigned long int : %d bits\n", count); + return false; + } + + count = 0; + while (usi) { + if (usi & 1U) count++; + usi >>= 1; + } + if( count != 16) + { + printf("ERROR! unsigned short int : %d bits\n", count); + return false; + } + + return true; +} + + +int main (int argc, char *argv[]) +{ + // 処理系の unsignedビット数が想定外ならエラー終了(types.hを変更してビルドし直してください) + if( !int_bits()) + { + return 1; + } + + SA_searchopt(argc, argv); + + printf("[output_fname]%s\n", output_fname); + printf("[genuine_fname]%s\n", genuine_fname); + printf("[magicon_fname]%s\n", magicon_fname); + + { + FILE* gfp; + FILE* mfp; + Checker checker; + + gfp = fopen( genuine_fname, "r"); + mfp = fopen( magicon_fname, "r"); + + checker.Initialize( gfp, mfp, gBuf, mBuf, BUFFER_SIZE); + checker.LoadHeader( &gHeaderBuf, &mHeaderBuf); + + printf( "------------------\n"); + printf( "Nitro Rom Header\n"); + checker.Diff( 0, sizeof(RomHeader), 0, sizeof(RomHeader), false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "ARM9 Static Module\n"); + checker.Diff( (u32)(gHeaderBuf.arm9.romAddr), + (u32)(gHeaderBuf.arm9.romSize), + (u32)(mHeaderBuf.arm9.romAddr), + (u32)(mHeaderBuf.arm9.romSize), + false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "ARM7 Static Module\n"); + checker.Diff( (u32)(gHeaderBuf.arm7.romAddr), + (u32)(gHeaderBuf.arm7.romSize), + (u32)(mHeaderBuf.arm7.romAddr), + (u32)(mHeaderBuf.arm7.romSize), + false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "File Name Table\n"); + checker.Diff( (u32)(gHeaderBuf.fnt_offset), + (u32)(gHeaderBuf.fnt_size), + (u32)(mHeaderBuf.fnt_offset), + (u32)(mHeaderBuf.fnt_size), + false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "File Allocation Table\n"); + checker.Diff( (u32)(gHeaderBuf.fat_offset), + (u32)(gHeaderBuf.fat_size), + (u32)(mHeaderBuf.fat_offset), + (u32)(mHeaderBuf.fat_size), + false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "ARM9 Overlay Table\n"); + checker.Diff( (u32)(gHeaderBuf.main_ovt_offset), + (u32)(gHeaderBuf.main_ovt_size), + (u32)(mHeaderBuf.main_ovt_offset), + (u32)(mHeaderBuf.main_ovt_size), + false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "ARM7 Overlay Table\n"); + checker.Diff( (u32)(gHeaderBuf.sub_ovt_offset), + (u32)(gHeaderBuf.sub_ovt_size), + (u32)(mHeaderBuf.sub_ovt_offset), + (u32)(mHeaderBuf.sub_ovt_size), + false, PRINT_LEVEL_1); + printf( "------------------\n"); + + printf( "\nBanner\n"); + checker.AnalyzeBanner( &gHeaderBuf, &mHeaderBuf); + + printf( "\nFNT & FAT\n"); + checker.AnalyzeFNT( &gHeaderBuf, gfp, &gEntry, PRINT_LEVEL_0); + gEntry.FollowParent(); + gEntry.AutoSetFullPath(); + + checker.AnalyzeFNT( &mHeaderBuf, mfp, &mEntry, PRINT_LEVEL_0); + mEntry.FollowParent(); + mEntry.AutoSetFullPath(); + + checker.CheckAllEntries( &gEntry, &mEntry); + + checker.ExportGenuineBmpFiles( &gEntry, PRINT_LEVEL_0); +/* + gEntry.PrintAllDirEntry(); + gEntry.PrintAllFileEntry(); + + mEntry.PrintAllDirEntry(); + mEntry.PrintAllFileEntry(); + */ + printf( "------------------\n"); + + +// AnalyzeFNT( mHeaderBuf, mfp); + + fclose( gfp); + fclose( mfp); + } + + return 0; +} + diff --git a/build/tools/TamperDetectorForSrl/nitro_romheader.h b/build/tools/TamperDetectorForSrl/nitro_romheader.h new file mode 100644 index 0000000..b8bdcb7 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/nitro_romheader.h @@ -0,0 +1,316 @@ +/*---------------------------------------------------------------------------* + nitroeva3/card/rom_filesystem.h + *---------------------------------------------------------------------------*/ +#ifndef NITROEVA3_CARD_ROM_FILESYSTEM_H_ +#define NITROEVA3_CARD_ROM_FILESYSTEM_H_ + +#include "types.h" + +/*---------------------------------------------------------------------------* + 定数定義 + *---------------------------------------------------------------------------*/ +#define FILE_NAME_LENGTH 128 + +// マスクROMのヘッダアドレスを獲得 +#define GetRomHeaderAddr() ((RomHeader *)HW_ROM_HEADER_BUF) + +// 共有ワーク領域のアドレス獲得 +#define GetSharedWorkAddr() ((SharedWork *)HW_RED_RESERVED) // (HW_MAIN_MEM + 0x007ff800) maybe change later + +#define ROM_FILE_MAIN_PROCESSOR 0 +#define ROM_FILE_SUB_PROCESSOR 1 + +#define ROM_FILE_CARD 0 +#define ROM_FILE_CARTRIDGE 1 + + + +/*---------------------------------------------------------------------------* + 型定義 + *---------------------------------------------------------------------------*/ +// 共有ワーク構造体 +typedef struct { + u32 nCardID; // NORMALカードID + u32 sCardID; // SECUREカードID + u16 cardHeaderCrc16; // カードヘッダCRC16 + u16 cardSecureCrc16; // カードSECURE領域CRC16 + s16 cardHeaderError; // カードヘッダエラー +} SharedWork; + + + +/*---------------------------------------------------------------------------* + ファイル名テーブルとは、 + + ファイル名からファイルIDを取得するためのテーブル。 + 「ディレクトリ・テーブル」と「エントリ名テーブル」で構成される。 + *---------------------------------------------------------------------------*/ +typedef struct +{ + char entry_name[FILE_NAME_LENGTH]; // ファイル名 (終端 \0 は省く) + +} ROM_FNT; + + + +/*---------------------------------------------------------------------------* + ディレクトリ・テーブルとは、 + + ディレクトリ・テーブル構造体の配列として実装されている。 + 各テーブルにはディレクトリIDと呼ばれる番号が割り振られ、格納順にカウント + アップされる。ファイルIDと区別するため、ディレクトリIDは、0xF000〜0xFFFF。 + (ファイルIDは、0x0000〜0xEFFF)ディレクトリ数は4096、ファイル数は61440まで + となる。ルート・ディレクトリは、ディレクトリIDが、0xF000、親ディレクトリIDには + ディレクトリ・エントリ数が格納される。 + + ※ ディレクトリ・テーブル構造体配列の要素数 = ディレクトリ数 + ※ ディレクトリ・テーブル構造体配列の添え字 = ディレクトリID − 0xF000 + + 以下、ディレクトリ・テーブル構造体の型定義 + *---------------------------------------------------------------------------*/ +typedef struct +{ + u32 entry_start; // エントリ名の検索位置(ファイル名テーブルの先頭位置からのオフセット) + u16 entry_file_id; // 先頭エントリのファイル ID + u16 parent_id; // 親ディレクトリの ID + +} ROM_FNTDir; + + + +/*---------------------------------------------------------------------------* + エントリ名テーブルとは、 + + 下記の2種類(ROM_FNTStrFile, ROM_FNTStrDir)の可変長データの集合。 + エントリがファイル --> ROM_FNTStrFile + エントリがディレクトリ --> ROM_FNTStrDir + + 同一ディレクトリ内に含まれるエントリは連続した領域に配置される(ファイルIDも連続)。 + 同一ディレクトリ内の最終エントリの次にはエントリ名の長さが0のファイルエントリが置かれる + + ########################################################################## + (ファイル構成の例:例1) + /Nitro.ROM + /BQ.DAT + /image/APPLE.JPG + + ディレクトリ・テーブル + DIR-ID + -------------------------------------------------------------------------- + 0xF000 : entry_start = &(Nitro.ROM), entry_file_id = 0, parent_id = 2 (ディレクトリ数) + 0xF001 : entry_start = &(APPLE.JPG), entry_file_id = 2, parent_id = 0xF000 + -------------------------------------------------------------------------- + + エントリ名・テーブル + FILE-ID + ---(ディレクトリ / )------------------------------------------------------ + 0 : entry_type = 0, entry_name_length = 9, entry_name = "Nitro.ROM" + 1 : entry_type = 0, entry_name_length = 6, entry_name = "BQ.DAT" + : entry_type = 1, entry_name_length = 5, entry_name = "image" dir_id = 0xF001 + x : entry_type = 0, entry_name_length = 0 + ---(ディレクトリ /image/ )------------------------------------------------ + 2 : entry_type = 0, entry_name_length = 9, entry_name = "APPLE.JPG" + x : entry_type = 0, entry_name_length = 0 + ########################################################################## + *---------------------------------------------------------------------------*/ +typedef struct +{ + u8 entry_type :1; // ファイルエントリの場合は 0 + u8 entry_name_length:7; // ファイル名の長さ (0-127) + char entry_name[FILE_NAME_LENGTH]; // ファイル名 (終端 \0 は省く) + +} ROM_FNTStrFile; + +typedef struct +{ + u8 entry_type :1; // ディレクトリエントリの場合は 1 + u8 entry_name_length:7; // ディレクトリ名の長さ (0-127) + char entry_name[FILE_NAME_LENGTH]; // ディレクトリ名 (終端 \0 は省く) + u8 dir_id_L; // ディレクトリ ID Low 8bit + u8 dir_id_H; // ディレクトリ ID High 8bit + +} ROM_FNTStrDir; + + + +/*---------------------------------------------------------------------------* + FAT : ファイル・アロケーション・テーブルとは、 + + 下記の構造体の配列として実装されている。 + 配列の添え字がファイルIDと一致。 + + romFatTable を ROM_FAT型 の配列と見なすと、 + romFat[5].top は、ファイルID=5 のファイルの先頭ROMアドレス + + 使用されていないファイルIDに対応するデータには、top = bottom = 0 が入る + *---------------------------------------------------------------------------*/ +typedef struct +{ + void* top; // ファイルの先頭 ROM アドレス + void* bottom; // ファイルの最終 ROM アドレス + +} ROM_FAT; + + + +/*---------------------------------------------------------------------------* + オーバレイ・ヘッダ・テーブルとは、 + + オーバレイ・ファイルのロード情報を含んだテーブル。 + リンク処理時に nef ファイルやオーバレイ・モジュールと同時にバイナリで作成される + 下記の構造体データの配列として実装され、 + 配列のサイズ = オーバレイ・ファイル数 + 配列の添え字 = オーバレイID + + オーバレイIDはリンク処理時には仮の値が設定されているが、makeromにより実際の値となる + *---------------------------------------------------------------------------*/ +typedef struct +{ + u32 id; // オーバーレイ ID + void* ram_address; // ロード先頭位置 + u32 ram_size; // ロードサイズ + u32 bss_size; // bss 領域サイズ + void* sinit_init; // static initializer 先頭アドレス + void* sinit_init_end; // static initializer 最終アドレス + u32 file_id; // オーバーレイファイルID + u32 rom_size; // オーバーレイファイルサイズ +} ROM_OVT; + + + +/*---------------------------------------------------------------------------* + 常駐モジュール用パラメタ + *---------------------------------------------------------------------------*/ +typedef struct { + + u8* rom_address; // 転送元 ROM アドレス + u8* entry_address; // 実行開始(エントリ)アドレス + u8* ram_address; // 転送先 RAM アドレス + s32 rom_size; // 転送 ROM サイズ + +} ROM_ResidentModuleParam; // 常駐の ... resident + +// ROMヘッダ・ブートパラメータ構造体 (オリジナルの定義) +typedef struct { + u8 *romAddr; // ROMアドレス + u8 *entryAddr; // エントリアドレス + u8 *ramAddr; // RAMアドレス + s32 romSize; // ROMサイズ +} BootUsrParam; + + + +/*---------------------------------------------------------------------------* + ROMヘッダ構造体 + *---------------------------------------------------------------------------*/ +typedef struct { + + //------------------------------------------------------------------------ + // 0x000 System Reserved + // + char title_name[12]; // Soft title name + u32 game_code; // Game code + + u16 maker_code; // Maker code + u8 machine_code; // Machine code + u8 rom_type; // Rom type + u8 rom_size; // Rom size + + u8 reserved_A[9]; // System Reserved A ( Set ALL 0 ) + + u8 soft_version; // Soft version + u8 comp_arm9_boot_area:1; // Compress arm9 boot area + u8 comp_arm7_boot_area:1; // Compress arm7 boot area + u8 :0; + + + //------------------------------------------------------------------------ + // 0x020 for Static modules (Section:B) + // + // ARM9 + BootUsrParam arm9; + + // ARM7 + BootUsrParam arm7; + + + //------------------------------------------------------------------------ + // 0x040 for File Name Table[FNT] (Section:C) + // + ROM_FNT* fnt_offset; // ROM offset + u32 fnt_size; // Table size + + + //------------------------------------------------------------------------ + // 0x048 for File Allocation Table[FAT] (Section:E) + // + ROM_FAT* fat_offset; // ROM offset + u32 fat_size; // Table size + + + //------------------------------------------------------------------------ + // 0x050 for Overlay Tables[OVT] (Section:D) + // + // ARM9 + ROM_OVT* main_ovt_offset; // ROM offset + u32 main_ovt_size; // Table size + + // ARM7 + ROM_OVT* sub_ovt_offset; // ROM offset + u32 sub_ovt_size; // Table size + + + //------------------------------------------------------------------------ + // 0x060 for ROM control parameter + u32 game_cmd_param; // Game command parameter + u32 secure_cmd_param; // Secure command parameter + u32 banner_offset; // Ctrl Reserved A (Set 0) + u16 secure_area_crc16; // Secure area CRC-16 + u16 secure_cmd_latency; // Secure command latency ((param+2)*256 system cycles) + u8 ctrl_reserved_B[16]; // Ctrl Reserved B (Set 0) + + + //------------------------------------------------------------------------ + // 0x080 - 0x0C0 System Reserved + u8 reserved_B[64]; // System Reserved B (Set 0) + + + //------------------------------------------------------------------------ + // 0x0C0 for NINTENDO logo data + u8 nintendo_logo[0x9c]; // NINTENDO logo data + u16 nintendo_logo_crc16; // CRC-16 + + + //------------------------------------------------------------------------ + // 0x15E ROM header CRC-16 + u16 header_crc16; // ROM header CRC-16 + + + //------------------------------------------------------------------------ + // 0x0160 - 0x0180 System Reserved + // + u8 reserved_C[32]; // Debugger Reserved (Set ALL 0) + +} RomHeader; + + + +/*---------------------------------------------------------------------------* + 大域変数宣言 + *---------------------------------------------------------------------------*/ + + + +/*---------------------------------------------------------------------------* + 関数プロトタイプ宣言 + *---------------------------------------------------------------------------*/ +/* +BOOL get_overlay_info(int card_or_cart, int main_or_sub, u32 id, ROM_OVT *ovt); +BOOL my_romfile_load_overlay_segment(int card_or_cart, int main_or_sub, u32 id); +void print_overlay_info(int main_or_sub, u32 id); +*/ + +#endif // NITROEVA3_CARD_ROM_FILESYSTEM_H_ + + + diff --git a/build/tools/TamperDetectorForSrl/searcharg.cpp b/build/tools/TamperDetectorForSrl/searcharg.cpp new file mode 100644 index 0000000..a58b9d4 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/searcharg.cpp @@ -0,0 +1,85 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - - makelst + File: searcharg.c + + Copyright 2006-2009 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + *---------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include "searcharg.h" + +char* output_fname = NULL; +char* genuine_fname = NULL; +char* magicon_fname = NULL; + + +void SA_Usage( void) +{ + fprintf( stderr, "Analyzing Tool\n"); + fprintf( stderr, "Usage: makelst [-o output-file] [--g genuine-srl-file] [--m magicon-srl-file]\n\n"); + exit( 1); +} + + +/*引数を解析する*/ +void SA_searchopt( int argc, char* argv[]) +{ + int n; + struct option optionInfo[] = { + { "genuine", required_argument, NULL, 'g'}, + { "magicon", required_argument, NULL, 'm'}, + { NULL, 0, NULL, 0} + }; + + if( argc <= 1) { + SA_Usage(); + } + + while( (n = getopt_long( argc, argv, "do:h", &optionInfo[0], NULL)) + != -1) + { + switch( n) { + case 'd': +// dbg_print_flag = 1; + break; + case 'o': + if( output_fname != NULL) { + fprintf( stderr, "ERROR! redefined output filename.\n"); + SA_Usage(); + } + output_fname = optarg; + break; + case 'g': // "--genuine" + if( genuine_fname != NULL) { + fprintf( stderr, "ERROR! redefined genuine filename.\n"); + SA_Usage(); + } + genuine_fname = optarg; + break; + case 'm': // "--magicon" + if( magicon_fname != NULL) { + fprintf( stderr, "ERROR! redefined magicon filename.\n"); + SA_Usage(); + } + magicon_fname = optarg; + break; + case 'h': + SA_Usage(); + break; + default: // '?' + SA_Usage(); + break; + } + } +} + diff --git a/build/tools/TamperDetectorForSrl/searcharg.h b/build/tools/TamperDetectorForSrl/searcharg.h new file mode 100644 index 0000000..cf1489f --- /dev/null +++ b/build/tools/TamperDetectorForSrl/searcharg.h @@ -0,0 +1,25 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - - makelst + File: searcharg.h + + Copyright 2006-2008 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + *---------------------------------------------------------------------------*/ +#ifndef __SEARCH_ARG__ +#define __SEARCH_ARG__ + + + + +/*引数を解析する*/ +void SA_searchopt( int argc, char* argv[]); + + + +#endif /*__SEARCH_ARG__*/ diff --git a/build/tools/TamperDetectorForSrl/tamperdetector.exe b/build/tools/TamperDetectorForSrl/tamperdetector.exe new file mode 100755 index 0000000000000000000000000000000000000000..8a83d1694f10e532debb16324b3ecdcb84e7e40c GIT binary patch literal 157273 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 literal 0 HcmV?d00001 diff --git a/build/tools/TamperDetectorForSrl/types.h b/build/tools/TamperDetectorForSrl/types.h new file mode 100644 index 0000000..74d7ed0 --- /dev/null +++ b/build/tools/TamperDetectorForSrl/types.h @@ -0,0 +1,13 @@ + +#ifndef NITRO_TYPES_H_ +#define NITRO_TYPES_H_ + +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned long u32; + +typedef signed short int s16; +typedef signed long s32; + + +#endif /*NITRO_TYPES_H_*/