Exemplo n.º 1
0
/* add module inside the tmd
   take care of alignement.
*/
void tmd_add_module(tmd *p_tmd,const u8 *elf, u32 elf_size)
{
        int ncid;
        int i,found=0;
        tmd_content *p_cr = (tmd_content *)TMD_CONTENTS(p_tmd);
        sha1 hash;
        int content_size = (elf_size+31)&~31;
        u8 *buf = malloc(content_size);
        int index =  p_tmd->num_contents;
		memset((void *) buf,0,content_size);

        memcpy((void *) buf,elf,elf_size);
        ncid = 10;
        while(!found){
                found = 1;
                ncid++;
                for (i=0;i<p_tmd->num_contents;i++) {
                        if(p_cr[i].cid == ncid){ found = 0;break;}
                }
        }
		
        debug_printf("found a free cid: %x\n",ncid);
        p_cr[index].cid = ncid;
        p_cr[index].type = 0x8001; // shared is 0x8001
        p_cr[index].size = content_size;
        p_cr[index].index = index;

        //calc sha
        SHA1(buf, content_size, hash);
        memcpy(p_cr[index].hash, hash, sizeof hash);
        p_tmd->num_contents++;
        save_nus_object(ncid, buf, content_size);
}
Exemplo n.º 2
0
//---------------------------------------------------------------------------------
void print_tmd_summary(const tmd *p_tmd){
//---------------------------------------------------------------------------------
  const tmd_content *p_cr;
  p_cr = TMD_CONTENTS(p_tmd);

  u32 size=0;

  u16 i=0;
  for(i=0;i<p_tmd->num_contents;i++) {
    size += p_cr[i].size;
  }

//	printf("Title ID: %016llx\n",p_tmd->title_id);
//	printf("Number of parts: %d.  Total size: %uK\n", p_tmd->num_contents, (u32) (size / 1024));
}
Exemplo n.º 3
0
int add_custom_modules(tmd *p_tmd)
{
        tmd_content tmp;
        int tmd_dirty=0;
        tmd_content *p_cr = (tmd_content *)TMD_CONTENTS(p_tmd);
#ifdef REMOVE_OH0 // remove oh0 module
        {
                int i;
                debug_printf("removing cid %d\n",p_cr[2].cid);
                memmove(&p_cr[2],&p_cr[3],p_tmd->num_contents*sizeof(tmd_content)-2);
                p_tmd->num_contents--;
                for (i=0;i<p_tmd->num_contents;i++) 
                        p_cr[i].index = i;
                tmd_dirty = 1;
        }
#endif
#ifdef ADD_EHC
/* add ehc module. We need it installed before OH0 and OH1, because IOS loads it in the order of the tmd.
   for some reason, we cant shift all indexes or the IOS_Reload will crashed without saying anything.
*/
        debug_printf("adding EHC module\n");
        tmd_add_module(p_tmd,ehc_elf,ehc_elf_size);
   
		tmp = p_cr[3]; // inverse ehc and oh0 place in tmd
        p_cr[3] = p_cr[p_tmd->num_contents-1];
        p_cr[p_tmd->num_contents-1] = tmp;
        tmd_dirty = 1;
#endif
#ifdef ADD_HAXX
/*add haxx module. We need it installed before OH0 and OH1, because IOS loads it in the order of the tmd.
   for some reason, we cant shift all indexes or the IOS_Reload will crashed without saying anything.
*/
        debug_printf("adding haxx module\n");
        tmd_add_module(p_tmd,mload_elf,mload_elf_size);

		tmp = p_cr[3]; // inverse ehc and oh0 place in tmd
        p_cr[3] = p_cr[p_tmd->num_contents-1];
        p_cr[p_tmd->num_contents-1] = tmp;

   
        tmd_dirty = 1;
#endif
        return tmd_dirty;
}
Exemplo n.º 4
0
s32 extractChannelContents(u64 titleID, char* location)
{
	u32 TitleIDH=TITLE_UPPER(titleID);
	u32 TitleIDL=TITLE_LOWER(titleID);
	if(ES_SetUID(titleID)<0)
		return ERR_UID;

	u32 tmd_size ATTRIBUTE_ALIGN(32);


	if(ES_GetStoredTMDSize(titleID, &tmd_size)<0)
		return ERR_TMDSIZE;

	signed_blob *TMD = (signed_blob *)memalign( 32, tmd_size );
	memset(TMD, 0, tmd_size);

	if(ES_GetStoredTMD(titleID, TMD, tmd_size)<0)
	{
		free(TMD);
		return ERR_TMD;
	}

	u32 cnt ATTRIBUTE_ALIGN(32);

	if(ES_GetNumTicketViews(titleID, &cnt)<0)
	{
		free(TMD);
		return ERR_TIKCOUNT;
	}

	if( cnt <= 0 )
	{
		free(TMD);
		return ERR_TIKCOUNT;
	}

	tikview *views = (tikview *)memalign( 32, sizeof(tikview)*cnt );
	if(ES_GetTicketViews(titleID, views, cnt)<0)
	{
		free(views);
		free(TMD);
		return ERR_TIK;
	}
	printf("Allocated and filled views.\n");
	sleep(3);

	Identify_SU();

	int z;
	tmd_content *TMDc = TMD_CONTENTS(((tmd*)(SIGNATURE_PAYLOAD(TMD))));		// OH GOD CREDIAR, WTF WAS THAT MESS!
	// List format is "XXXXXXXX = YYYYYYYY" where X is index, and Y is cid.
	char *lookup_list=calloc(21,((tmd*)(SIGNATURE_PAYLOAD(TMD)))->num_contents);
	ClearScreen();
	printf("\nNumber of contents: %d\n",((tmd*)(SIGNATURE_PAYLOAD(TMD)))->num_contents);
	sleep(1);
	for(z=0; z < ((tmd*)(SIGNATURE_PAYLOAD(TMD)))->num_contents; z++)
	{
		/* Get Content */
		char nameDirectory[80];
		sprintf(nameDirectory,"/title/%08x/%08x/content/%08x.app",TitleIDH,TitleIDL,TMDc[z].cid);
		s32 contentFd=ISFS_Open(nameDirectory,ISFS_OPEN_READ);
		u8 *data=calloc(TMDc[z].size,1);
		if(contentFd<0)
		{
			switch(contentFd)
			{
				case ISFS_EINVAL:
					printf("FAILED! (Invalid Argument %s)\n\tQuitting...\n",nameDirectory);
					sleep(5);
					Finish(1);
					break;
			
				case ISFS_ENOMEM:
					printf("FAILED! (Out of memory %s)\n\tQuitting...\n",nameDirectory);
					sleep(5);
					Finish(1);
					break;
			
				default:
					goto skip;
//					Finish(1);
					break;
			}
		}
		int isUnaligned = ((int)data)%32 | TMDc[z].size%32;
		int alignedLen = (TMDc[z].size+31)&0xffffffe0;
		unsigned char* alignedBuf;
		if(isUnaligned) alignedBuf = memalign(32, alignedLen);
		else alignedBuf = data;
		ISFS_Seek(contentFd,0,0);
		ISFS_Read(contentFd, alignedBuf, alignedLen);
		// If it was unaligned, you have to copy it and clean up
		if(isUnaligned){
			memcpy(data, alignedBuf, TMDc[z].size);
			free(alignedBuf);
		}
		ISFS_Close(contentFd);

		// Do copying here.
		// data is the actual content data (use fwrite with it :P).
		// Copy the file with it's index as it's filename
		char* destination=calloc(sizeof(location)+14, 1);
		char lookup_entry[21];
		sprintf(destination, "%s/%08x.app",location,TMDc[z].index);
		sprintf(lookup_entry, "%08x = %08x\n",TMDc[z].index,TMDc[z].cid);
		strcat(lookup_list, lookup_entry);
		printf("Got destination as: %s\n", destination);
		sleep(3);
		FILE *dfd=fopen(destination,"wb+");
		printf("Opened %s\n", destination);
		sleep(3);
//		free(destination);
		fwrite(data, TMDc[z].size, 1, dfd);
		printf("Wrote data to %s\n", destination);
		sleep(3);
		fclose(dfd);
		printf("Closed %s\n", destination);
		sleep(2);
skip:
		_nop();
	}
	// Make a file containing the lookups called files.txt
	char* lookup_file=calloc(sizeof(location)+14, 1);
	sprintf(lookup_file, "%s/files.txt",location);
	printf("Got destination as: %s\n", lookup_file);
	sleep(3);
	FILE* lfd=fopen(lookup_file,"wb+");
	printf("Opened %s\n", lookup_file);
	sleep(3);
//	free(lookup_file);
	fwrite(lookup_list, 21, ((tmd*)SIGNATURE_PAYLOAD(TMD))->num_contents, lfd);
	printf("Wrote lookups to %s\n", lookup_file);
	sleep(3);
	fclose(lfd);
	printf("Closed %s\n", lookup_file);
	sleep(2);
	printf("Freed TMD and views");
	sleep(1);
Exemplo n.º 5
0
	return ERR_NONE;
}

void changeChannelName(u64 titleID)
{
	printf("Identifying as 00000001-00000000 (SU)!... ");
	CheckESRetval(ES_Identify(SU_IDENTIFY));
//	if(ES_SetUID(titleID)<0)
//		return;

	u32 tmd_size ATTRIBUTE_ALIGN(32);


	if(CheckESRetval(ES_GetStoredTMDSize(titleID, &tmd_size))!=0)
		return;

	signed_blob *TMD = (signed_blob *)memalign( 32, tmd_size );
	memset(TMD, 0, tmd_size);

	if(CheckESRetval(ES_GetStoredTMD(titleID, TMD, tmd_size))!=0)
	{
		free(TMD);
		return;
	}

	u32 cnt ATTRIBUTE_ALIGN(32);

	if(CheckESRetval(ES_GetNumTicketViews(titleID, &cnt))!=0)
	{
		free(TMD);
		return;
	}

	if( cnt <= 0 )
	{
		free(TMD);
		return;
	}

	tikview *views = (tikview *)memalign( 32, sizeof(tikview)*cnt );

	if(CheckESRetval(ES_GetTicketViews(titleID, views, cnt))!=0)
	{
		free(views);
		free(TMD);
		return;
	}
	char *name=calloc((0x54/2),1);
	int z;
	for(z=0; z < 1; ++z)
	{
		tmd_content *TMDc = TMD_CONTENTS(((tmd*)(SIGNATURE_PAYLOAD(TMD))));		// OH GOD CREDIAR, WTF WAS THAT MESS!!!
		//printf("%d,",TMDc->index);
		s32 cfd = ES_OpenTitleContent( titleID, TMDc->index);
		free(views);
		if(CheckESRetval(cfd)!=0)
		{	
			;
			//printf("ES_OpenContent(%d) failed\n", cfd);
			//sleep(10);
			//exit(0);
		} else {
			u64 sz ATTRIBUTE_ALIGN(32) = 0x140;
			u8 *data = (u8*)memalign(32, sz);

			if( TMDc->size < sz )
				sz = TMDc->size;

			if( data != NULL )
			{
				if(ES_ReadContent(cfd, data, sz)<0)
				{
					free(data);
					return;
				}

				int y;
				int chan_name_offset=(language_setting*0x54);				// Set to WiiMU's language
				for(y=0;y<(0x54/2);y++)
					name[y]=data[(0x9C+chan_name_offset)+(y*2)+1];

			}
			if(CheckESRetval(ES_CloseContent(cfd))!=0)
			{
				;
				//printf("ES_CloseContent failed\n");
				//sleep(10);
				//exit(0);
			}
			free(data);
		}
	}
	int wiistring_size;
reenter:
	wiistring_size=type_string_wiimote(name, 0x54/2);
	if(wiistring_size<=0)
	{
		printf("\n\nPlease enter a name!\n");
		sleep(3);
		goto reenter;
	}
	name[wiistring_size+1]=0;

	/* Get Content */
	char nameDirectory[80];
	tmd_content *TMDc = TMD_CONTENTS(((tmd*)(SIGNATURE_PAYLOAD(TMD))));
	sprintf(nameDirectory,"/title/%08x/%08x/content/%08x.app",TITLE_UPPER(titleID),TITLE_LOWER(titleID),TMDc->cid);
	s32 contentFd=ISFS_Open(nameDirectory,ISFS_OPEN_RW);
	CheckISFSRetval(contentFd);
	ClearScreen();
	printf("\n\nOpened content!\n");
	u8 *data=calloc(TMDc->size,1);
	int isUnaligned = ((int)data)%32 | TMDc->size%32;
	int alignedLen = (TMDc->size+31)&0xffffffe0;
	u8* alignedBuf;
	if(isUnaligned) alignedBuf = memalign(32, alignedLen);
	else alignedBuf = data;
	CheckISFSRetval(ISFS_Seek(contentFd,0,0));
	CheckISFSRetval(ISFS_Read(contentFd, alignedBuf, alignedLen));
	printf("Read content!\n");
	int y;
	int chan_name_offset=(SYSCONF_GetArea()*0x54);		// Edits the one for the Wii's system Menu
	char* nameOut=calloc(96,1);
	for(y=0;y<(0x54/2);y++)
		nameOut[(y*2)+1]=name[y];
	printf("Wrote new name! %s\n", name);
	CheckISFSRetval(ISFS_Seek(contentFd,(0x9C+chan_name_offset),0));
	printf("Seeked to location!\n");
	if(nameOut==NULL)
	{
		printf("FAILED! (Name Out is NULL!)\n");
		sleep(5);
		Finish(1);
	}
	if(((u32)nameOut%32)!=0)
	{
		isUnaligned = ((int)nameOut)%32 | 0x54%32;
		alignedLen = (0x54+31)&0xffffffe0;
		if(isUnaligned){ alignedBuf = memalign(32, alignedLen); memcpy(alignedBuf, nameOut, 0x54); }
		else alignedBuf = (u8*)nameOut;
	}
	CheckISFSRetval(ISFS_Write(contentFd, alignedBuf, 0x54));
	printf("Wrote content name!\nReading new Header Chunk!\n");
	CheckISFSRetval(ISFS_Seek(contentFd,0,0));
	CheckISFSRetval(ISFS_Read(contentFd, alignedBuf, alignedLen));
	printf("Read content!\n");
	u8* header_chunk=calloc(0x640, 1);
	int i;
	for(i=0;i<0x630;i++)
		header_chunk[i]=alignedBuf[i];
	for(i=0x630;i<0x640;i++)
		header_chunk[i]=0;
	u8* hash=calloc(0x10,1);
	md5(header_chunk, 0x640, hash);
	CheckISFSRetval(ISFS_Seek(contentFd,0x630,0));
	printf("Seeked to location!\n");
	if(hash==NULL)
	{
		printf("FAILED! (Hash is NULL!)\n");
		sleep(5);
		Finish(1);
	}
	if(((u32)hash%32)!=0)
	{
		isUnaligned = ((int)hash)%32 | 0x10%32;
		alignedLen = (0x10+31)&0xffffffe0;
		if(isUnaligned){ alignedBuf = memalign(32, alignedLen); memcpy(alignedBuf, hash, 0x10); }
		else alignedBuf = hash;
Exemplo n.º 6
0
s32 Install_patched_IOS(u32 iosnr, u32 iosrevision, bool es_trucha_patch, bool es_identify_patch, bool nand_patch, bool version_patch, bool Kill_AntiSysTitleInstall_patch, u32 location, u32 newrevision, bool free)
{
	int ret;
/*	if (iosnr == location && iosrevision == newrevision && !es_trucha_patch && !es_identify_patch && !nand_patch && !Kill_AntiSysTitleInstall_patch)
	{
		ret = install_unpatched_IOS(iosnr, iosrevision, free);
		return ret;
	}
*/	
	IOS *ios;
	int index;
	bool tmd_dirty = false;
	bool tik_dirty = false;
	
	printf("Getting IOS%u revision %u...\n", iosnr, iosrevision);
	ret = get_IOS(&ios, iosnr, iosrevision);
	if (ret < 0)
	{
		printf("Error reading IOS into memory\n");
		return -1;
	}
	
	tmd *p_tmd = (tmd*)SIGNATURE_PAYLOAD(ios->tmd);
	tmd_content *p_cr = TMD_CONTENTS(p_tmd);

	index = kernel_index(ios);
	if (index < 0)
	{
		printf("Could not identify kernel\n");
		free_IOS(&ios);
		return -1;
	}
	printf("Kernel found at index %d...Patching.\n", index);
	// before loading this IOS some valid ARM code needs to be written to the beginning of mem2
	// or it will crash every time it tries to check kernel debug flags (syscall 0x4b)
	patch_syscall(ios->decrypted_buffer[index], 0x4b, 0x10000000);
	
	// Force the patched module to be not shared
	tmd_content *content = &p_tmd->contents[index];
	content->type = 1;

	// Update the content hash inside the tmd
	sha1 hash;
	SHA1(ios->decrypted_buffer[index], (u32)p_cr[index].size, hash);
	memcpy(p_cr[index].hash, hash, sizeof hash);
	tmd_dirty = true;

	if (es_trucha_patch || es_identify_patch || nand_patch || version_patch || Kill_AntiSysTitleInstall_patch)
	{
		index = module_index(ios, "ES:");
		if (index < 0)
		{
			printf("Could not identify ES module\n");
			free_IOS(&ios);
			return -1;
		}
		int trucha = 0;
		int identify = 0;
		int nand = 0;
		int version = 0;
		int systitle = 0;
		
		if (es_trucha_patch)
		{		
			printf("Patching trucha bug into ES module(#%u)...", index);
			trucha = patch_hash_check(ios->decrypted_buffer[index], ios->buffer_size[index]);
			printf("patched %u hash check(s)\n", trucha);
		}
		
		if (es_identify_patch)
		{
			printf("Patching ES_Identify in ES module(#%u)...", index);
			identify = patch_identify_check(ios->decrypted_buffer[index], ios->buffer_size[index]);
			printf("patch applied %u time(s)\n", identify);		
		}
		
		if (nand_patch)
		{
			printf("Patching nand permissions in ES module(#%u)...", index);
			nand = patch_patch_fsperms(ios->decrypted_buffer[index], ios->buffer_size[index]);
			printf("patch applied %u time(s)\n", nand);		
		}
		
		if (version_patch)
		{
			printf("Patching version check in ES module(#%u)...", index);
			version = patch_version_check(ios->decrypted_buffer[index], ios->buffer_size[index]);
			printf("patch applied %u time(s)\n", version);			
		}
		
		if (Kill_AntiSysTitleInstall_patch)
		{
			printf("Patching out Systitle Anti-Install Routine in ES module(#%u)...", index);
			systitle = patch_Kill_AntiSysTitleInstall(ios->decrypted_buffer[index], ios->buffer_size[index]);
			printf("patch applied %u time(s)\n", systitle);
		}
		
		if (trucha > 0 || identify > 0 || nand > 0 || version > 0 || systitle > 0)
		{
			// Force the patched module to be not shared
			tmd_content *content = &p_tmd->contents[index];
			content->type = 1;

			// Update the content hash inside the tmd
			sha1 hash;
			SHA1(ios->decrypted_buffer[index], (u32)p_cr[index].size, hash);
			memcpy(p_cr[index].hash, hash, sizeof hash);
			tmd_dirty = true;
		}
	}

	if (iosnr != location)
	{
		change_ticket_title_id(ios->ticket, 1, location);
		change_tmd_title_id(ios->tmd, 1, location);
		tmd_dirty = true;
		tik_dirty = true;
	}
	
	if (iosrevision != newrevision)
	{
		p_tmd->title_version = newrevision;
		tmd_dirty = true;
	}

	if (tmd_dirty)
	{
		printf("Trucha signing the tmd...\n");
		forge_tmd(ios->tmd);
	}

	if (tik_dirty)
	{
		printf("Trucha signing the ticket..\n");
		forge_tik(ios->ticket);
	}

	printf("Encrypting IOS...\n");
	encrypt_IOS(ios);
	
	printf("Preparations complete\n\n");
	

	printf("Installing...\n");
/*	ret = install_IOS(ios, false);
	if (ret < 0)
	{
		free_IOS(&ios);
		if (ret == -1017 || ret == -2011)
		{
			printf("You need to use an IOS with trucha bug.\n");
			printf("That's what the IOS15 downgrade is good for...\n");
		} else
		if (ret == -1035)
		{
			printf("Has your installed IOS%u a higher revison than %u?\n", iosnr, iosrevision);
		}
		
		return -1;
	}
*/	printf("done\n");
	
	if (free) free_IOS(&ios);
	return 0;
}
Exemplo n.º 7
0
s32 Download_IOS(IOS **ios, u32 iosnr, u32 revision)
{
	s32 ret;

	ret = Init_IOS(ios);
	if (ret < 0)
	{
		printf("Out of memory\n");
		goto err;
	}

	tmd *tmd_data  = NULL;
	u32 cnt;
	//static bool network_initialized = false;
	char buf[32];
	
	if (!network_initialized)
	{
		printf("Initializing network...");
		while (1) 
		{
			ret = net_init ();
			if (ret < 0) 
			{
				//if (ret != -EAGAIN) 
				if (ret != -11) 
				{
					printf ("net_init failed: %d\n", ret);
					goto err;
				}
			}
			if (!ret) break;
			usleep(100000);
			printf(".");
		}
		printf("done\n");
		network_initialized = true;
	}

	printf("Loading certs...\n");
	ret = GetCerts(&((*ios)->certs), &((*ios)->certs_size));
	if (ret < 0)
	{
		printf ("Loading certs from nand failed, ret = %d\n", ret);
		goto err;	
	}

	if ((*ios)->certs == NULL || (*ios)->certs_size == 0)
	{
		printf("certs error\n");
		ret = -1;
		goto err;		
	}

	if (!IS_VALID_SIGNATURE((*ios)->certs))
	{
		printf("Error: Bad certs signature!\n");
		ret = -1;
		goto err;
	}
	
	printf("Loading TMD...\n");
	sprintf(buf, "tmd.%u", revision);
	u8 *tmd_buffer = NULL;
	ret = get_nus_object(1, iosnr, buf, &tmd_buffer, &((*ios)->tmd_size));
	if (ret < 0)
	{
		printf("Loading tmd failed, ret = %u\n", ret);
		goto err;	
	}

	if (tmd_buffer == NULL || (*ios)->tmd_size == 0)
	{
		printf("TMD error\n");
		ret = -1;
		goto err;		
	}
	
	(*ios)->tmd_size = SIGNED_TMD_SIZE((signed_blob *)tmd_buffer);
 	(*ios)->tmd = MEM2_memalign(32, (*ios)->tmd_size);
	if ((*ios)->tmd == NULL)
	{
		printf("Out of memory\n");
		ret = -1;
		goto err;		
	}
	memcpy((*ios)->tmd, tmd_buffer, (*ios)->tmd_size);
	free(tmd_buffer);
	
	if (!IS_VALID_SIGNATURE((*ios)->tmd))
	{
		printf("Error: Bad TMD signature!\n");
		ret = -1;
		goto err;
	}

	printf("Loading ticket...\n");
	u8 *ticket_buffer = NULL;
	ret = get_nus_object(1, iosnr, "cetk", &ticket_buffer, &((*ios)->ticket_size));
	if (ret < 0)
	{
		printf("Loading ticket failed, ret = %u\n", ret);
		goto err;	
	}

	if (ticket_buffer == NULL || (*ios)->ticket_size == 0)
	{
		printf("ticket error\n");
		ret = -1;
		goto err;		
	}

	(*ios)->ticket_size = SIGNED_TIK_SIZE((signed_blob *)ticket_buffer);
 	(*ios)->ticket = MEM2_memalign(32, (*ios)->ticket_size);
	if ((*ios)->ticket == NULL)
	{
		printf("Out of memory\n");
		ret = -1;
		goto err;		
	}
	memcpy((*ios)->ticket, ticket_buffer, (*ios)->ticket_size);
	free(ticket_buffer);
	
	if(!IS_VALID_SIGNATURE((*ios)->ticket))
	{
		printf("Error: Bad ticket signature!\n");
		ret = -1;
		goto err;
	}

	/* Get TMD info */
	tmd_data = (tmd *)SIGNATURE_PAYLOAD((*ios)->tmd);

	printf("Checking titleid and revision...\n");
	if (TITLE_UPPER(tmd_data->title_id) != 1 || TITLE_LOWER(tmd_data->title_id) != iosnr)
	{
		printf("IOS has titleid: %08x%08x but expected was: %08x%08x\n", TITLE_UPPER(tmd_data->title_id), TITLE_LOWER(tmd_data->title_id), 1, iosnr);
		ret = -1;
		goto err;
	}

	if (tmd_data->title_version != revision)
	{
		printf("IOS has revision: %u but expected was: %u\n", tmd_data->title_version, revision);
		ret = -1;
		goto err;
	}

	ret = set_content_count(*ios, tmd_data->num_contents);
	if (ret < 0)
	{
		printf("Out of memory\n");
		goto err;
	}

	printf("Loading contents");
	for (cnt = 0; cnt < tmd_data->num_contents; cnt++) 
	{
		printf(".");
		tmd_content *content = &tmd_data->contents[cnt];

		/* Encrypted content size */
		(*ios)->buffer_size[cnt] = round_up((u32)content->size, 64);

		sprintf(buf, "%08x", content->cid);
   
		ret = get_nus_object(1, iosnr, buf, &((*ios)->encrypted_buffer[cnt]), &((*ios)->buffer_size[cnt]));

		if ((*ios)->buffer_size[cnt] % 16) 
		{
			printf("Content %u size is not a multiple of 16\n", cnt);
			ret = -1;
			goto err;
		}

   		if ((*ios)->buffer_size[cnt] < (u32)content->size) 
		{
			printf("Content %u size is too small\n", cnt);
			ret = -1;
			goto err;
   		} 

		(*ios)->decrypted_buffer[cnt] = MEM2_memalign(32, (*ios)->buffer_size[cnt]);
		if (!(*ios)->decrypted_buffer[cnt])
		{
			printf("Out of memory\n");
			ret = -1;
			goto err;
		}

	}
	printf("done\n");
	
	printf("Reading file into memory complete.\n");

	printf("Decrypting IOS...\n");
	decrypt_IOS(*ios);

	tmd_content *p_cr = TMD_CONTENTS(tmd_data);
	sha1 hash;
	int i;

	printf("Checking hashes...\n");
	for (i=0;i < (*ios)->content_count;i++)
	{
		SHA1((*ios)->decrypted_buffer[i], (u32)p_cr[i].size, hash);
		if (memcmp(p_cr[i].hash, hash, sizeof hash) != 0)
		{
			printf("Wrong hash for content #%u\n", i);
			ret = -1;
			goto err;
		}
	}	

	goto out;

err:
	free_IOS(ios);

out:
	return ret;
}