Godspeed Chovy

This commit is contained in:
SilicaAndPina 2019-10-07 05:55:42 +13:00
parent 8387d6029c
commit 9d698dc492
5 changed files with 786 additions and 68 deletions

View File

@ -77,6 +77,8 @@
<Compile Include="pbp.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PSVIMGReader.cs" />
<Compile Include="PSVIMGStream.cs" />
<Compile Include="psvimgtools.cs" />
<EmbeddedResource Include="CHOVY.resx">
<DependentUpon>CHOVY.cs</DependentUpon>

View File

@ -2,6 +2,7 @@
using DiscUtils.Iso9660;
using Microsoft.Win32;
using Param_SFO;
using PSVIMGTOOLS;
using System;
using System.Diagnostics;
using System.Drawing;
@ -313,9 +314,6 @@ namespace CHOVY
private void FindFromCMA_Click(object sender, EventArgs e)
{
/* Ccs_FormClosing(null, null);
return;*/
this.Hide();
string cmaDir = "";
string accountId = "0000000000000000";
@ -386,86 +384,54 @@ namespace CHOVY
{
return;
}
string output = Path.Combine(Application.StartupPath, "_tmp");
Directory.CreateDirectory(output);
string OutputPbp = Path.Combine(output, "ux0_pspemu_temp_game_PSP_GAME_" + Backup, "EBOOT.PBP");
string GameFolder = Path.Combine(output, "ux0_pspemu_temp_game_PSP_GAME_" + Backup);
string LicenseFolder = Path.Combine(output, "ux0_pspemu_temp_game_PSP_LICENSE");
Status.Text = "Declaring Independance from the GAME.PSVIMG";
string BackupPath = Path.Combine(CmaDir, "PGAME", CmaAid, Backup, "game", "game.psvimg");
if(!File.Exists(BackupPath))
{
return;
}
TotalProgress.Style = ProgressBarStyle.Marquee;
Process psvimg = psvimgtools.PSVIMG_EXTRACT(CmaAid, BackupPath, output);
while (!psvimg.HasExited)
{
Application.DoEvents();
}
if(!Directory.Exists(GameFolder) || psvimg.ExitCode != 0)
{
MessageBox.Show("PSVIMG-EXTRACT.EXE FAILED!\nArguments:\n" + psvimg.StartInfo.Arguments + "\nStdOut:\n" + psvimg.StandardOutput.ReadToEnd() + "\nStdErr:\n" + psvimg.StandardError.ReadToEnd());
return;
}
Status.Text = "Declaring Independance from the LICENSE.PSVIMG";
byte[] AID = BitConverter.GetBytes(Convert.ToInt64(CmaAid, 16));
Array.Reverse(AID);
byte[] Key = CmaKeys.GenerateKey(AID);
PSVIMGStream GamePsvimg = new PSVIMGStream(File.OpenRead(BackupPath), Key);
BackupPath = Path.Combine(CmaDir, "PGAME", CmaAid, Backup, "license", "license.psvimg");
psvimg = psvimgtools.PSVIMG_EXTRACT(CmaAid, BackupPath, output);
while(!psvimg.HasExited)
if (!File.Exists(BackupPath))
{
Application.DoEvents();
}
if (!Directory.Exists(LicenseFolder) || psvimg.ExitCode != 0)
{
MessageBox.Show("PSVIMG-EXTRACT.EXE FAILED!\nArguments:\n" + psvimg.StartInfo.Arguments + "\nStdOut:\n" + psvimg.StandardOutput.ReadToEnd() + "\nStdErr:\n" + psvimg.StandardError.ReadToEnd());
return;
}
/* -- This was some debug stuff
OpenFileDialog Openpbp = new OpenFileDialog();
Openpbp.ShowDialog();
string OutputPbp = Openpbp.FileName;
PSVIMGStream LicensePsvimg = new PSVIMGStream(File.OpenRead(BackupPath), Key);
OpenFileDialog OpenRif = new OpenFileDialog();
OpenRif.ShowDialog();
string Rif = OpenRif.FileName;
*/
string Rif = Path.Combine(Application.StartupPath, "GAME.RIF");
byte[] VersionKey = pbp.GetVersionKey(OutputPbp);
PSVIMGFileStream EbootPbp = new PSVIMGFileStream(GamePsvimg, "/EBOOT.PBP");
byte[] VersionKey = pbp.GetVersionKey(EbootPbp);
string VerKey = BitConverter.ToString(VersionKey).Replace("-", "");
WriteSetting("VersionKey", VerKey);
string ContentID = pbp.GetContentId(OutputPbp);
string Rif = Path.Combine(Application.StartupPath, "GAME.RIF");
string LicensePath = Path.Combine(output, "ux0_pspemu_temp_game_PSP_LICENSE", ContentID + ".rif");
if(!File.Exists(LicensePath))
{
MessageBox.Show("Cannot find RIF", "RIF ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
File.Copy(LicensePath, Rif, true);
string ContentID = pbp.GetContentId(EbootPbp);
PSVIMGFileStream LicenseRif = new PSVIMGFileStream(LicensePsvimg, "/"+ ContentID+ ".rif");
byte[] LicenseFile = new byte[LicenseRif.Length];
LicenseRif.Read(LicenseFile, 0x00, Convert.ToInt32(LicenseRif.Length));
File.WriteAllBytes(Rif, LicenseFile);
LicenseRif.Close();
LicensePsvimg.Close();
EbootPbp.Close();
GamePsvimg.Close();
WriteSetting("RifPath", Rif);
WriteSetting("CmaDir", CmaDir);
Versionkey.Text = VerKey;
RifPath.Text = Rif;
if (Directory.Exists(output))
{
try
{
Directory.Delete(output, true);
}
catch (Exception) { }
}
Status.Text = "Progress %";
TotalProgress.Style = ProgressBarStyle.Continuous;
MessageBox.Show("KEYS HAVE BEEN EXTRACTED FROM CMA, YOU MAY NOW LIBERATE YOURSELF", "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void BrowseButton_Click(object sender, EventArgs e)

407
CHOVY/PSVIMGReader.cs Normal file
View File

@ -0,0 +1,407 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PSVIMGTOOLS
{
internal class strings
{
internal static string readTerminator(byte[] StringBytes)
{
string str = "";
foreach (byte sByte in StringBytes)
{
if (sByte != 0x00)
{
str += (char)sByte;
}
else
{
return str;
}
}
return str;
}
}
class SceDateTime
{
public UInt16 Year;
public UInt16 Month;
public UInt16 Day;
public UInt16 Hour;
public UInt16 Minute;
public UInt16 Second;
public UInt32 Microsecond;
public SceDateTime()
{
}
}
class SceIoStat
{
public UInt32 Mode;
public uint Attributes;
/** Size of the file in bytes. */
public UInt64 Size;
/** Creation time. */
public SceDateTime CreationTime;
/** Access time. */
public SceDateTime AccessTime;
/** Modification time. */
public SceDateTime ModificaionTime;
/** Device-specific data. */
public UInt32[] Private = new UInt32[6];
public SceIoStat()
{
}
};
class PsvImgTailer
{
public UInt64 Flags;
public Byte[] Padding = new Byte[1004];
public Byte[] bEnd = new Byte[12];
public String End
{
get
{
return strings.readTerminator(bEnd);
}
}
}
class PsvImgHeader
{
public UInt64 SysTime;
public UInt64 Flags;
public SceIoStat Statistics;
public Byte[] bParentPath = new Byte[256];
public UInt32 unk_16C; // set to 1
public Byte[] bPath = new Byte[256];
public Byte[] Padding = new Byte[904];
public Byte[] bEnd = new Byte[12];
public String Path
{
get
{
return strings.readTerminator(bPath);
}
}
public String End
{
get
{
return strings.readTerminator(bEnd);
}
}
public String ParentPath
{
get
{
return strings.readTerminator(bParentPath);
}
}
public PsvImgHeader()
{
}
}
class PSVIMGFileStream : Stream
{
long length = 0;
long startPos = 0;
long endPos = 0;
long position = 0;
PSVIMGStream psvStream;
public PSVIMGFileStream(PSVIMGStream psv, string Path)
{
psvStream = psv;
findFile(Path);
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override long Length
{
get
{
return length;
}
}
public override long Position
{
get
{
return position;
}
set
{
this.Seek(value, SeekOrigin.Begin);
}
}
public override void Flush()
{
psvStream.Flush();
}
private int _read(byte[] buffer, int offset, int count)
{
using (MemoryStream ms = new MemoryStream())
{
int read = 0;
while(true)
{
int remainBlock = Convert.ToInt32(psvStream.BlockRemaining - psvStream.SHA256_BLOCK_SIZE);
int remainRead = count - read;
if (remainRead < remainBlock)
{
byte[] tmp = new byte[remainRead];
psvStream.Read(tmp, 0x00, remainRead);
ms.Write(tmp,0x00,tmp.Length);
read += remainRead;
break;
}
else
{
byte[] tmp = new byte[remainBlock];
psvStream.Read(tmp, 0x00, remainBlock);
ms.Write(tmp, 0x00, tmp.Length);
psvStream.Seek(psvStream.SHA256_BLOCK_SIZE, SeekOrigin.Current);
read += Convert.ToInt32(remainBlock);
}
}
ms.Seek(0x00, SeekOrigin.Begin);
return ms.Read(buffer, offset, count);
}
}
private long _seek(long amount,SeekOrigin orig)
{
long ToSeek = 0;
long SeekAdd = 0;
long remainBlock = psvStream.BlockRemaining - psvStream.SHA256_BLOCK_SIZE;
while (true)
{
long remainSeek = amount - ToSeek;
if (remainSeek < remainBlock)
{
ToSeek += remainSeek;
break;
}
else
{
ToSeek += remainBlock;
SeekAdd += psvStream.SHA256_BLOCK_SIZE;
}
remainBlock = psvStream.PSVIMG_BLOCK_SIZE;
}
ToSeek += SeekAdd;
return psvStream.Seek(ToSeek,orig);
}
public override int Read(byte[] buffer, int offset, int count)
{
if((startPos+count) > endPos)
{
count = Convert.ToInt32(endPos);
}
return _read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if(origin == SeekOrigin.Begin)
{
if(offset <= endPos)
{
psvStream.Seek(startPos, SeekOrigin.Begin);
_seek(offset, SeekOrigin.Current);
position = offset;
return position;
}
else
{
throw new IndexOutOfRangeException("Offset is out of range of file");
}
}
else if(origin == SeekOrigin.Current)
{
if (offset <= endPos)
{
_seek(offset, SeekOrigin.Current);
position += offset;
return position;
}
else
{
throw new IndexOutOfRangeException("Offset is out of range of file");
}
}
else
{
long realOffset = endPos + offset;
if (realOffset <= endPos)
{
_seek(realOffset, SeekOrigin.Begin);
return this.Position;
}
else
{
throw new IndexOutOfRangeException("Offset is out of range of file");
}
}
}
public override void SetLength(long value)
{
throw new NotImplementedException("PSVFileStream is Read-Only");
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException("PSVFileStream is Read-Only");
}
private ushort readUInt16()
{
byte[] intBuf = new byte[0x2];
_read(intBuf, 0x00, 0x02);
return BitConverter.ToUInt16(intBuf,0);
}
private uint readUInt32()
{
byte[] intBuf = new byte[0x4];
_read(intBuf, 0x00, 0x04);
return BitConverter.ToUInt32(intBuf,0);
}
private ulong readUInt64()
{
byte[] intBuf = new byte[0x8];
_read(intBuf, 0x00, 0x08);
return BitConverter.ToUInt64(intBuf,0);
}
private SceDateTime readDatetime()
{
SceDateTime dateTime = new SceDateTime();
dateTime.Year = readUInt16();
dateTime.Month = readUInt16();
dateTime.Day = readUInt16();
dateTime.Hour = readUInt16();
dateTime.Minute = readUInt16();
dateTime.Second = readUInt16();
dateTime.Microsecond = readUInt32();
return dateTime;
}
private SceIoStat readStats()
{
SceIoStat stat = new SceIoStat();
stat.Mode = readUInt32();
stat.Attributes = readUInt32();
stat.Size = readUInt64();
stat.CreationTime = readDatetime();
stat.AccessTime = readDatetime();
stat.ModificaionTime = readDatetime();
for(int i = 0; i < stat.Private.Length; i++)
{
stat.Private[i] = readUInt32();
}
return stat;
}
private PsvImgHeader readHeader()
{
PsvImgHeader header = new PsvImgHeader();
header.SysTime = readUInt64();
header.Flags = readUInt64();
header.Statistics = readStats();
_read(header.bParentPath, 0x00, 256);
header.unk_16C = readUInt32();
_read(header.bPath, 0x00, 256);
_read(header.Padding, 0x00, 904);
_read(header.bEnd, 0x00, 12);
return header;
}
private PsvImgTailer readTailer()
{
PsvImgTailer tailer = new PsvImgTailer();
tailer.Flags = readUInt64();
_read(tailer.Padding, 0x00, 1004);
_read(tailer.bEnd, 0x00, 12);
return tailer;
}
private void findFile(string path)
{
_seek(0x00, SeekOrigin.Begin);
while (psvStream.Position < psvStream.Length)
{
PsvImgHeader header = readHeader();
long size = (long)header.Statistics.Size;
//Read Padding (if any)
int padding;
if ((size & (psvStream.PSVIMG_ENTRY_ALIGN - 1))>=1)
{
padding = Convert.ToInt32(psvStream.PSVIMG_ENTRY_ALIGN - (size & (psvStream.PSVIMG_ENTRY_ALIGN - 1)));
}
else
{
padding = 0;
}
if (header.Path == path)
{
length = size;
startPos = psvStream.Position;
endPos = startPos + length;
return;
}
else
{
_seek(size+padding, SeekOrigin.Current);
PsvImgTailer tailer = readTailer();
}
}
throw new FileNotFoundException("Cannot find file specified");
}
}
}

340
CHOVY/PSVIMGStream.cs Normal file
View File

@ -0,0 +1,340 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace PSVIMGTOOLS
{
class PSVIMGStream : Stream
{
public int AES_BLOCK_SIZE = 0x10;
public int SHA256_BLOCK_SIZE = 0x20;
public int PSVIMG_BLOCK_SIZE = 0x8000;
public int FULL_PSVIMG_SIZE = 0x8020;
public int PSVIMG_ENTRY_ALIGN = 0x400;
private Stream baseStream;
private MemoryStream blockStream;
private byte[] key;
public Stream BaseStream
{
get
{
return baseStream;
}
}
public Byte[] Key
{
get
{
return key;
}
set
{
key = value;
}
}
public long BlockNo
{
get
{
return getBlockIndex();
}
set
{
seekToBlock(value);
update();
}
}
public long BlockRemaining
{
get
{
return getRemainingBlock();
}
}
public long BlockPosition
{
get
{
return blockStream.Position;
}
set
{
blockStream.Seek(value, SeekOrigin.Begin);
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override bool CanRead
{
get
{
return true;
}
}
public override long Length
{
get
{
return baseStream.Length - AES_BLOCK_SIZE;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override long Position
{
get
{
return baseStream.Position - AES_BLOCK_SIZE;
}
set
{
this.Seek(value, SeekOrigin.Begin);
}
}
public PSVIMGStream(Stream file, byte[] KEY)
{
baseStream = file;
key = KEY;
blockStream = new MemoryStream();
baseStream.Seek(AES_BLOCK_SIZE, SeekOrigin.Begin);
update();
}
public override int Read(byte[] buffer, int offset, int count)
{
int remaining = (int)getRemainingBlock();
int read = 0;
if (count < remaining)
{
read += blockStream.Read(buffer, offset, count);
baseStream.Seek(count, SeekOrigin.Current);
}
else
{
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
update();
remaining = (int)getRemainingBlock();
int curPos = count - read;
if (curPos > remaining)
{
read += remaining;
blockStream.CopyTo(ms, remaining);
baseStream.Seek(remaining, SeekOrigin.Current);
}
else
{
read += curPos;
blockStream.CopyTo(ms, curPos);
baseStream.Seek(curPos, SeekOrigin.Current);
break;
}
}
ms.Seek(0x00, SeekOrigin.Begin);
ms.Read(buffer, offset, count);
}
}
return read;
}
public override void Flush()
{
update();
baseStream.Flush();
blockStream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
long ret = 0;
if(origin == SeekOrigin.Begin)
{
ret = baseStream.Seek(offset + AES_BLOCK_SIZE, SeekOrigin.Begin);
}
else if (origin == SeekOrigin.Current)
{
long pos = baseStream.Position;
if ((pos + offset) >= AES_BLOCK_SIZE)
{
ret = baseStream.Seek(offset, SeekOrigin.Current);
}
else
{
ret = baseStream.Seek(offset+ AES_BLOCK_SIZE, SeekOrigin.Current);
}
}
else if (origin == SeekOrigin.End)
{
long pos = baseStream.Length;
if ((pos + offset) >= AES_BLOCK_SIZE)
{
ret = baseStream.Seek(offset, SeekOrigin.End);
}
else
{
ret = baseStream.Seek(offset + AES_BLOCK_SIZE, SeekOrigin.End);
}
}
update();
return ret;
}
public override void SetLength(long value)
{
throw new NotImplementedException("PSVIMGStream is Read-Only");
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException("PSVIMGStream is Read-Only");
}
public new void Close()
{
blockStream.Close();
blockStream.Dispose();
baseStream.Close();
baseStream.Dispose();
this.Dispose();
}
private void update()
{
long offset = (this.Position % FULL_PSVIMG_SIZE);
long blockIndex = getBlockIndex();
byte[] decryptedBlock = getBlock(blockIndex);
blockStream.Seek(0x00, SeekOrigin.Begin);
blockStream.SetLength(decryptedBlock.Length);
blockStream.Write(decryptedBlock, 0x00, decryptedBlock.Length);
seekToBlock(blockIndex);
baseStream.Seek(offset, SeekOrigin.Current);
blockStream.Seek(offset, SeekOrigin.Begin);
}
private long getBlockIndex()
{
long i = 0;
long curPos = baseStream.Position;
long fullBlock;
long blockOffset;
while (true)
{
blockOffset = (i * FULL_PSVIMG_SIZE) + AES_BLOCK_SIZE;
long remaining = getRemainingBase();
if (remaining < FULL_PSVIMG_SIZE)
{
fullBlock = blockOffset + remaining;
}
else
{
fullBlock = blockOffset + FULL_PSVIMG_SIZE;
}
if ((curPos >= blockOffset) && (curPos < fullBlock))
{
break;
}
if (blockOffset > baseStream.Length)
{
break;
}
i++;
}
return i;
}
private long getRemainingBase()
{
return baseStream.Length - baseStream.Position;
}
private long getRemainingBlock()
{
return blockStream.Length - blockStream.Position;
}
private byte[] getIV(long blockindex)
{
byte[] iv = new byte[0x10];
seekToBlock(blockindex);
baseStream.Seek(baseStream.Position - AES_BLOCK_SIZE, SeekOrigin.Begin);
baseStream.Read(iv, 0x00, iv.Length);
return iv;
}
private byte[] aes_ecb_decrypt(byte[] cipherData, byte[] IV)
{
MemoryStream ms = new MemoryStream();
Aes alg = Aes.Create();
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.None;
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
private void seekToBlock(long blockIndex)
{
long blockOffset;
blockOffset = (blockIndex * FULL_PSVIMG_SIZE) + AES_BLOCK_SIZE;
if (blockOffset > baseStream.Length)
{
blockOffset = baseStream.Length;
}
baseStream.Seek(blockOffset, SeekOrigin.Begin);
}
private byte[] getBlock(long blockIndex)
{
byte[] iv = getIV(blockIndex);
long remaining = getRemainingBase();
byte[] encryptedBlock;
if (FULL_PSVIMG_SIZE < remaining)
{
encryptedBlock = new byte[FULL_PSVIMG_SIZE];
}
else
{
encryptedBlock = new byte[remaining];
}
baseStream.Read(encryptedBlock, 0x00, encryptedBlock.Length);
byte[] decryptedBlock = aes_ecb_decrypt(encryptedBlock, iv);
return decryptedBlock;
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using PSVIMGTOOLS;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
@ -58,16 +59,20 @@ namespace CHOVY
}
public unsafe static byte[] GetVersionKey(string pbpfile)
private static UInt32 readUInt32(Stream src)
{
byte[] intBuf = new byte[0x4];
src.Read(intBuf, 0x00, 0x04);
return BitConverter.ToUInt32(intBuf, 0);
}
public unsafe static byte[] GetVersionKey(Stream pbp)
{
MAC_KEY mkey;
kirk_init();
FileStream pbp = File.OpenRead(pbpfile);
BinaryReader binPBP = new BinaryReader(pbp);
pbp.Seek(0x24,SeekOrigin.Begin);
int NPUMDIMGOffest = binPBP.ReadInt32();
Int64 NPUMDIMGOffest = Convert.ToInt64(readUInt32(pbp));
pbp.Seek(NPUMDIMGOffest, SeekOrigin.Begin);
@ -92,12 +97,10 @@ namespace CHOVY
return chovy_gen(EbootFile, AID, OutSceebootpbpFile);
}
public static string GetContentId(string pbpfile)
public static string GetContentId(Stream pbp)
{
FileStream pbp = File.OpenRead(pbpfile);
BinaryReader binPBP = new BinaryReader(pbp);
pbp.Seek(0x24, SeekOrigin.Begin);
int NPUMDIMGOffest = binPBP.ReadInt32();
Int64 NPUMDIMGOffest = Convert.ToInt64(readUInt32(pbp));
pbp.Seek(NPUMDIMGOffest+0x10, SeekOrigin.Begin);
byte[] ContentId = new byte[0x24];
pbp.Read(ContentId, 0x00, 0x24);