ツールデバッグ用の不正ROMを作成するツールを作成。指定したビットを上げ下げしたり、クリアして、CRCと署名を再計算するプログラム。

git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlToolsRED@338 7061adef-622a-194b-ae81-725974e89856
This commit is contained in:
nishikawa_takeshi 2009-07-02 06:45:20 +00:00
parent 3769e0664b
commit 62df5741b8
2 changed files with 673 additions and 0 deletions

View File

@ -0,0 +1,80 @@
#! make -f
#---------------------------------------------------------------------------
# Project: TwlSDK - tools - mastering.TWL
# File: Makefile.TWL
#
# Copyright 2007 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.
#
# $Date:: #$
# $Rev$
# $Author$
#---------------------------------------------------------------------------
TARGET_PLATFORM = TWL
include $(TWLSDK_ROOT)/build/buildtools/commondefs
TARGETS = IllegalRomMaker.exe
SOURCES_C = main.c ../MenuSkipFlagTool/keys.c
SOURCES = $(SOURCES_C)
OBJECTS = $(SOURCES:.c=.o)
INCDIR = $(TWLSDK_ROOT)/build/tools/makerom.TWL \
$(TWL_IPL_RED_ROOT)/include \
../MenuSKipFlagTool
REVISION_H = revision.h
#INSTALL_DIR = $(FIRM_INSTALL_TOOLSDIR)/bin
#INSTALL_TARGETS = $(TARGETS)
MACROS += $(addprefix -I,$(INCDIR)) -DPLATFORM_ENDIAN_LITTLE
LIBACSIGN = $(ROOT)/build/tools/makerom.TWL/acsign/lib/X86/libacsign_x86.a
LIBAES = $(ROOT)/build/tools/makerom.TWL/aes/lib/X86/libaes_x86.a
LIBDIGEST = $(ROOT)/build/tools/makerom.TWL/digest/lib/X86/libdigest_x86.a
#LIBDGT = $(ROOT)/build/tools/libdgt.TWL/lib/libdgt.TWL.a
LDIRT_CLEAN = $(OBJECTS) $(TARGETS) *.BAK $(REVISION_H)
include $(TWLSDK_ROOT)/build/buildtools/twl/modulerules.x86
#----------------------------------------------------------------------------
# build
#----------------------------------------------------------------------------
do-build: $(TARGETS)
$(TARGETS): $(REVISION_H) $(OBJECTS) $(LIBACSIGN) $(LIBAES) $(LIBDIGEST)
$(CC_X86) $+ -o $@ -lnetapi32 -lwsock32
main.o: main.c
#main.h:
# avoid to warning message
#misc.o:WARNING += -Wno-format-y2k
#
$(REVISION_H) ::
@if test -e $(TWLSDK_ROOT)/.svn; then \
LANG=C svn info $(call empath,$(TWLSDK_ROOT)) | grep 'Revision' | sed "s|Revision: \(.*\)|#define SDK_REVISION \"\1\"|" > $(REVISION_H); \
else \
echo "#define SDK_REVISION \"-\"" > $(REVISION_H); \
fi
@if test -e $(TWL_IPL_RED_ROOT)/.svn; then \
LANG=C svn info $(call empath,$(TWL_IPL_RED_ROOT)) | grep 'Revision' | sed "s|Revision: \(.*\)|#define IPL_REVISION \"\1\"|" >> $(REVISION_H); \
else \
echo "#define IPL_REVISION \"-\"" >> $(REVISION_H); \
fi
#test: path.c misc.c
# $(CC_X86) -DTEST $+ -o $@

View File

@ -0,0 +1,593 @@
/*---------------------------------------------------------------------------*
Project: TwlFirmware - tools - mastering
File: mastering.c
Copyright 2003 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.
$Date:: #$
$Rev$
$Author$
*---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // strcasecmp()
#include <getopt.h> // getopt()
#include <sys/stat.h> // stat()
#include <stdarg.h>
#include <conio.h>
#include <twl/types.h>
#include <twl/os/common/format_rom.h>
#include <twl/os/common/ownerInfoEx.h>
#include <acsign/include/acsign.h>
#include "revision.h"
#include "keys.h"
/*---------------------------------------------------------------------------*
&
*---------------------------------------------------------------------------*/
// -- 内部パラメータ
// CRC (makerom.TWL/misc.cとmakerom.TWL/format_rom_private.hからコピペ)
#define CRC16_INIT_VALUE (0xffff)
#define CALC_CRC16_SIZE (0x15e)
// -- Context
typedef struct _SContext
{
// 入出力ファイル
FILE *ifp;
FILE *ofp;
// オプション
BOOL isBitEnable; // ビット上げモード
BOOL isBitDisable; // ビット下げモード
BOOL isByteClear; // バイトクリアモード
BOOL isByteAssign; // バイト代入モード
// 引数
u32 byteAddr; // 不正挿入バイトアドレス
u32 bitAddr; // 不正挿入ビットアドレス(オプション)
u8 assignValue; // 代入値(オプション)
}
SContext;
// -- internal function
static BOOL iMain( SContext *pContext );
BOOL CopyFile( FILE *dst, FILE *src );
u16 CalcCRC16(u16 start, u8 *data, int size);
BOOL SignRomHeader( ROM_Header *prh );
BOOL DecryptSignRomHeader( ROM_Header *prh );
/*---------------------------------------------------------------------------*
*---------------------------------------------------------------------------*/
BOOL DebugMode = FALSE;
/*---------------------------------------------------------------------------*
*---------------------------------------------------------------------------*/
void usage()
{
printf( "-----------------------------------------------------------------------------\n" );
printf( "Usage: RatingAllFreeTool.exe input_file output_file ByteAddr [option]\n" );
printf( " input_file : a ROM data file.\n" );
printf( " output_file : a destination file.\n" );
printf( " ByteAddr : an address of Byte which will be insert an illegal data. [00000000-FFFFFFFF]\n" );
printf( "\nOption:\n" );
printf( "-e bitAddr : enable the specific bit. [0-7]\n" );
printf( "-d bitAddr : desable the specific bit. [0-7]\n" );
printf( "-c : clear the byte.\n" );
printf( "-a value : assign the value into the byte. [00-FF]\n" );
printf( "-h : print help only.\n" );
printf( "-f : force to overwrite a output_file.\n" );
printf( "-----------------------------------------------------------------------------\n" );
}
int main(int argc, char *argv[])
{
SContext context;
char *pSrc, *pDst;
int opt;
extern char *optarg;
extern int optind, opterr;
struct stat st;
char *errptr;
BOOL bResult = TRUE;
BOOL bForceOverwrite = FALSE;
printf( "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" );
printf( " ManuSkipFlagTool [%s-%s]\n", SDK_REVISION, IPL_REVISION );
printf( "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" );
// context の初期化
memset( &context, 0, sizeof(SContext) );
// オプション
while( (opt = getopt(argc, argv, "e:d:ca:hf")) >= 0 )
{
switch( opt )
{
case 'e':
context.isBitEnable = TRUE;
context.bitAddr = atoi(optarg);
break;
case 'd':
context.isBitDisable = TRUE;
context.bitAddr = atoi(optarg);
break;
case 'c':
context.isByteClear = TRUE;
break;
case 'a':
context.isByteAssign = TRUE;
context.assignValue = strtol(optarg, &errptr, 16);
break;
case 'h':
usage();
return 0;
break;
case 'f':
bForceOverwrite = TRUE;
break;
default: // オプション引数が指定されていないときにも実行される
usage();
fprintf( stdout, "\n*** Error: illegal option. ***\n" );
exit(1);
break;
}
}
argc = argc - optind;
argv = argv + optind;
// 引数処理
if( argc != 3 )
{
usage();
printf( "\n*** Error: Auguments. ***\n" );
return -1;
}
pSrc = argv[0];
pDst = argv[1];
context.byteAddr = strtol(argv[2], &errptr, 16);
printf( "Auguments:\n" );
printf( " input_file : %s\n", pSrc );
printf( " output_file : %s\n", pDst );
printf( "--\n" );
// 入力ファイルと出力ファイル名が同じならダメ
if( (strlen(pSrc) == strlen(pDst)) && (memcmp(pSrc, pDst, strlen(pSrc)) == 0) )
{
printf( "\n*** Error: Same filename between the input_file and the output_file. ***\n" );
printf( "\n" );
printf( "error.\n" );
return -1;
}
// 出力ファイルが存在するとき上書きするかどうかを確認する
if( !bForceOverwrite && stat( pDst, &st )==0 )
{
char ans;
printf( "The %s already exists. Overwrite it? (y or n) ", pDst );
//fscanf( stdin, "%c", &ans );
ans = (char)getch(); // リターンキーなし
printf( "%c\n", ans ); // getch では入力が表示されないので自分で表示する必要あり
printf( "\n" );
if( !(ans=='y') && !(ans=='Y') )
{
printf( "\n" );
printf( "canceled.\n" );
return -1;
}
}
// ファイルを開く
context.ifp = fopen(pSrc, "rb");
context.ofp = fopen(pDst, "wb+");
if( !(context.ifp) || !(context.ofp) )
{
printf( "\n*** Error: Failed to open the file. ***\n" );
bResult = FALSE;
goto FINALIZE;
}
// ファイルコピー
if( !CopyFile( context.ofp, context.ifp ) )
{
printf( "\n*** Error: Failed to copy the file. ***\n" );
bResult = FALSE;
goto FINALIZE;
}
// フラグ立てが指定されていないときはコピーだけしてそのまま終了
bResult = iMain( &context );
// 終了処理
FINALIZE:
if( !bResult )
{
printf( "\n" );
printf( "error.\n" );
}
else
{
printf( "\n" );
printf( "succeeded.\n" );
}
if( context.ifp ) fclose(context.ifp); // 資源の解放
if( context.ofp ) fclose(context.ofp);
if( (!bResult) && (stat( pDst, &st ) == 0) )
{
unlink( pDst ); // 出力ファイルを削除する
}
return ((bResult == TRUE)?0:-1); // 成功のとき0
}
/*---------------------------------------------------------------------------*
*---------------------------------------------------------------------------*/
static BOOL iMain( SContext *pContext )
{
u8 byte;
// 指定されたバイトをリード
printf("Byte Address : 0x%X\n", (unsigned int)pContext->byteAddr);
fseek( pContext->ifp, pContext->byteAddr, SEEK_SET );
if( 1 != fread( &byte, 1, 1, pContext->ifp ) )
{
printf( "\n*** Error: Failed to read the byte from input_file. ***\n" );
return FALSE;
}
if( pContext->isBitEnable )
{
u8 mask = 1 << pContext->bitAddr;
u8 oldbyte = byte;
byte |= mask;
printf("Enable bit %d : 0x%02X -> 0x%02X\n", (int)pContext->bitAddr, oldbyte, byte);
}
if( pContext->isBitDisable )
{
u8 mask = 1 << pContext->bitAddr;
u8 oldbyte = byte;
byte &= ~mask;
printf("Disable bit %d : 0x%02X -> 0x%02X\n", (int)pContext->bitAddr, oldbyte, byte);
}
if( pContext->isByteClear )
{
u8 oldbyte = byte;
byte = 0;
printf("Clear byte : 0x%02X -> 0x%02X\n", oldbyte, byte);
}
if( pContext->isByteAssign )
{
printf("Assign : 0x%02X -> 0x%02X\n", byte, pContext->assignValue);
byte = pContext->assignValue;
}
// 変更したバイトを出力ファイルに書き出す
fseek( pContext->ofp, pContext->byteAddr, SEEK_SET );
if( 1 != fwrite( &byte, 1, 1, pContext->ofp ) )
{
printf( "\n*** Error: Failed to write the byte to output_fie. ***\n" );
return FALSE;
}
// 変更したバイトがCRCの対象領域ならCRC再計算
if( (0 <= pContext->byteAddr) && (pContext->byteAddr < 0x15e) )
{
ROM_Header rh;
u16 curr_crc;
// ROMヘッダをリード(変更は出力ファイルに加えられたので出力ファイルからリードする)
fseek( pContext->ofp, 0, SEEK_SET );
if( sizeof(ROM_Header) != fread( &rh, 1, sizeof(ROM_Header), pContext->ofp ) )
{
printf( "\n*** Error: Failed to read the ROM Header. ***\n" );
return FALSE;
}
// ヘッダCRC再計算
curr_crc = rh.s.header_crc16;
rh.s.header_crc16 = CalcCRC16( CRC16_INIT_VALUE, (u8*)&rh, CALC_CRC16_SIZE );
printf( "Header CRC : 0x%04X -> 0x%04X\n", curr_crc, rh.s.header_crc16 );
// ROMヘッダを上書き
fseek( pContext->ofp, 0, SEEK_SET );
if( sizeof(ROM_Header) != fwrite( &rh, 1, sizeof(ROM_Header), pContext->ofp ) )
{
printf( "\n*** Error: Failed to write the ROM Header. ***\n" );
return FALSE;
}
}
// 変更したバイトが署名の対象領域なら再署名
if( (0 <= pContext->byteAddr) && (pContext->byteAddr < 0xe00) )
{
ROM_Header rh;
// ROMヘッダをリード(変更は出力ファイルに加えられたので出力ファイルからリードする)
fseek( pContext->ofp, 0, SEEK_SET );
if( sizeof(ROM_Header) != fread( &rh, 1, sizeof(ROM_Header), pContext->ofp ) )
{
printf( "\n*** Error: Failed to read the ROM Header. ***\n" );
return FALSE;
}
// 再署名
if( !SignRomHeader( &rh ) )
{
printf( "Encrypt the sign : Failed\n" );
printf("\n*** Error: Failed to encrypt the sigunature. ***\n");
return FALSE;
}
printf( "Encrypt the sign : Succeeded\n" );
// ROMヘッダを上書き
fseek( pContext->ofp, 0, SEEK_SET );
if( sizeof(ROM_Header) != fwrite( &rh, 1, sizeof(ROM_Header), pContext->ofp ) )
{
printf( "\n*** Error: Failed to write the ROM Header. ***\n" );
return FALSE;
}
}
return TRUE;
}
/*---------------------------------------------------------------------------*
*---------------------------------------------------------------------------*/
// ファイルコピー用バッファ
#define BUFSIZE (16*1024)
BOOL CopyFile( FILE *dst, FILE *src )
{
static u8 buf[ BUFSIZE ];
int size;
fseek( src, 0, SEEK_END );
size = ftell( src );
fseek( src, 0, SEEK_SET );
fseek( dst, 0, SEEK_SET );
while( size > 0 )
{
int len = (BUFSIZE < size)?BUFSIZE:size;
if( len != fread( buf, 1, len, src ) )
{
printf( "\n*** Error! failed to read the file in copying. ***\n" );
return FALSE;
}
if( len != fwrite( buf, 1, len, dst ) )
{
printf( "\n*** Error! failed to write the file in copying. ***\n" );
return FALSE;
}
size -= len;
}
return TRUE;
}
/*---------------------------------------------------------------------------*
CRCの計算
*---------------------------------------------------------------------------*/
static u16 crc16_table[16] = {
0x0000, 0xCC01, 0xD801, 0x1400,
0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401,
0x5000, 0x9C01, 0x8801, 0x4400
};
u16 CalcCRC16(u16 start, u8 *data, int size)
{
u16 r1;
u16 total = start;
while (size-- > 0)
{
// 下位4bit
r1 = crc16_table[total & 0xf];
total = (total >> 4) & 0x0fff;
total = total ^ r1 ^ crc16_table[*data & 0xf];
// 上位4bit
r1 = crc16_table[total & 0xf];
total = (total >> 4) & 0x0fff;
total = total ^ r1 ^ crc16_table[(*data >> 4) & 0xf];
data++;
}
return total;
}
/*---------------------------------------------------------------------------*
ROMヘッダの署名 ()
*---------------------------------------------------------------------------*/
BOOL SignRomHeader( ROM_Header *prh )
{
SignatureData signSrc; // 署名のもととなるダイジェスト値
u8 signDst[ RSA_KEY_LENGTH ]; // 署名の格納先Tmpバッファ
u8 decryptBlock[ RSA_KEY_LENGTH ]; // 署名を解除後ブロックバッファ
BOOL result;
int pos;
const u8 *privatekey = NULL;
const u8 *publickey = NULL;
u8 *idL = prh->s.titleID_Lo;
u32 idH = prh->s.titleID_Hi;
// ROMヘッダのダイジェストを算出(先頭から証明書領域の直前までが対象)
ACSign_DigestUnit(
signSrc.digest,
prh,
(u32)&(prh->certificate) - (u32)prh
);
// 鍵選択
if( (idL[3]=='H') && (idL[2]=='N') && (idL[1]=='A') ) // ランチャアプリかどうかはTitleID_Loの値で決定
{
printf("Key type : Launcher\n");
privatekey = g_devPrivKey_DER_Launcher;
publickey = g_devPubKey_DER_Launcher;
}
else if( idH & TITLE_ID_HI_SECURE_FLAG_MASK ) // 各ビットは排他的とは限らないのでelse ifにはならない
{
printf("Key type : Secure\n");
privatekey = g_devPrivKey_DER_Secure;
publickey = g_devPubKey_DER_Secure;
}
else if( (idH & TITLE_ID_HI_APP_TYPE_MASK) == 1 )
{
printf("Key type : System\n");
privatekey = g_devPrivKey_DER_System;
publickey = g_devPubKey_DER_System;
}
else if( (idH & TITLE_ID_HI_APP_TYPE_MASK) == 0 )
{
printf("Key type : User\n");
privatekey = g_devPrivKey_DER_User;
publickey = g_devPubKey_DER_User;
}
if( !privatekey || !publickey )
{
printf( "\n*** Error: Illegal key. ***\n" );
return FALSE;
}
// ダイジェストに署名をかける
result = ACSign_Encrypto( signDst, privatekey, &signSrc, sizeof(SignatureData) );
if( !result )
{
printf( "\n*** Error: Failed to encrypt the signature. ***\n" );
return FALSE;
}
// 署名を解除してダイジェストと一致するかベリファイする
result = ACSign_Decrypto( decryptBlock, publickey, signDst, RSA_KEY_LENGTH );
for( pos=0; pos < RSA_KEY_LENGTH; pos++ )
{
if( decryptBlock[pos] == 0x0 ) // 解除後ブロックから実データをサーチ
break;
}
if( !result || (memcmp( &signSrc, &(decryptBlock[pos+1]), sizeof(SignatureData) ) != 0) )
{
printf( "\n*** Error: Failed to verify the signature. ***\n" );
return FALSE;
}
// ROMヘッダに署名を上書き
memcpy( prh->signature, signDst, RSA_KEY_LENGTH );
return TRUE;
} // ECSrlResult RCSrl::signRomHeader(void)
// 署名を外す
BOOL DecryptSignRomHeader( ROM_Header *prh )
{
u8 original[ RSA_KEY_LENGTH ]; // 署名外した後のデータ格納先
s32 pos = 0; // ブロックの先頭アドレス
u8 digest[ DIGEST_SIZE_SHA1 ]; // ROMヘッダのダイジェスト
const u8 *publickey = NULL;
u8 *idL = prh->s.titleID_Lo;
u32 idH = prh->s.titleID_Hi;
// <データの流れ>
// (1) 公開鍵で復号した結果(ブロック)をローカル変数(original)に格納
// (2) ブロックから余分な部分を取り除いて引数(pDst)にコピー
// 鍵選択
if( (idL[3]=='H') && (idL[2]=='N') && (idL[1]=='A') ) // ランチャアプリかどうかはTitleID_Loの値で決定
{
publickey = g_devPubKey_DER_Launcher;
}
else if( idH & TITLE_ID_HI_SECURE_FLAG_MASK ) // 各ビットは排他的とは限らないのでelse ifにはならない
{
publickey = g_devPubKey_DER_Secure;
}
else if( (idH & TITLE_ID_HI_APP_TYPE_MASK) == 1 )
{
publickey = g_devPubKey_DER_System;
}
else if( (idH & TITLE_ID_HI_APP_TYPE_MASK) == 0 )
{
publickey = g_devPubKey_DER_User;
}
if( !publickey )
{
printf( "\n*** Error: Illegal key. ***\n" );
return FALSE;
}
// 署名の解除 = 公開鍵で復号
if( !ACSign_Decrypto( original, publickey, prh->signature, RSA_KEY_LENGTH ) )
{
printf( "\n*** Error: Failed to decrypt the signature. ***\n" );
return FALSE;
}
// 署名前データを復号後ブロックからゲット
for( pos=0; pos < (RSA_KEY_LENGTH-2); pos++ ) // 本来ブロックの先頭は0x00だが復号化の内部処理によって消える仕様
{
// 暗号ブロック形式 = 0x00, BlockType, Padding, 0x00, 実データ
if( original[pos] == 0x00 ) // 実データの直前の0x00をサーチ
{
break;
}
}
// ベリファイ
// ROMヘッダのダイジェストを算出(先頭から証明書領域の直前までが対象)
ACSign_DigestUnit( digest, prh, (u32)&(prh->certificate) - (u32)prh );
if( memcmp( &(original[pos+1]), digest, DIGEST_SIZE_SHA1 ) != 0 )
{
return FALSE;
}
return TRUE;
}
// end of file