-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d741630
commit 408d0e6
Showing
13 changed files
with
425 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,3 +46,4 @@ gradle-local.properties | |
.idea/ | ||
lib/ | ||
*.cxi | ||
*.cia |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
distributionBase=GRADLE_USER_HOME | ||
distributionPath=wrapper/dists | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip | ||
zipStoreBase=GRADLE_USER_HOME | ||
zipStorePath=wrapper/dists | ||
zipStorePath=wrapper/dists |
96 changes: 96 additions & 0 deletions
96
src/main/kotlin/com/martmists/ctr/loader/filesystem/CIAFileSystem.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package com.martmists.ctr.loader.filesystem | ||
|
||
import com.martmists.ctr.ext.* | ||
import com.martmists.ctr.loader.format.* | ||
import com.martmists.ctr.reader.Reader | ||
import ghidra.app.util.bin.ByteProvider | ||
import ghidra.formats.gfilesystem.* | ||
import ghidra.formats.gfilesystem.annotations.FileSystemInfo | ||
import ghidra.formats.gfilesystem.fileinfo.FileAttributes | ||
import ghidra.util.exception.CancelledException | ||
import ghidra.util.task.TaskMonitor | ||
import java.io.ByteArrayInputStream | ||
import java.io.File | ||
import java.io.FileInputStream | ||
import java.io.IOException | ||
import java.io.InputStream | ||
import javax.crypto.Cipher | ||
import javax.crypto.CipherInputStream | ||
import kotlin.experimental.and | ||
import kotlin.math.min | ||
|
||
|
||
@FileSystemInfo( | ||
type = "cia", | ||
description = "CIA Container", | ||
factory = CIAFileSystemFactory::class, | ||
priority = FileSystemInfo.PRIORITY_HIGH, | ||
) | ||
class CIAFileSystem(fsFSRL: FSRLRoot, provider: ByteProvider, fsService: FileSystemService) : MountableGFileSystem by CXIFileSystem(fsFSRL, CIAByteProvider(provider), fsService) { | ||
class CIAByteProvider(private val provider: ByteProvider) : ByteProvider by provider { | ||
private var startOffset: Long | ||
|
||
init { | ||
val source = provider.getInputStream(0) | ||
source.reader { | ||
val header = read<CIAHeader>() | ||
align(64) | ||
var pos = tell() | ||
val caCert = Certificate.parse(this) | ||
val ticketCert = Certificate.parse(this) | ||
val tmdCert = Certificate.parse(this) | ||
require(tell() - pos == header.certificateChainSize.toLong()) { "Certificate chain size mismatch; expected ${header.certificateChainSize}, got ${tell() - pos}" } | ||
align(64) | ||
pos = tell() | ||
val ticket = Ticket.parse(this) | ||
require(tell() - pos == header.ticketSize.toLong()) { "Ticket size mismatch; expected ${header.ticketSize}, got ${tell() - pos}" } | ||
align(64) | ||
pos = tell() | ||
val tmd = readBytes(header.tmdSize) | ||
require(tell() - pos == header.tmdSize.toLong()) { "TMD size mismatch; expected ${header.tmdSize}, got ${tell() - pos}" } | ||
align(64) | ||
|
||
// TODO: Verify in TMD that there is no encryption | ||
// TODO: Add support for multiple NCCH containers in CIA | ||
|
||
startOffset = tell() | ||
} | ||
} | ||
|
||
override fun getInputStream(index: Long): InputStream { | ||
return OffsetInputStream(provider, startOffset + index) | ||
} | ||
} | ||
|
||
class OffsetInputStream(private val provider: ByteProvider, private val offset: Long) : InputStream() { | ||
private var mark = 0L | ||
private var currentPos = 0L | ||
private var stream = provider.getInputStream(offset) | ||
|
||
override fun markSupported() = true | ||
|
||
override fun mark(readlimit: Int) { | ||
mark = currentPos + readlimit | ||
} | ||
|
||
override fun reset() { | ||
stream.close() | ||
stream = provider.getInputStream(offset + mark) | ||
currentPos = mark | ||
} | ||
|
||
override fun read() = stream.read().also { currentPos++ } | ||
override fun read(b: ByteArray) = read(b, 0, b.size).also { currentPos += it } | ||
override fun read(b: ByteArray, off: Int, len: Int) = stream.read(b, off, len).also { currentPos += it } | ||
override fun readNBytes(len: Int) = stream.readNBytes(len).also { currentPos += it.size } | ||
override fun readNBytes(b: ByteArray?, off: Int, len: Int) = stream.readNBytes(b, off, len).also { currentPos += it } | ||
override fun skip(n: Long) = stream.skip(n).also { currentPos += n } | ||
override fun skipNBytes(n: Long) = stream.skipNBytes(n).also { currentPos += n } | ||
override fun readAllBytes() = stream.readAllBytes().also { currentPos += it.size } | ||
override fun available() = stream.available() | ||
|
||
override fun close() { | ||
stream.close() | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/kotlin/com/martmists/ctr/loader/filesystem/CIAFileSystemFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.martmists.ctr.loader.filesystem | ||
|
||
import ghidra.app.util.bin.ByteProvider | ||
import ghidra.formats.gfilesystem.FSRLRoot | ||
import ghidra.formats.gfilesystem.FSUtilities | ||
import ghidra.formats.gfilesystem.FileSystemService | ||
import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider | ||
import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider | ||
import ghidra.util.task.TaskMonitor | ||
import java.util.* | ||
|
||
|
||
class CIAFileSystemFactory : GFileSystemFactoryByteProvider<CIAFileSystem>, GFileSystemProbeByteProvider { | ||
override fun create(targetFSRL: FSRLRoot, byteProvider: ByteProvider, fsService: FileSystemService, monitor: TaskMonitor): CIAFileSystem { | ||
val fs = CIAFileSystem(targetFSRL, byteProvider, fsService) | ||
fs.mount(monitor) | ||
return fs | ||
} | ||
|
||
override fun probe(provider: ByteProvider, fsService: FileSystemService, taskMonitor: TaskMonitor): Boolean { | ||
val filename = provider.fsrl.name | ||
var ext: String = FSUtilities.getExtension(filename, 1) ?: return false | ||
ext = ext.lowercase(Locale.getDefault()) | ||
return ext == ".cia" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/kotlin/com/martmists/ctr/loader/filesystem/MountableGFileSystem.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.martmists.ctr.loader.filesystem | ||
|
||
import ghidra.formats.gfilesystem.GFileSystem | ||
import ghidra.util.task.TaskMonitor | ||
|
||
interface MountableGFileSystem : GFileSystem { | ||
/** | ||
* Mounts (opens) the file system. | ||
* | ||
* @param monitor A cancellable task monitor. | ||
*/ | ||
fun mount(monitor: TaskMonitor) | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/kotlin/com/martmists/ctr/loader/format/CIAHeader.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.martmists.ctr.loader.format | ||
|
||
data class CIAHeader( | ||
val archiveHeaderSize: Int, | ||
val type: Short, | ||
val version: Short, | ||
val certificateChainSize: Int, | ||
val ticketSize: Int, | ||
val tmdSize: Int, | ||
val metaSize: Int, | ||
val contentSize: Long, | ||
val contentIndex_8192: ByteArray, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.martmists.ctr.loader.format | ||
|
||
data class CIAMeta( | ||
val dependencyModuleList_384: ByteArray, | ||
val reserved1_384: ByteArray, | ||
val coreVersion: Int, | ||
val reserved2_252: ByteArray, | ||
val iconData_14016: ByteArray, | ||
) |
68 changes: 68 additions & 0 deletions
68
src/main/kotlin/com/martmists/ctr/loader/format/Certificate.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.martmists.ctr.loader.format | ||
|
||
import com.martmists.ctr.reader.Reader | ||
|
||
interface PublicKey | ||
|
||
data class RSAPublicKey( | ||
val modulus: ByteArray, | ||
val exponent: Int, | ||
) : PublicKey | ||
|
||
data class ECCPublicKey( | ||
val key: ByteArray, | ||
) : PublicKey | ||
|
||
data class Certificate( | ||
val signatureType: Int, | ||
val signature: ByteArray, | ||
val issuer: ByteArray, | ||
val keyType: Int, | ||
val name: ByteArray, | ||
val expirationTime: Int, | ||
val pubKey: PublicKey, | ||
) { | ||
companion object { | ||
fun parse(reader: Reader): Certificate { | ||
return reader.withEndian(false) { | ||
val signatureType = read<Int>() | ||
val signature = when (signatureType) { | ||
0x010000, 0x010003 -> readBytes(0x200).also { skip(0x3c) } | ||
0x010001, 0x010004 -> readBytes(0x100).also { skip(0x3c) } | ||
0x010002, 0x010005 -> readBytes(0x3c).also { skip(0x40) } | ||
else -> throw Exception("Unknown signature type: $signatureType") | ||
} | ||
val issuer = readBytes(0x40) | ||
val keyType = read<Int>() | ||
val name = readBytes(0x40) | ||
val expirationTime = read<Int>() | ||
val pubkey = when (keyType) { | ||
0 -> RSAPublicKey( | ||
modulus = readBytes(0x200), | ||
exponent = read<Int>(), | ||
).also { skip(0x34) } | ||
|
||
1 -> RSAPublicKey( | ||
modulus = readBytes(0x100), | ||
exponent = read<Int>(), | ||
).also { skip(0x34) } | ||
|
||
2 -> ECCPublicKey( | ||
key = readBytes(0x3c), | ||
).also { skip(0x3c) } | ||
|
||
else -> throw Exception("Unknown key type: $keyType") | ||
} | ||
Certificate( | ||
signatureType, | ||
signature, | ||
issuer, | ||
keyType, | ||
name, | ||
expirationTime, | ||
pubkey, | ||
) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.