Initial extractor automation

This commit is contained in:
Jack Andersen 2015-06-29 20:46:19 -10:00
parent ec8db1694b
commit 293b7c0ce6
13 changed files with 271 additions and 90 deletions

View File

@ -1,13 +1,10 @@
TEMPLATE = app TEMPLATE = subdirs
CONFIG += console c++11
CONFIG -= app_bundle CONFIG -= app_bundle
CONFIG -= qt CONFIG -= qt
QT = QT =
QMAKE_CXXFLAGS += -maes SUBDIRS += lib driver
QMAKE_LFLAGS += -maes driver.depends += lib
INCLUDEPATH += include
HEADERS += \ HEADERS += \
include/Util.hpp \ include/Util.hpp \
@ -19,14 +16,3 @@ HEADERS += \
include/DiscWii.hpp \ include/DiscWii.hpp \
include/aes.hpp include/aes.hpp
SOURCES += \
lib/NODLib.cpp \
lib/FileIOFILE.cpp \
lib/FileIOMEM.cpp \
lib/DiscBase.cpp \
lib/DiscGCN.cpp \
lib/DiscWii.cpp \
lib/DiscIOWBFS.cpp \
lib/DiscIOISO.cpp \
main.cpp \
lib/aes.cpp

11
driver/driver.pro Normal file
View File

@ -0,0 +1,11 @@
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
QT =
INCLUDEPATH += ../include
QMAKE_LFLAGS += -maes
LIBS += -L$$OUT_PWD/../lib -lNOD
SOURCES += main.cpp

37
driver/main.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include <string.h>
#include "NODLib.hpp"
int main(int argc, char* argv[])
{
if (argc < 2)
{
fprintf(stderr, "Usage: nodlib <image-in>\n");
return -1;
}
std::unique_ptr<NOD::DiscBase> disc = NOD::OpenDiscFromImage(argv[1]);
if (!disc)
return -1;
disc->extractToDirectory("/home/jacko/Desktop/TrilogyDump2");
const NOD::DiscBase::IPartition* dataPart = disc->getDataPartition();
if (dataPart)
{
for (const NOD::DiscBase::IPartition::Node& node : dataPart->getFSTRoot())
{
if (node.getKind() == NOD::DiscBase::IPartition::Node::NODE_FILE)
printf("FILE: %s\n", node.getName().c_str());
else if (node.getKind() == NOD::DiscBase::IPartition::Node::NODE_DIRECTORY)
{
printf("DIR: %s\n", node.getName().c_str());
for (const NOD::DiscBase::IPartition::Node& subnode : node)
printf("SUBFILE: %s\n", subnode.getName().c_str());
}
}
}
return 0;
}

View File

@ -36,17 +36,6 @@ public:
} }
}; };
struct IPartReadStream
{
virtual void seek(size_t offset, int whence=SEEK_SET)=0;
virtual size_t read(void* buf, size_t length)=0;
};
struct IPartWriteStream
{
virtual size_t write(void* buf, size_t length)=0;
};
class FSTNode class FSTNode
{ {
uint32_t typeAndNameOffset; uint32_t typeAndNameOffset;
@ -86,8 +75,8 @@ public:
size_t m_discLength; size_t m_discLength;
std::string m_name; std::string m_name;
std::vector<Node>::const_iterator m_childrenBegin; std::vector<Node>::iterator m_childrenBegin;
std::vector<Node>::const_iterator m_childrenEnd; std::vector<Node>::iterator m_childrenEnd;
public: public:
Node(const IPartition& parent, const FSTNode& node, const char* name) Node(const IPartition& parent, const FSTNode& node, const char* name)
@ -107,15 +96,15 @@ public:
} }
return m_parent.beginReadStream(m_discOffset); return m_parent.beginReadStream(m_discOffset);
} }
inline std::vector<Node>::const_iterator rawBegin() const {return m_childrenBegin;} inline std::vector<Node>::iterator rawBegin() const {return m_childrenBegin;}
inline std::vector<Node>::const_iterator rawEnd() const {return m_childrenEnd;} inline std::vector<Node>::iterator rawEnd() const {return m_childrenEnd;}
class DirectoryIterator : std::iterator<std::forward_iterator_tag, const Node> class DirectoryIterator : std::iterator<std::forward_iterator_tag, Node>
{ {
friend class Node; friend class Node;
std::vector<Node>::const_iterator m_it; std::vector<Node>::iterator m_it;
DirectoryIterator(std::vector<Node>::const_iterator&& it) DirectoryIterator(const std::vector<Node>::iterator& it)
: m_it(std::move(it)) {} : m_it(it) {}
public: public:
inline bool operator!=(const DirectoryIterator& other) {return m_it != other.m_it;} inline bool operator!=(const DirectoryIterator& other) {return m_it != other.m_it;}
inline DirectoryIterator& operator++() inline DirectoryIterator& operator++()
@ -126,10 +115,12 @@ public:
++m_it; ++m_it;
return *this; return *this;
} }
inline const Node& operator*() {return *m_it;} inline Node& operator*() {return *m_it;}
}; };
inline DirectoryIterator begin() const {return DirectoryIterator(rawBegin());} inline DirectoryIterator begin() const {return DirectoryIterator(m_childrenBegin);}
inline DirectoryIterator end() const {return DirectoryIterator(rawEnd());} inline DirectoryIterator end() const {return DirectoryIterator(m_childrenEnd);}
void extractToDirectory(const std::string& basePath, bool force=false);
}; };
protected: protected:
uint32_t m_dolOff; uint32_t m_dolOff;
@ -148,6 +139,9 @@ public:
inline Kind getKind() const {return m_kind;} inline Kind getKind() const {return m_kind;}
virtual std::unique_ptr<IPartReadStream> beginReadStream(size_t offset=0) const=0; virtual std::unique_ptr<IPartReadStream> beginReadStream(size_t offset=0) const=0;
inline const Node& getFSTRoot() const {return m_nodes[0];} inline const Node& getFSTRoot() const {return m_nodes[0];}
inline Node& getFSTRoot() {return m_nodes[0];}
inline void extractToDirectory(const std::string& path, bool force=false)
{m_nodes[0].extractToDirectory(path, force);}
}; };
protected: protected:
@ -173,6 +167,11 @@ public:
return part.get(); return part.get();
return nullptr; return nullptr;
} }
inline void extractToDirectory(const std::string& path, bool force=false)
{
for (std::unique_ptr<IPartition>& part : m_partitions)
part->extractToDirectory(path, force);
}
}; };
} }

View File

@ -27,6 +27,17 @@ public:
virtual std::unique_ptr<IWriteStream> beginWriteStream(size_t offset=0) const=0; virtual std::unique_ptr<IWriteStream> beginWriteStream(size_t offset=0) const=0;
}; };
struct IPartReadStream
{
virtual void seek(size_t offset, int whence=SEEK_SET)=0;
virtual size_t read(void* buf, size_t length)=0;
};
struct IPartWriteStream
{
virtual size_t write(void* buf, size_t length)=0;
};
} }
#endif // __NOD_IDISC_IO__ #endif // __NOD_IDISC_IO__

View File

@ -17,18 +17,21 @@ public:
struct IWriteStream struct IWriteStream
{ {
virtual ~IWriteStream() {} virtual ~IWriteStream() {}
virtual size_t copyFromDisc(IDiscIO::IReadStream& discio, size_t length)=0; virtual size_t copyFromDisc(struct IPartReadStream& discio, size_t length)=0;
}; };
virtual std::unique_ptr<IWriteStream> beginWriteStream(); virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0;
struct IReadStream struct IReadStream
{ {
virtual ~IReadStream() {} virtual ~IReadStream() {}
virtual size_t copyToDisc(IDiscIO::IWriteStream& discio, size_t length)=0; virtual size_t copyToDisc(struct IPartWriteStream& discio, size_t length)=0;
}; };
virtual std::unique_ptr<IReadStream> beginReadStream(); virtual std::unique_ptr<IReadStream> beginReadStream() const=0;
}; };
std::unique_ptr<IFileIO> NewFileIO(const std::string& path);
std::unique_ptr<IFileIO> NewMemIO(void* buf, size_t size);
} }
#endif // __NOD_IFILE_IO__ #endif // __NOD_IFILE_IO__

View File

@ -3,7 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <memory> #include <memory>
namespace NOD namespace NOD

View File

@ -1,4 +1,6 @@
#include <sys/stat.h>
#include "DiscBase.hpp" #include "DiscBase.hpp"
#include "IFileIO.hpp"
namespace NOD namespace NOD
{ {
@ -26,7 +28,7 @@ void DiscBase::IPartition::parseFST(IPartReadStream& s)
for (uint32_t n=0 ; n<nodeCount ; ++n) for (uint32_t n=0 ; n<nodeCount ; ++n)
{ {
const FSTNode& node = nodes[n]; const FSTNode& node = nodes[n];
m_nodes.emplace_back(*this, node, n ? names + node.getNameOffset() : "<root>"); m_nodes.emplace_back(*this, node, n ? names + node.getNameOffset() : "");
} }
/* Setup dir-child iterators */ /* Setup dir-child iterators */
@ -43,4 +45,27 @@ void DiscBase::IPartition::parseFST(IPartReadStream& s)
} }
} }
void DiscBase::IPartition::Node::extractToDirectory(const std::string& basePath, bool force)
{
std::string path = basePath + "/" + getName();
if (m_kind == NODE_DIRECTORY)
{
if (mkdir(path.c_str(), 0755) && errno != EEXIST)
throw std::runtime_error("unable to mkdir '" + path + "'");
for (DiscBase::IPartition::Node& subnode : *this)
subnode.extractToDirectory(path);
}
else if (m_kind == NODE_FILE)
{
struct stat theStat;
if (force || stat(path.c_str(), &theStat))
{
m_hddFile = NewFileIO(path);
std::unique_ptr<IPartReadStream> rs = beginReadStream();
std::unique_ptr<IFileIO::IWriteStream> ws = m_hddFile->beginWriteStream();
ws->copyFromDisc(*rs.get(), m_discLength);
}
}
}
} }

View File

@ -10,7 +10,7 @@ public:
: IPartition(parent, kind, offset) : IPartition(parent, kind, offset)
{ {
/* GCN-specific header reads */ /* GCN-specific header reads */
std::unique_ptr<DiscBase::IPartReadStream> s = beginReadStream(0x420); std::unique_ptr<IPartReadStream> s = beginReadStream(0x420);
uint32_t vals[3]; uint32_t vals[3];
s->read(vals, 12); s->read(vals, 12);
m_dolOff = SBig(vals[0]); m_dolOff = SBig(vals[0]);
@ -21,7 +21,7 @@ public:
parseFST(*s.get()); parseFST(*s.get());
} }
class PartReadStream : public DiscBase::IPartReadStream class PartReadStream : public IPartReadStream
{ {
const PartitionGCN& m_parent; const PartitionGCN& m_parent;
std::unique_ptr<IDiscIO::IReadStream> m_dio; std::unique_ptr<IDiscIO::IReadStream> m_dio;
@ -36,9 +36,9 @@ public:
{return m_dio->read(buf, length);} {return m_dio->read(buf, length);}
}; };
std::unique_ptr<DiscBase::IPartReadStream> beginReadStream(size_t offset) const std::unique_ptr<IPartReadStream> beginReadStream(size_t offset) const
{ {
return std::unique_ptr<DiscBase::IPartReadStream>(new PartReadStream(*this, offset)); return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, offset));
} }
}; };

View File

@ -1,4 +1,5 @@
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "DiscWii.hpp" #include "DiscWii.hpp"
#include "aes.hpp" #include "aes.hpp"
@ -12,6 +13,19 @@ static const uint8_t COMMON_KEY[] = {0xeb, 0xe4, 0x2a, 0x22,
class PartitionWii : public DiscBase::IPartition class PartitionWii : public DiscBase::IPartition
{ {
enum SigType : uint32_t
{
SRSA_4096 = 0x00010000,
SRSA_2048 = 0x00010001,
SELIPTICAL_CURVE = 0x00010002
};
enum KeyType : uint32_t
{
KRSA_4096 = 0x00000000,
KRSA_2048 = 0x00000001
};
struct Ticket struct Ticket
{ {
uint32_t sigType; uint32_t sigType;
@ -57,7 +71,7 @@ class PartitionWii : public DiscBase::IPartition
struct TMD struct TMD
{ {
uint32_t sigType; SigType sigType;
char sig[256]; char sig[256];
char padding[60]; char padding[60];
char sigIssuer[64]; char sigIssuer[64];
@ -68,7 +82,7 @@ class PartitionWii : public DiscBase::IPartition
uint32_t iosIdMajor; uint32_t iosIdMajor;
uint32_t iosIdMinor; uint32_t iosIdMinor;
uint32_t titleIdMajor; uint32_t titleIdMajor;
uint32_t titleIdMinor; char titleIdMinor[4];
uint32_t titleType; uint32_t titleType;
uint16_t groupId; uint16_t groupId;
char padding2[62]; char padding2[62];
@ -100,11 +114,10 @@ class PartitionWii : public DiscBase::IPartition
void read(IDiscIO::IReadStream& s) void read(IDiscIO::IReadStream& s)
{ {
s.read(this, 484); s.read(this, 484);
sigType = SBig(sigType); sigType = (SigType)SBig(sigType);
iosIdMajor = SBig(iosIdMajor); iosIdMajor = SBig(iosIdMajor);
iosIdMinor = SBig(iosIdMinor); iosIdMinor = SBig(iosIdMinor);
titleIdMajor = SBig(titleIdMajor); titleIdMajor = SBig(titleIdMajor);
titleIdMinor = SBig(titleIdMinor);
titleType = SBig(titleType); titleType = SBig(titleType);
groupId = SBig(groupId); groupId = SBig(groupId);
accessFlags = SBig(accessFlags); accessFlags = SBig(accessFlags);
@ -121,10 +134,10 @@ class PartitionWii : public DiscBase::IPartition
struct Certificate struct Certificate
{ {
uint32_t sigType; SigType sigType;
char sig[512]; char sig[512];
char issuer[64]; char issuer[64];
uint32_t keyType; KeyType keyType;
char subject[64]; char subject[64];
char key[512]; char key[512];
uint32_t modulus; uint32_t modulus;
@ -133,22 +146,22 @@ class PartitionWii : public DiscBase::IPartition
void read(IDiscIO::IReadStream& s) void read(IDiscIO::IReadStream& s)
{ {
s.read(&sigType, 4); s.read(&sigType, 4);
sigType = SBig(sigType); sigType = (SigType)SBig(sigType);
if (sigType == 0x00010000) if (sigType == SRSA_4096)
s.read(sig, 512); s.read(sig, 512);
else if (sigType == 0x00010001) else if (sigType == SRSA_2048)
s.read(sig, 256); s.read(sig, 256);
else if (sigType == 0x00010002) else if (sigType == SELIPTICAL_CURVE)
s.read(sig, 64); s.read(sig, 64);
s.seek(60, SEEK_CUR); s.seek(60, SEEK_CUR);
s.read(issuer, 64); s.read(issuer, 64);
s.read(&keyType, 4); s.read(&keyType, 4);
s.read(subject, 64); s.read(subject, 64);
keyType = SBig(keyType); keyType = (KeyType)SBig(keyType);
if (keyType == 0x00000000) if (keyType == KRSA_4096)
s.read(key, 512); s.read(key, 512);
else if (keyType == 0x00000001) else if (keyType == KRSA_2048)
s.read(key, 256); s.read(key, 256);
s.read(&modulus, 8); s.read(&modulus, 8);
@ -214,7 +227,7 @@ public:
aes->decrypt(iv, m_ticket.encKey, m_decKey, 16); aes->decrypt(iv, m_ticket.encKey, m_decKey, 16);
/* Wii-specific header reads (now using title key to decrypt) */ /* Wii-specific header reads (now using title key to decrypt) */
std::unique_ptr<DiscBase::IPartReadStream> ds = beginReadStream(0x420); std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420);
uint32_t vals[3]; uint32_t vals[3];
ds->read(vals, 12); ds->read(vals, 12);
m_dolOff = SBig(vals[0]) << 2; m_dolOff = SBig(vals[0]) << 2;
@ -225,7 +238,7 @@ public:
parseFST(*ds.get()); parseFST(*ds.get());
} }
class PartReadStream : public DiscBase::IPartReadStream class PartReadStream : public IPartReadStream
{ {
std::unique_ptr<IAES> m_aes; std::unique_ptr<IAES> m_aes;
const PartitionWii& m_parent; const PartitionWii& m_parent;
@ -233,6 +246,7 @@ public:
size_t m_offset; size_t m_offset;
std::unique_ptr<IDiscIO::IReadStream> m_dio; std::unique_ptr<IDiscIO::IReadStream> m_dio;
size_t m_curBlock = SIZE_MAX;
uint8_t m_encBuf[0x8000]; uint8_t m_encBuf[0x8000];
uint8_t m_decBuf[0x7c00]; uint8_t m_decBuf[0x7c00];
@ -248,6 +262,8 @@ public:
m_aes->setKey(parent.m_decKey); m_aes->setKey(parent.m_decKey);
size_t block = m_offset / 0x7c00; size_t block = m_offset / 0x7c00;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000); m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
decryptBlock();
m_curBlock = block;
} }
void seek(size_t offset, int whence) void seek(size_t offset, int whence)
{ {
@ -259,16 +275,26 @@ public:
return; return;
size_t block = m_offset / 0x7c00; size_t block = m_offset / 0x7c00;
m_dio->seek(m_baseOffset + block * 0x8000); m_dio->seek(m_baseOffset + block * 0x8000);
if (block != m_curBlock)
{
decryptBlock();
m_curBlock = block;
}
} }
size_t read(void* buf, size_t length) size_t read(void* buf, size_t length)
{ {
size_t block = m_offset / 0x7c00;
size_t cacheOffset = m_offset % 0x7c00; size_t cacheOffset = m_offset % 0x7c00;
size_t cacheSize; size_t cacheSize;
uint8_t* dst = (uint8_t*)buf; uint8_t* dst = (uint8_t*)buf;
while (length) while (length)
{ {
decryptBlock(); if (block != m_curBlock)
{
decryptBlock();
m_curBlock = block;
}
cacheSize = length; cacheSize = length;
if (cacheSize + cacheOffset > 0x7c00) if (cacheSize + cacheOffset > 0x7c00)
@ -278,6 +304,7 @@ public:
dst += cacheSize; dst += cacheSize;
length -= cacheSize; length -= cacheSize;
cacheOffset = 0; cacheOffset = 0;
++block;
} }
m_offset += length; m_offset += length;
@ -285,9 +312,9 @@ public:
} }
}; };
std::unique_ptr<DiscBase::IPartReadStream> beginReadStream(size_t offset) const std::unique_ptr<IPartReadStream> beginReadStream(size_t offset) const
{ {
return std::unique_ptr<DiscBase::IPartReadStream>(new PartReadStream(*this, m_dataOff, offset)); return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, m_dataOff, offset));
} }
}; };

View File

@ -1,21 +1,99 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdexcept> #include <stdexcept>
#include "IFileIO.hpp" #include "IFileIO.hpp"
/* Macros for min/max */
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
namespace NOD namespace NOD
{ {
class FileIOFILE : public IFileIO class FileIOFILE : public IFileIO
{ {
const char* filepath; std::string m_path;
public: public:
FileIOFILE(const char* path) FileIOFILE(const std::string& path)
: filepath(path) : m_path(path) {}
size_t size()
{ {
struct stat theStat; FILE* fp = fopen(m_path.c_str(), "rb");
if (stat(path, &theStat)) if (!fp)
throw std::runtime_error("unable to "); return 0;
fseek(fp, 0, SEEK_END);
size_t result = ftell(fp);
fclose(fp);
return result;
} }
struct WriteStream : public IFileIO::IWriteStream
{
FILE* fp;
uint8_t buf[0x7c00];
WriteStream(const std::string& path)
{
fp = fopen(path.c_str(), "wb");
if (!fp)
throw std::runtime_error("unable to open '" + path + "' for writing");
}
~WriteStream() {fclose(fp);}
size_t copyFromDisc(IPartReadStream& discio, size_t length)
{
size_t read = 0;
while (length)
{
size_t thisSz = MIN(0x7c00, length);
size_t readSz = discio.read(buf, thisSz);
if (thisSz != readSz)
throw std::runtime_error("unable to read enough from disc");
if (fwrite(buf, 1, readSz, fp) != readSz)
throw std::runtime_error("unable to write in file");
length -= thisSz;
read += thisSz;
}
return read;
}
};
std::unique_ptr<IWriteStream> beginWriteStream() const
{return std::unique_ptr<IWriteStream>(new WriteStream(m_path));}
struct ReadStream : public IFileIO::IReadStream
{
FILE* fp;
uint8_t buf[0x7c00];
ReadStream(const std::string& path)
{
fp = fopen(path.c_str(), "rb");
if (!fp)
throw std::runtime_error("unable to open '" + path + "' for reading");
}
~ReadStream() {fclose(fp);}
size_t copyToDisc(IPartWriteStream& discio, size_t length)
{
size_t written = 0;
while (length)
{
size_t thisSz = MIN(0x7c00, length);
if (fread(buf, 1, thisSz, fp) != thisSz)
throw std::runtime_error("unable to read enough from file");
if (discio.write(buf, thisSz) != thisSz)
throw std::runtime_error("unable to write enough to disc");
length -= thisSz;
written += thisSz;
}
return written;
}
};
std::unique_ptr<IReadStream> beginReadStream() const
{return std::unique_ptr<IReadStream>(new ReadStream(m_path));}
}; };
std::unique_ptr<IFileIO> NewFileIO(const std::string& path)
{
return std::unique_ptr<IFileIO>(new FileIOFILE(path));
}
} }

View File

@ -1,16 +1,5 @@
/* Rijndael Block Cipher - aes.c
Written by Mike Scott 21st April 1999
mike@compapp.dcu.ie
Permission for free direct or derivative use is granted subject
to compliance with any conditions that the originators of the
algorithm place on its exploitation.
*/
#include "aes.hpp" #include "aes.hpp"
#include <stdio.h> #include <stdio.h>
//#include <stdlib.h>
#include <string.h> #include <string.h>
#include <cpuid.h> #include <cpuid.h>
@ -605,8 +594,6 @@ public:
#endif #endif
static int HAS_AES_NI = -1; static int HAS_AES_NI = -1;
std::unique_ptr<IAES> NewAES() std::unique_ptr<IAES> NewAES()
{ {
#if __AES__ #if __AES__
@ -614,10 +601,7 @@ std::unique_ptr<IAES> NewAES()
{ {
unsigned int a,b,c,d; unsigned int a,b,c,d;
__cpuid(1, a,b,c,d); __cpuid(1, a,b,c,d);
if (c & 0x2000000) HAS_AES_NI = ((c & 0x2000000) != 0);
HAS_AES_NI = 1;
else
HAS_AES_NI = 0;
} }
if (HAS_AES_NI) if (HAS_AES_NI)
return std::unique_ptr<IAES>(new NiAES); return std::unique_ptr<IAES>(new NiAES);

21
lib/lib.pro Normal file
View File

@ -0,0 +1,21 @@
TEMPLATE = lib
CONFIG += staticlib c++11
CONFIG -= app_bundle
CONFIG -= qt
QT =
TARGET = NOD
QMAKE_CXXFLAGS += -maes
INCLUDEPATH += ../include
SOURCES += \
NODLib.cpp \
FileIOFILE.cpp \
FileIOMEM.cpp \
DiscBase.cpp \
DiscGCN.cpp \
DiscWii.cpp \
DiscIOWBFS.cpp \
DiscIOISO.cpp \
aes.cpp