rpcsx/rpcs3/Crypto/unself.cpp
Robbie 6cfb184b1e RPCS3 QT (#2645)
* Fix windows build.  I made sure to do everything with a win32 prefix to not effect linux build.

* Make the window resizable instead of fixed in the corner.

* Ignore moc files and things in the debug/release folder.  I might also ignore rpcs3qt.vcxproj and its filters as they're autogenerated by importing the qt project file.  But, this helps clean out clutter for now.

* Add cmake.  This doesn't interact with the rest of rpcs3 nor the main cmake file.  That's the next thing I'm doing. I'll probably need to modify them so it'll take me time to figure out. But, this will build rpcs3qt on linux and build as is with using qt.

* The build works. I'd like to thank my friends, Google and Stackoverflow.

Setted up by importing rpcs3Qt project using Qt's visual studio plugin.

* Cleanup.  Remove all the stuff in the rpcs3qt folder as its incorporated elsewhere. Remove the rpcs3qt project file as its now built into the solution and cmake doesn't care about pro files.

* Update readme to reflect getting Qt.

* Remove wxwidgets as submodule and add zlib instead. Wxwidgets was our old way of having zlib. I also added build dependencies to rpcs3qt so you should no longer get link errors on the first clean rebuild.

* Add rpcs3_version, few GUI tweaks

* Set defaultSize to 70% of screen size

* Add the view menu (#3)

* Added the view menu with the corresponding elements. Now, the debugger/log are hidden by default. The view menu has a checkbox which you click to show/hide the dock widgets.

* Make log visible by default

* Improve UI by making it into a checkbox that's easier to use.

* fix qt build for vs2017 (seems to work fine in 2015 with plugin but needs testing by other users)

* updated readme for qt

* update appveyor for qt
- cleaned formatting for the post build command

* fix build (#6)

* fix build legit this time i promise

* [Ready] Gamepadsettings (#4)

* WIP Gamepadsettings
pushbutton Eventhandling missing

* GamepadSettings should work except for cfg Init
Some KeyInputs are missing

* Update padsettingsdialog.h

* Update padsettingsdialog.cpp (#5)

* Update padsettingsdialog.cpp

removed silly tabs

* Update padsettingsdialog.cpp

* GetKeyCode simplified

* rename pad settings to keyboard settings o.O

* rename keyboard setting to input settings

* Remvoed the QT_UI defines.

* Readded new line at end of file. Replaced define in padsettings with constant.

* GUI fixes (Settings)

* Stub the logger UI. Nothing special besides a simple stub.

* Unstub the log. I haven't tested TTY but it should work.

Only thing to do, but this is in general, is add persistent settings.

* Minor refactoring to simplify code.

* Fix image loading. I'm 90% sure it works because it loads the path as expected and that's the same format I used in my gamelist implementation for the images.

* Made game lists much more functional than it was.

* mainwindow

* gamelist

* Please forgive me for I have lambdaed.

Added the ability to toggle showing columns via a context menu.

* Fix GameList further

* sort by name on init fixed

* Created the baseline refactoring. I'm going to start working on the callbacks now.  May need to implement other classes in the process. Fun stuff, I know.

* adds InstallPkg (tested) and InstallPup (should work but makes unknown shenanigans) implementation
adds RefreshGameList
obliterates 10sec Refresh

* messages

* Rpcs3 gs frame (#16)

* Messing with project settings try to get trails of cold steel to boot.bluh

Definitely one change is needed in linker settings for RPCS3 to not crash immediately.

Can't even see how horribly botched my implementation of GSFrame is because we aren't booting lol. Something is gone awry with elf.

* remove random ! not that it matters much right now

* minor additions

* "Working" with debug mode though you have to ignore an assert reached from Qt. Qt is upset that the rsx thread is calling stuff on the UI thread despite not owning it.  However, I can't do a thing to change that atm. (The fix would be to do what the TODO says in System.cpp-- making gsframe and stuff get initialized via system call)

Crashes due to needing pad callback to be done.

* With this build in debug mode, Trails of Cold steel will get FPS. (caveat. You have to ignore when Qt throws a debug assert lol)

* Fix release mode.  Fix the Qt debug assert by using ancient occault rituals.  I want to be able to remove the blocking connects but it won't work right now without it.  It isn't perfect but it's good enough for now IMO.

* Add enters to the end of files.

* Removing target and setting source of events to be the application instead of the main window. The main window isn't the game window, and I don't really know what widget will be targetted for the game event.  Works, though, it's admittedly probably not optimal by ANY means.

* Fix comment.

* Fix libpng wit zlib.

* Move Qt GUI into RPCS3Qt. (#17)

Restore wx GUI.

* fix install-progressdialogs randomly not showing

* install-progressdialog cosmetics

* add stylesheet file loading

* apply request

* Add stylesheet to git ignore.

* XInput..

* Joystick...

* Rpcs3 qt small fixes (#20)

* Small fixes.  Have emulator stop when x button is pressed on game window.  Have emulator/application stop when the main window is closed.

* If I forget another new line ending for a file.............................................

* Add CgDisasm (#21)

* fix install-progressdialogs randomly not showing

* install-progressdialog cosmetics

* add stylesheet file loading

* apply request

* add CgDisasm
add code to disable contextmenu options
fix gamelist issue

* missing proj changes

* Add ability to open stylesheets from menu.

* Mega searcher (#23)

* add MemoryStringSearcher
set minimum Sizes for mainwindow and CgDisasm

* minor fixes

* Since the system.cpp callbacks for emulator state were unused, I removed them.  Then, I replaced them with callbacks for the Gui.

* added stylesheet options
setfocus on settings fixed
newline added

* added signals and slots for EmuRun and EmuStop

* update ui

update ui now works
added callback onReady
added EnableMenues
added ps3 commands

* added restart logic to menu

* newline

* event header removed

* Added graphic settings class. (#26)

* Added graphic settings class. First thing is to have the dock widgets and window size/location be stateful.  Minor bug with debugger frame changing size on hide/show on default setup on second load. But, otherwise, fine. Also, the GUI doesn't update to accomodate the statefulness of the widgets.  But, that'll come in time as I update this class.

* Add view debugger, logger, gamelist to settings and synchronize them.

* Separate initializing actions from connects

* Add invisible fullscreen cursor and double click event.

* Add the UI log settings.

* Add MemoryViewer (#30)

* Add Memoryviewer
Image Button crashes/not fully implemented
focus on some button annoying

minor changes for question dialogs

* GuiSettings Refactoring (#31)

* Add settings for columns shown and which one is saved

* I accidentally refactored the settings class.  Added ability to reset to default GUI.  Added statefulness to column widths.

* add gui tab

* Fix logging at startup.

* Preset settings.I think I ironed out MOST of the glitches. Will work on the rest of it soon. Should be a lot simpler as I won't have to use the so-called meta settings. Also, renamed all settings methods to CapitalCase.

* Removed dock widget controls.

* Added style sheets. Removed the option from the menu.

* Rewrite to use folder design. Much simpler! Yay! Simpler. Better, right?

* It's remarkable how tricky this is.

* Added convenience button to open up the settings folder in explorer

* Add newlines at end of file

* simplified logic. Fixed a bug.. hopefully not more bugs

* Fix the undocumented feature

* Make the dialog big enough to have entire text on title shown. If talkashie changes the font to size 1203482 I don't care lol

* Make warning messagebox instead of changing the title of the dialog.

* marking...

* Hcorion suggested changes.

* [WIP] autopause (#32)

* autopause added
needs fixing
headers do not show text

* fix compile stuff

* Add MsgDialog + edge widgets (#33)

* Add MsgDialog
needs magic

* add "Debugger" Buttons to menubar

* Adapt ds4 changes. I'm not sure if they work as I don't have a compatible controller.  But, at the same time, it's kind of silly all I had to do was remove stdguiafx to get compilation.

* [Ready] Add KernelExplorer (#36)

* KernelExplorer added

* Fix build.  Connect mainwindow to show explorer.

* qstr formatting added
hid header, fixed button size

* Taskbar Progress for install PUP/PKG (#37)

* Add Taskbar Progress for both PKG and PUP installer

* fix missing ifdefs for windows

* add mainwindow icon + thumbnail toolbar

* add game specific icons to the GSFrame

* fix icon crash

* fix appIcon's aspect ratio in SetAppIconFromPath

* Fix black borders in RGB32 icons

* rename thumbar related buttons

* EmuSettings (#35)

* Core tab done minus doing the library list.

* Graphics tab.

* Audio tab

* Input tab

* Added the other tabs

* LLE part one-- load existing libraries sorted. (I'd finish it but I'm going to look at a PR by mega)

* add search and add other libraries that aren't checked.

* Finish adding lle selecting things.

* marking my territory (#38)

fixed settingsdialog glitch and width
added groupbox to gui buttons
removed parents from layouts

* add debuggerframe + RSXDebugger (#34)

* Add Debuggerframe

* add RSXDebugger

* add RSXDebugger fo real

* RSXDebugger improved
minor adjustments

* add utf8 conversions like neko told me to
hopefully i did not utf8-ise too many things xD

* fix some variables

* maybe fix image buffers in RSXDebugger

* fixed image view (pretty sure)

* fixed image buffer (hopefully)

*  QT Opengl frame (#41)

* fix RSX Debugger headers (#40)

* fix some debugger layout issues
fix RSX Debugger headers + some comments

* add kd-11's SPU options
fix D3D12 showing on non-compatible systems
tidy up coretab

* improve D3D12 behaviour in graphicstab:
adapter selection and D3D12 render won't show on non-compatible systems
add monospace font to cgDisasm

* enable update only on visibility

* Rpcs3 qt llvm build (#42)

* LLVM pushed so mega can test

* probably is what is needed with Release LLVM

* should probably have RPCS3-Qt be using release-llvm

* include zlib the same way.

* don't talk to me about how I made this happen.

* I applied the magical treatment to debug mode too.  Though, it's entirely probably that doing it once in LLVM-release mode made this entirely redundant

* hack

* progress bar for LLVM spawns but doesn't close yet.

* fix msgDialog (#43)

fix oskDialog

* Minor bug fixzz

* fix osk and msgdialog for real (#44)

* fix msgDialog
fix oskDialog

* fix OskDialog part 2
fix MsgDialog part 2

* This bug is evil, and it should be ashamed of itself.

* Refactor YAML.  Commented out gui options that aren't added to config yet (add em back later when we merge that in)

* Fix pad stuff.

* add SaveDataUtility (#45)

* add SaveDataUtility

* fix slots

* fix slots again
fix lists not showing stuff
fix dialogs not showing
add colClicked
refactor stuff and polish some layouts

* add SaveDataDialog.h and SaveDataDialog.cpp

* tidy up mainwindow

* add callback

* fix RegisterEditor (#47)

* fix RegisterEditor

* fix other dialogs' immortality (gasp...vampires)

* remove debug leftovers

* fix InstructionEditor (#46)

* fix InstructionEditor

* fix typo

* Fix MouseClickEvents in RSXDebugger (#50)

* Fix MouseClickEvents in RSXDebugger
Fix focus on MemoryViewer and RSXDebugger
Adjust PadButtonWidth

* fix another comment

* fix debuggerframe events (#49)

* Fix pad settings bro (#48)

* Fix pad settings bro

* fix comment

* Icons and Menu-Additions (#39)

* Add Icons and iconlogic to cornerWidget and actions

* add cornerWidget toggle
fix dockwidget action state on start
remove DoSettings

* fix game removal bug
remove tableitem focus rectangle
therefore add TableItemDelegate.h

* remove grid and focus rectangle from autopausedialog

* add fullscreen checkbox to misctab
minor padsettings layout improvements

* Add show category submenu to view menu
Add gamelist filter accordingly
fix minor bug where play icon was displayed despite pause label
add boolean b_fullscreen to mainwindow for later use in GSFrame

* fix headers in autopausesettings
fix remove bug in autopausesettings
add delete keypressevent in autopausesettings
fix missing tr() and minor refactoring in gamelist

* add default Icons for play/pause/stop/restart

* Fix fullscreen start.  Some stuff was wrong with settings, just trust me.

* remove fullscreen leftovers and fix merge

* SPU stuff. (There was also a weird thing with config.h in GLGSFrame.h with an include that I removed to fix build)

* please neko's lambda fetishes (#53)

* please neko's lambda fetish in mainwindow

* please neko's lambda fetish in gamelistframe

* please neko's lambda fetish in logframe

* fix neko's lambda fetish in debuggerframe

* pleasefixdofetishsomething in Autopausesettingsdialog

* fix sth sth lambda in cg disasm

* lambda stuff in instructioneditor

* lambda kernelexplorer

* lambda-ise memoryviewer

* lambda rsxdebugger

* lambda savedatautil
this could be done even more, but the functions are not implemented

* Rpcs3 qt fixes -- shadow taskbar bug (#52)

* SShadow's bug of taskbar progress staying fixed on cancelling pkg install.

* other taskbar

* i'm still a baka

* Fix a warning

* qtQt refactoring (#54)

* fix neko's snake fetish

* File names should match headers. Are these the names I want?  Not necessarily.  But, this is much less confusing.

* i thought I committed everything with stage all.........................

* remove unused utilities

* The most important commit of them all.

* Disable legacy opengl buffers when not using opengl.

* fix code review comment

* Quick crash patch. Neko removed autopause. SO, I remove it too from emusettings/misc tab

* Merge lovely things from master (#55)

* Configuration simplified

* untrivial parts of the merge

* no need for these options anymore

* Minor change to fix column widths at startup (not sure why it doesn't work already, but adding the true makes it work so......... whatever)

* here ya go

* FIx hitting okay in settings causing graphics to messup (#57)

* fixes + msgdialog taskbarprogress (#56)

* fix ok button in taskbar
add taskicon progressbar for msgdialog
add tablewidgetitem to rsxdebugger
fix comments in save_data_utility.cpp

* fix d3d adapter default

* fix taskicon progressbar not being destroyed properly

* add last_path to filedialogs

* fix msgdialog crash on ok (#58)

* fix thread stopping in debbugerFrame (#59)

* Move Emu.init to be first.  This will fix the qt stuff seeming to ignore the virtual filesystem in the config. (VFS to be made soon maybe) (#60)

* Fix full screen opening on double RIGHT click.

* fix other instances of double click ...

* Fix locaiton of gui config. (#61)

* fix d3d bug (#62)

* fix d3d bug

* small utf8 addition

* Fix cmake for qt (#64)

* Initial CMake fix

* Fix compilation with GCC

* Get rid of awful hack

* Update cotire with qt support

* Maybe fix travis

* Emergency Hack Relief Program Activated

* Fix travis build (#65)

* make about dialog great again (#67)

and add previous additions

* Fix library sort / smart gamelist context menu (#63)

* fix library sort

* add Title to custom game config dialog

* disable options on gamelist context menu

* use namespace for category Strings

* introduce sstr

* fix some tr nonsense

* Rpcs3qt Appveyor (#68)

Fix appyveyor build!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

* possible fix for gamelist icons (#69)

add warning for appicon

* Fix clang build (#66)

Hcorion, the build savior.

* Rpcs3 qt resources (#70)

* Resource files attempt 1

* Autorcc should probably be on?

* forgot the most important file lol

* Forgot an instance of the icon in the code...

* Patch fix for clang build.

* vulkan/d3d12 combobox merge (#71)

* add vulkan adapterbox and merge with d3d12 box

* fix adapter text on other renderer

* gather render strings

* attempt fix on gamelist row height

* adjust adapter behaviour to new guideline

* Compiler of Peace.

* High critical hit rate.

* Mugi eating strawberries is savage.

* Apply KD-11 Hotfix (#73)

* Most of Ani Adjusts (#72)

* Most of the adjustments are made here.

* fix gamelist rowheight

* fix msg dialog layout and disable_cancel

* cleanup

* fix disable cancle again

* fix debuggerframe buttons and doubleclick

* Add a fun little bonus feature :) (#74)

* category filters simplyfied (#75)

* Cleaning up cmake a bit.

* fixezzzzzz (#76)

* upgrade Info Boxes

* upgrade file explorer

* refactor GetSettings and SetSettings

* second refactoring

* cleanup

* travis is a grammar nazi

* second travis shenanigans

* third travis weirdo thingy

* travis 4 mega fun

* travis 5 default to def

* finish refactoring for settings
fix gamelist headers

* hotfix msgdialog and infobox (#77)

* msgdialog fix 1

* fix zombie infobox

* Rpcs3 Qt Welcome Page (#78)

* Add a welcome dialog.

* Add enter to end of file

* i'm an idiot

* last mistake i hope

* sponsored via --> funded by

* RPCS3 does not condone piracy.

* Mega Adjusts

* Ani Adjustments and a few refactorings

* Yay

* Add Gamelist Icon Sizes (#79)

* Reverting Mega's suggestion.  If people can use alt-f4 to get around this dialog, they can probably use an emulator too.

* Fix firmware file choice dialog in QT GUI (#80)

* ani adjusts 2 + minor icon size simplifications (#81)

FPS Additions

* Update Travis to Qt 5.9 (#82)
2017-06-04 17:48:33 +03:00

1566 lines
39 KiB
C++

#include "stdafx.h"
#include "aes.h"
#include "sha1.h"
#include "utils.h"
#include "unself.h"
#include "Emu/VFS.h"
#include <algorithm>
#include <zlib.h>
inline u8 Read8(const fs::file& f)
{
u8 ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline u16 Read16(const fs::file& f)
{
be_t<u16> ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline u32 Read32(const fs::file& f)
{
be_t<u32> ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline u64 Read64(const fs::file& f)
{
be_t<u64> ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline u16 Read16LE(const fs::file& f)
{
u16 ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline u32 Read32LE(const fs::file& f)
{
u32 ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline u64 Read64LE(const fs::file& f)
{
u64 ret;
f.read(&ret, sizeof(ret));
return ret;
}
inline void Write8(const fs::file& f, const u8 data)
{
f.write(&data, sizeof(data));
}
inline void Write16LE(const fs::file& f, const u16 data)
{
f.write(&data, sizeof(data));
}
inline void Write32LE(const fs::file& f, const u32 data)
{
f.write(&data, sizeof(data));
}
inline void Write64LE(const fs::file& f, const u64 data)
{
f.write(&data, sizeof(data));
}
inline void Write16(const fs::file& f, const be_t<u16> data)
{
f.write(&data, sizeof(data));
}
inline void Write32(const fs::file& f, const be_t<u32> data)
{
f.write(&data, sizeof(data));
}
inline void Write64(const fs::file& f, const be_t<u64> data)
{
f.write(&data, sizeof(data));
}
void WriteEhdr(const fs::file& f, Elf64_Ehdr& ehdr)
{
Write32(f, ehdr.e_magic);
Write8(f, ehdr.e_class);
Write8(f, ehdr.e_data);
Write8(f, ehdr.e_curver);
Write8(f, ehdr.e_os_abi);
Write64(f, ehdr.e_abi_ver);
Write16(f, ehdr.e_type);
Write16(f, ehdr.e_machine);
Write32(f, ehdr.e_version);
Write64(f, ehdr.e_entry);
Write64(f, ehdr.e_phoff);
Write64(f, ehdr.e_shoff);
Write32(f, ehdr.e_flags);
Write16(f, ehdr.e_ehsize);
Write16(f, ehdr.e_phentsize);
Write16(f, ehdr.e_phnum);
Write16(f, ehdr.e_shentsize);
Write16(f, ehdr.e_shnum);
Write16(f, ehdr.e_shstrndx);
}
void WritePhdr(const fs::file& f, Elf64_Phdr& phdr)
{
Write32(f, phdr.p_type);
Write32(f, phdr.p_flags);
Write64(f, phdr.p_offset);
Write64(f, phdr.p_vaddr);
Write64(f, phdr.p_paddr);
Write64(f, phdr.p_filesz);
Write64(f, phdr.p_memsz);
Write64(f, phdr.p_align);
}
void WriteShdr(const fs::file& f, Elf64_Shdr& shdr)
{
Write32(f, shdr.sh_name);
Write32(f, shdr.sh_type);
Write64(f, shdr.sh_flags);
Write64(f, shdr.sh_addr);
Write64(f, shdr.sh_offset);
Write64(f, shdr.sh_size);
Write32(f, shdr.sh_link);
Write32(f, shdr.sh_info);
Write64(f, shdr.sh_addralign);
Write64(f, shdr.sh_entsize);
}
void WriteEhdr(const fs::file& f, Elf32_Ehdr& ehdr)
{
Write32(f, ehdr.e_magic);
Write8(f, ehdr.e_class);
Write8(f, ehdr.e_data);
Write8(f, ehdr.e_curver);
Write8(f, ehdr.e_os_abi);
Write64(f, ehdr.e_abi_ver);
Write16(f, ehdr.e_type);
Write16(f, ehdr.e_machine);
Write32(f, ehdr.e_version);
Write32(f, ehdr.e_entry);
Write32(f, ehdr.e_phoff);
Write32(f, ehdr.e_shoff);
Write32(f, ehdr.e_flags);
Write16(f, ehdr.e_ehsize);
Write16(f, ehdr.e_phentsize);
Write16(f, ehdr.e_phnum);
Write16(f, ehdr.e_shentsize);
Write16(f, ehdr.e_shnum);
Write16(f, ehdr.e_shstrndx);
}
void WritePhdr(const fs::file& f, Elf32_Phdr& phdr)
{
Write32(f, phdr.p_type);
Write32(f, phdr.p_offset);
Write32(f, phdr.p_vaddr);
Write32(f, phdr.p_paddr);
Write32(f, phdr.p_filesz);
Write32(f, phdr.p_memsz);
Write32(f, phdr.p_flags);
Write32(f, phdr.p_align);
}
void WriteShdr(const fs::file& f, Elf32_Shdr& shdr)
{
Write32(f, shdr.sh_name);
Write32(f, shdr.sh_type);
Write32(f, shdr.sh_flags);
Write32(f, shdr.sh_addr);
Write32(f, shdr.sh_offset);
Write32(f, shdr.sh_size);
Write32(f, shdr.sh_link);
Write32(f, shdr.sh_info);
Write32(f, shdr.sh_addralign);
Write32(f, shdr.sh_entsize);
}
void AppInfo::Load(const fs::file& f)
{
authid = Read64(f);
vendor_id = Read32(f);
self_type = Read32(f);
version = Read64(f);
padding = Read64(f);
}
void AppInfo::Show()
{
LOG_NOTICE(LOADER, "AuthID: 0x%llx", authid);
LOG_NOTICE(LOADER, "VendorID: 0x%08x", vendor_id);
LOG_NOTICE(LOADER, "SELF type: 0x%08x", self_type);
LOG_NOTICE(LOADER, "Version: 0x%llx", version);
}
void SectionInfo::Load(const fs::file& f)
{
offset = Read64(f);
size = Read64(f);
compressed = Read32(f);
unknown1 = Read32(f);
unknown2 = Read32(f);
encrypted = Read32(f);
}
void SectionInfo::Show()
{
LOG_NOTICE(LOADER, "Offset: 0x%llx", offset);
LOG_NOTICE(LOADER, "Size: 0x%llx", size);
LOG_NOTICE(LOADER, "Compressed: 0x%08x", compressed);
LOG_NOTICE(LOADER, "Unknown1: 0x%08x", unknown1);
LOG_NOTICE(LOADER, "Unknown2: 0x%08x", unknown2);
LOG_NOTICE(LOADER, "Encrypted: 0x%08x", encrypted);
}
void SCEVersionInfo::Load(const fs::file& f)
{
subheader_type = Read32(f);
present = Read32(f);
size = Read32(f);
unknown = Read32(f);
}
void SCEVersionInfo::Show()
{
LOG_NOTICE(LOADER, "Sub-header type: 0x%08x", subheader_type);
LOG_NOTICE(LOADER, "Present: 0x%08x", present);
LOG_NOTICE(LOADER, "Size: 0x%08x", size);
LOG_NOTICE(LOADER, "Unknown: 0x%08x", unknown);
}
void ControlInfo::Load(const fs::file& f)
{
type = Read32(f);
size = Read32(f);
next = Read64(f);
if (type == 1)
{
control_flags.ctrl_flag1 = Read32(f);
control_flags.unknown1 = Read32(f);
control_flags.unknown2 = Read32(f);
control_flags.unknown3 = Read32(f);
control_flags.unknown4 = Read32(f);
control_flags.unknown5 = Read32(f);
control_flags.unknown6 = Read32(f);
control_flags.unknown7 = Read32(f);
}
else if (type == 2)
{
if (size == 0x30)
{
f.read(file_digest_30.digest, 20);
file_digest_30.unknown = Read64(f);
}
else if (size == 0x40)
{
f.read(file_digest_40.digest1, 20);
f.read(file_digest_40.digest2, 20);
file_digest_40.unknown = Read64(f);
}
}
else if (type == 3)
{
npdrm.magic = Read32(f);
npdrm.unknown1 = Read32(f);
npdrm.license = Read32(f);
npdrm.type = Read32(f);
f.read(npdrm.content_id, 48);
f.read(npdrm.digest, 16);
f.read(npdrm.invdigest, 16);
f.read(npdrm.xordigest, 16);
npdrm.unknown2 = Read64(f);
npdrm.unknown3 = Read64(f);
}
}
void ControlInfo::Show()
{
LOG_NOTICE(LOADER, "Type: 0x%08x", type);
LOG_NOTICE(LOADER, "Size: 0x%08x", size);
LOG_NOTICE(LOADER, "Next: 0x%llx", next);
if (type == 1)
{
LOG_NOTICE(LOADER, "Control flag 1: 0x%08x", control_flags.ctrl_flag1);
LOG_NOTICE(LOADER, "Unknown1: 0x%08x", control_flags.unknown1);
LOG_NOTICE(LOADER, "Unknown2: 0x%08x", control_flags.unknown2);
LOG_NOTICE(LOADER, "Unknown3: 0x%08x", control_flags.unknown3);
LOG_NOTICE(LOADER, "Unknown4: 0x%08x", control_flags.unknown4);
LOG_NOTICE(LOADER, "Unknown5: 0x%08x", control_flags.unknown5);
LOG_NOTICE(LOADER, "Unknown6: 0x%08x", control_flags.unknown6);
LOG_NOTICE(LOADER, "Unknown7: 0x%08x", control_flags.unknown7);
}
else if (type == 2)
{
if (size == 0x30)
{
std::string digest_str;
for (int i = 0; i < 20; i++)
digest_str += fmt::format("%02x", file_digest_30.digest[i]);
LOG_NOTICE(LOADER, "Digest: %s", digest_str.c_str());
LOG_NOTICE(LOADER, "Unknown: 0x%llx", file_digest_30.unknown);
}
else if (size == 0x40)
{
std::string digest_str1;
std::string digest_str2;
for (int i = 0; i < 20; i++)
{
digest_str1 += fmt::format("%02x", file_digest_40.digest1[i]);
digest_str2 += fmt::format("%02x", file_digest_40.digest2[i]);
}
LOG_NOTICE(LOADER, "Digest1: %s", digest_str1.c_str());
LOG_NOTICE(LOADER, "Digest2: %s", digest_str2.c_str());
LOG_NOTICE(LOADER, "Unknown: 0x%llx", file_digest_40.unknown);
}
}
else if (type == 3)
{
std::string contentid_str;
std::string digest_str;
std::string invdigest_str;
std::string xordigest_str;
for (int i = 0; i < 48; i++)
contentid_str += fmt::format("%02x", npdrm.content_id[i]);
for (int i = 0; i < 16; i++)
{
digest_str += fmt::format("%02x", npdrm.digest[i]);
invdigest_str += fmt::format("%02x", npdrm.invdigest[i]);
xordigest_str += fmt::format("%02x", npdrm.xordigest[i]);
}
LOG_NOTICE(LOADER, "Magic: 0x%08x", npdrm.magic);
LOG_NOTICE(LOADER, "Unknown1: 0x%08x", npdrm.unknown1);
LOG_NOTICE(LOADER, "License: 0x%08x", npdrm.license);
LOG_NOTICE(LOADER, "Type: 0x%08x", npdrm.type);
LOG_NOTICE(LOADER, "ContentID: %s", contentid_str.c_str());
LOG_NOTICE(LOADER, "Digest: %s", digest_str.c_str());
LOG_NOTICE(LOADER, "Inverse digest: %s", invdigest_str.c_str());
LOG_NOTICE(LOADER, "XOR digest: %s", xordigest_str.c_str());
LOG_NOTICE(LOADER, "Unknown2: 0x%llx", npdrm.unknown2);
LOG_NOTICE(LOADER, "Unknown3: 0x%llx", npdrm.unknown3);
}
}
void MetadataInfo::Load(u8* in)
{
memcpy(key, in, 0x10);
memcpy(key_pad, in + 0x10, 0x10);
memcpy(iv, in + 0x20, 0x10);
memcpy(iv_pad, in + 0x30, 0x10);
}
void MetadataInfo::Show()
{
std::string key_str;
std::string key_pad_str;
std::string iv_str;
std::string iv_pad_str;
for (int i = 0; i < 0x10; i++)
{
key_str += fmt::format("%02x", key[i]);
key_pad_str += fmt::format("%02x", key_pad[i]);
iv_str += fmt::format("%02x", iv[i]);
iv_pad_str += fmt::format("%02x", iv_pad[i]);
}
LOG_NOTICE(LOADER, "Key: %s", key_str.c_str());
LOG_NOTICE(LOADER, "Key pad: %s", key_pad_str.c_str());
LOG_NOTICE(LOADER, "IV: %s", iv_str.c_str());
LOG_NOTICE(LOADER, "IV pad: %s", iv_pad_str.c_str());
}
void MetadataHeader::Load(u8* in)
{
memcpy(&signature_input_length, in, 8);
memcpy(&unknown1, in + 8, 4);
memcpy(&section_count, in + 12, 4);
memcpy(&key_count, in + 16, 4);
memcpy(&opt_header_size, in + 20, 4);
memcpy(&unknown2, in + 24, 4);
memcpy(&unknown3, in + 28, 4);
// Endian swap.
signature_input_length = swap64(signature_input_length);
unknown1 = swap32(unknown1);
section_count = swap32(section_count);
key_count = swap32(key_count);
opt_header_size = swap32(opt_header_size);
unknown2 = swap32(unknown2);
unknown3 = swap32(unknown3);
}
void MetadataHeader::Show()
{
LOG_NOTICE(LOADER, "Signature input length: 0x%llx", signature_input_length);
LOG_NOTICE(LOADER, "Unknown1: 0x%08x", unknown1);
LOG_NOTICE(LOADER, "Section count: 0x%08x", section_count);
LOG_NOTICE(LOADER, "Key count: 0x%08x", key_count);
LOG_NOTICE(LOADER, "Optional header size: 0x%08x", opt_header_size);
LOG_NOTICE(LOADER, "Unknown2: 0x%08x", unknown2);
LOG_NOTICE(LOADER, "Unknown3: 0x%08x", unknown3);
}
void MetadataSectionHeader::Load(u8* in)
{
memcpy(&data_offset, in, 8);
memcpy(&data_size, in + 8, 8);
memcpy(&type, in + 16, 4);
memcpy(&program_idx, in + 20, 4);
memcpy(&hashed, in + 24, 4);
memcpy(&sha1_idx, in + 28, 4);
memcpy(&encrypted, in + 32, 4);
memcpy(&key_idx, in + 36, 4);
memcpy(&iv_idx, in + 40, 4);
memcpy(&compressed, in + 44, 4);
// Endian swap.
data_offset = swap64(data_offset);
data_size = swap64(data_size);
type = swap32(type);
program_idx = swap32(program_idx);
hashed = swap32(hashed);
sha1_idx = swap32(sha1_idx);
encrypted = swap32(encrypted);
key_idx = swap32(key_idx);
iv_idx = swap32(iv_idx);
compressed = swap32(compressed);
}
void MetadataSectionHeader::Show()
{
LOG_NOTICE(LOADER, "Data offset: 0x%llx", data_offset);
LOG_NOTICE(LOADER, "Data size: 0x%llx", data_size);
LOG_NOTICE(LOADER, "Type: 0x%08x", type);
LOG_NOTICE(LOADER, "Program index: 0x%08x", program_idx);
LOG_NOTICE(LOADER, "Hashed: 0x%08x", hashed);
LOG_NOTICE(LOADER, "SHA1 index: 0x%08x", sha1_idx);
LOG_NOTICE(LOADER, "Encrypted: 0x%08x", encrypted);
LOG_NOTICE(LOADER, "Key index: 0x%08x", key_idx);
LOG_NOTICE(LOADER, "IV index: 0x%08x", iv_idx);
LOG_NOTICE(LOADER, "Compressed: 0x%08x", compressed);
}
void SectionHash::Load(const fs::file& f)
{
f.read(sha1, 20);
f.read(padding, 12);
f.read(hmac_key, 64);
}
void CapabilitiesInfo::Load(const fs::file& f)
{
type = Read32(f);
capabilities_size = Read32(f);
next = Read32(f);
unknown1 = Read32(f);
unknown2 = Read64(f);
unknown3 = Read64(f);
flags = Read64(f);
unknown4 = Read32(f);
unknown5 = Read32(f);
}
void Signature::Load(const fs::file& f)
{
f.read(r, 21);
f.read(s, 21);
f.read(padding, 6);
}
void SelfSection::Load(const fs::file& f)
{
*data = Read32(f);
size = Read64(f);
offset = Read64(f);
}
void Elf32_Ehdr::Load(const fs::file& f)
{
e_magic = Read32(f);
e_class = Read8(f);
e_data = Read8(f);
e_curver = Read8(f);
e_os_abi = Read8(f);
if (IsLittleEndian())
{
e_abi_ver = Read64LE(f);
e_type = Read16LE(f);
e_machine = Read16LE(f);
e_version = Read32LE(f);
e_entry = Read32LE(f);
e_phoff = Read32LE(f);
e_shoff = Read32LE(f);
e_flags = Read32LE(f);
e_ehsize = Read16LE(f);
e_phentsize = Read16LE(f);
e_phnum = Read16LE(f);
e_shentsize = Read16LE(f);
e_shnum = Read16LE(f);
e_shstrndx = Read16LE(f);
}
else
{
e_abi_ver = Read64(f);
e_type = Read16(f);
e_machine = Read16(f);
e_version = Read32(f);
e_entry = Read32(f);
e_phoff = Read32(f);
e_shoff = Read32(f);
e_flags = Read32(f);
e_ehsize = Read16(f);
e_phentsize = Read16(f);
e_phnum = Read16(f);
e_shentsize = Read16(f);
e_shnum = Read16(f);
e_shstrndx = Read16(f);
}
}
void Elf32_Shdr::Load(const fs::file& f)
{
sh_name = Read32(f);
sh_type = Read32(f);
sh_flags = Read32(f);
sh_addr = Read32(f);
sh_offset = Read32(f);
sh_size = Read32(f);
sh_link = Read32(f);
sh_info = Read32(f);
sh_addralign = Read32(f);
sh_entsize = Read32(f);
}
void Elf32_Shdr::LoadLE(const fs::file& f)
{
f.read(this, sizeof(*this));
}
void Elf32_Phdr::Load(const fs::file& f)
{
p_type = Read32(f);
p_offset = Read32(f);
p_vaddr = Read32(f);
p_paddr = Read32(f);
p_filesz = Read32(f);
p_memsz = Read32(f);
p_flags = Read32(f);
p_align = Read32(f);
}
void Elf32_Phdr::LoadLE(const fs::file& f)
{
f.read(this, sizeof(*this));
}
void Elf64_Ehdr::Load(const fs::file& f)
{
e_magic = Read32(f);
e_class = Read8(f);
e_data = Read8(f);
e_curver = Read8(f);
e_os_abi = Read8(f);
e_abi_ver = Read64(f);
e_type = Read16(f);
e_machine = Read16(f);
e_version = Read32(f);
e_entry = Read64(f);
e_phoff = Read64(f);
e_shoff = Read64(f);
e_flags = Read32(f);
e_ehsize = Read16(f);
e_phentsize = Read16(f);
e_phnum = Read16(f);
e_shentsize = Read16(f);
e_shnum = Read16(f);
e_shstrndx = Read16(f);
}
void Elf64_Shdr::Load(const fs::file& f)
{
sh_name = Read32(f);
sh_type = Read32(f);
sh_flags = Read64(f);
sh_addr = Read64(f);
sh_offset = Read64(f);
sh_size = Read64(f);
sh_link = Read32(f);
sh_info = Read32(f);
sh_addralign = Read64(f);
sh_entsize = Read64(f);
}
void Elf64_Phdr::Load(const fs::file& f)
{
p_type = Read32(f);
p_flags = Read32(f);
p_offset = Read64(f);
p_vaddr = Read64(f);
p_paddr = Read64(f);
p_filesz = Read64(f);
p_memsz = Read64(f);
p_align = Read64(f);
}
void SceHeader::Load(const fs::file& f)
{
se_magic = Read32(f);
se_hver = Read32(f);
se_flags = Read16(f);
se_type = Read16(f);
se_meta = Read32(f);
se_hsize = Read64(f);
se_esize = Read64(f);
}
void SelfHeader::Load(const fs::file& f)
{
se_htype = Read64(f);
se_appinfooff = Read64(f);
se_elfoff = Read64(f);
se_phdroff = Read64(f);
se_shdroff = Read64(f);
se_secinfoff = Read64(f);
se_sceveroff = Read64(f);
se_controloff = Read64(f);
se_controlsize = Read64(f);
pad = Read64(f);
}
SCEDecrypter::SCEDecrypter(const fs::file& s)
: sce_f(s)
, data_buf_length(0)
{
}
bool SCEDecrypter::LoadHeaders()
{
// Read SCE header.
sce_f.seek(0);
sce_hdr.Load(sce_f);
// Check SCE magic.
if (!sce_hdr.CheckMagic())
{
LOG_ERROR(LOADER, "SELF: Not a SELF file!");
return false;
}
return true;
}
bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
{
aes_context aes;
u32 metadata_info_size = SIZE_32(meta_info);
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
// Locate and read the encrypted metadata info.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
sce_f.read(metadata_info.get(), metadata_info_size);
// Locate and read the encrypted metadata header and section header.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
sce_f.read(metadata_headers.get(), metadata_headers_size);
// Copy the necessary parameters.
u8 metadata_key[0x20];
u8 metadata_iv[0x10];
memcpy(metadata_key, erk, 0x20);
memcpy(metadata_iv, riv, 0x10);
// Check DEBUG flag.
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
{
// Decrypt the metadata info.
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
}
// Load the metadata info.
meta_info.Load(metadata_info.get());
// If the padding is not NULL for the key or iv fields, the metadata info
// is not properly decrypted.
if ((meta_info.key_pad[0] != 0x00) ||
(meta_info.iv_pad[0] != 0x00))
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
return false;
}
// Perform AES-CTR encryption on the metadata headers.
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
aes_setkey_enc(&aes, meta_info.key, 128);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
// Load the metadata header.
meta_hdr.Load(metadata_headers.get());
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
meta_shdr.emplace_back();
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
}
// Copy the decrypted data keys.
data_keys_length = meta_hdr.key_count * 0x10;
data_keys = std::make_unique<u8[]>(data_keys_length);
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
return true;
}
bool SCEDecrypter::DecryptData()
{
aes_context aes;
// Calculate the total data size.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
data_buf_length += meta_shdr[i].data_size;
}
// Allocate a buffer to store decrypted data.
data_buf = std::make_unique<u8[]>(data_buf_length);
// Set initial offset.
u32 data_buf_offset = 0;
// Parse the metadata section headers to find the offsets of encrypted data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
u8 data_key[0x10];
u8 data_iv[0x10];
// Check if this is an encrypted section.
if (meta_shdr[i].encrypted == 3)
{
// Make sure the key and iv are not out of boundaries.
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
// Seek to the section data offset and read the encrypted data.
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
// Zero out our ctr nonce.
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
// Copy the decrypted data.
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
}
}
else
{
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
}
// Advance the buffer's offset.
data_buf_offset += meta_shdr[i].data_size;
}
return true;
}
// Each section gets put into its own file.
std::vector<fs::file> SCEDecrypter::MakeFile()
{
std::vector<fs::file> vec;
// Set initial offset.
u32 data_buf_offset = 0;
// Write data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
fs::file out_f = fs::make_stream<std::vector<u8>>();
bool isValid = true;
// Decompress if necessary.
if (meta_shdr[i].compressed == 2)
{
const size_t BUFSIZE = 32 * 1024;
u8 tempbuf[BUFSIZE];
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = meta_shdr[i].data_size;
strm.avail_out = BUFSIZE;
strm.next_in = data_buf.get()+data_buf_offset;
strm.next_out = tempbuf;
int ret = inflateInit(&strm);
while (strm.avail_in)
{
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_STREAM_END)
break;
if (ret != Z_OK)
isValid = false;
if (!strm.avail_out) {
out_f.write(tempbuf, BUFSIZE);
strm.next_out = tempbuf;
strm.avail_out = BUFSIZE;
}
else
break;
}
int inflate_res = Z_OK;
inflate_res = inflate(&strm, Z_FINISH);
if (inflate_res != Z_STREAM_END)
isValid = false;
out_f.write(tempbuf, BUFSIZE - strm.avail_out);
inflateEnd(&strm);
}
else
{
// Write the data.
out_f.write(data_buf.get()+data_buf_offset, meta_shdr[i].data_size);
}
// Advance the data buffer offset by data size.
data_buf_offset += meta_shdr[i].data_size;
if (out_f.pos() != out_f.size())
fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size());
if (isValid) vec.push_back(std::move(out_f));
}
return vec;
}
SELFDecrypter::SELFDecrypter(const fs::file& s)
: self_f(s)
, key_v()
, data_buf_length(0)
{
}
bool SELFDecrypter::LoadHeaders(bool isElf32)
{
// Read SCE header.
self_f.seek(0);
sce_hdr.Load(self_f);
// Check SCE magic.
if (!sce_hdr.CheckMagic())
{
LOG_ERROR(LOADER, "SELF: Not a SELF file!");
return false;
}
// Read SELF header.
self_hdr.Load(self_f);
// Read the APP INFO.
self_f.seek(self_hdr.se_appinfooff);
app_info.Load(self_f);
// Read ELF header.
self_f.seek(self_hdr.se_elfoff);
if (isElf32)
elf32_hdr.Load(self_f);
else
elf64_hdr.Load(self_f);
// Read ELF program headers.
if (isElf32)
{
phdr32_arr.clear();
if(elf32_hdr.e_phoff == 0 && elf32_hdr.e_phnum)
{
LOG_ERROR(LOADER, "SELF: ELF program header offset is null!");
return false;
}
self_f.seek(self_hdr.se_phdroff);
for(u32 i = 0; i < elf32_hdr.e_phnum; ++i)
{
phdr32_arr.emplace_back();
phdr32_arr.back().Load(self_f);
}
}
else
{
phdr64_arr.clear();
if (elf64_hdr.e_phoff == 0 && elf64_hdr.e_phnum)
{
LOG_ERROR(LOADER, "SELF: ELF program header offset is null!");
return false;
}
self_f.seek(self_hdr.se_phdroff);
for (u32 i = 0; i < elf64_hdr.e_phnum; ++i)
{
phdr64_arr.emplace_back();
phdr64_arr.back().Load(self_f);
}
}
// Read section info.
secinfo_arr.clear();
self_f.seek(self_hdr.se_secinfoff);
for(u32 i = 0; i < ((isElf32) ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
{
secinfo_arr.emplace_back();
secinfo_arr.back().Load(self_f);
}
// Read SCE version info.
self_f.seek(self_hdr.se_sceveroff);
scev_info.Load(self_f);
// Read control info.
ctrlinfo_arr.clear();
self_f.seek(self_hdr.se_controloff);
u32 i = 0;
while(i < self_hdr.se_controlsize)
{
ctrlinfo_arr.emplace_back();
ControlInfo &cinfo = ctrlinfo_arr.back();
cinfo.Load(self_f);
i += cinfo.size;
}
// Read ELF section headers.
if (isElf32)
{
shdr32_arr.clear();
if (elf32_hdr.e_shoff == 0 && elf32_hdr.e_shnum)
{
LOG_WARNING(LOADER, "SELF: ELF section header offset is null!");
return true;
}
self_f.seek(self_hdr.se_shdroff);
for(u32 i = 0; i < elf32_hdr.e_shnum; ++i)
{
shdr32_arr.emplace_back();
shdr32_arr.back().Load(self_f);
}
}
else
{
shdr64_arr.clear();
if (elf64_hdr.e_shoff == 0 && elf64_hdr.e_shnum)
{
LOG_WARNING(LOADER, "SELF: ELF section header offset is null!");
return true;
}
self_f.seek(self_hdr.se_shdroff);
for(u32 i = 0; i < elf64_hdr.e_shnum; ++i)
{
shdr64_arr.emplace_back();
shdr64_arr.back().Load(self_f);
}
}
return true;
}
void SELFDecrypter::ShowHeaders(bool isElf32)
{
LOG_NOTICE(LOADER, "SCE header");
LOG_NOTICE(LOADER, "----------------------------------------------------");
sce_hdr.Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "SELF header");
LOG_NOTICE(LOADER, "----------------------------------------------------");
self_hdr.Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "APP INFO");
LOG_NOTICE(LOADER, "----------------------------------------------------");
app_info.Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "ELF header");
LOG_NOTICE(LOADER, "----------------------------------------------------");
isElf32 ? elf32_hdr.Show() : elf64_hdr.Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "ELF program headers");
LOG_NOTICE(LOADER, "----------------------------------------------------");
for(unsigned int i = 0; i < ((isElf32) ? phdr32_arr.size() : phdr64_arr.size()); i++)
isElf32 ? phdr32_arr[i].Show() : phdr64_arr[i].Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "Section info");
LOG_NOTICE(LOADER, "----------------------------------------------------");
for(unsigned int i = 0; i < secinfo_arr.size(); i++)
secinfo_arr[i].Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "SCE version info");
LOG_NOTICE(LOADER, "----------------------------------------------------");
scev_info.Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "Control info");
LOG_NOTICE(LOADER, "----------------------------------------------------");
for(unsigned int i = 0; i < ctrlinfo_arr.size(); i++)
ctrlinfo_arr[i].Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
LOG_NOTICE(LOADER, "ELF section headers");
LOG_NOTICE(LOADER, "----------------------------------------------------");
for(unsigned int i = 0; i < ((isElf32) ? shdr32_arr.size() : shdr64_arr.size()); i++)
isElf32 ? shdr32_arr[i].Show() : shdr64_arr[i].Show();
LOG_NOTICE(LOADER, "----------------------------------------------------");
}
bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
{
aes_context aes;
ControlInfo *ctrl = NULL;
u8 npdrm_key[0x10];
u8 npdrm_iv[0x10];
// Parse the control info structures to find the NPDRM control info.
for(unsigned int i = 0; i < ctrlinfo_arr.size(); i++)
{
if (ctrlinfo_arr[i].type == 3)
{
ctrl = &ctrlinfo_arr[i];
break;
}
}
// Check if we have a valid NPDRM control info structure.
// If not, the data has no NPDRM layer.
if (!ctrl)
{
LOG_WARNING(LOADER, "SELF: No NPDRM control info found!");
return true;
}
if (ctrl->npdrm.license == 1) // Network license.
{
LOG_ERROR(LOADER, "SELF: Can't decrypt network NPDRM!");
return false;
}
else if (ctrl->npdrm.license == 2) // Local license.
{
// Try to find a RAP file to get the key.
if (!GetKeyFromRap(ctrl->npdrm.content_id, npdrm_key))
{
LOG_ERROR(LOADER, "SELF: Can't find RAP file for NPDRM decryption!");
return false;
}
}
else if (ctrl->npdrm.license == 3) // Free license.
{
// Use klicensee if available.
if (key_v.GetKlicenseeKey() != nullptr)
memcpy(npdrm_key, key_v.GetKlicenseeKey(), 0x10);
else
memcpy(npdrm_key, NP_KLIC_FREE, 0x10);
}
else
{
LOG_ERROR(LOADER, "SELF: Invalid NPDRM license type!");
return false;
}
// Decrypt our key with NP_KLIC_KEY.
aes_setkey_dec(&aes, NP_KLIC_KEY, 128);
aes_crypt_ecb(&aes, AES_DECRYPT, npdrm_key, npdrm_key);
// IV is empty.
memset(npdrm_iv, 0, 0x10);
// Use our final key to decrypt the NPDRM layer.
aes_setkey_dec(&aes, npdrm_key, 128);
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_size, npdrm_iv, metadata, metadata);
return true;
}
bool SELFDecrypter::LoadMetadata(u8* klic_key)
{
aes_context aes;
u32 metadata_info_size = SIZE_32(meta_info);
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
// Locate and read the encrypted metadata info.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
self_f.read(metadata_info.get(), metadata_info_size);
// Locate and read the encrypted metadata header and section header.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
self_f.read(metadata_headers.get(), metadata_headers_size);
// Find the right keyset from the key vault.
SELF_KEY keyset = key_v.FindSelfKey(app_info.self_type, sce_hdr.se_flags, app_info.version);
// Set klic if given
if (klic_key)
key_v.SetKlicenseeKey(klic_key);
// Copy the necessary parameters.
u8 metadata_key[0x20];
u8 metadata_iv[0x10];
memcpy(metadata_key, keyset.erk, 0x20);
memcpy(metadata_iv, keyset.riv, 0x10);
// Check DEBUG flag.
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
{
// Decrypt the NPDRM layer.
if (!DecryptNPDRM(metadata_info.get(), metadata_info_size))
return false;
// Decrypt the metadata info.
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
}
// Load the metadata info.
meta_info.Load(metadata_info.get());
// If the padding is not NULL for the key or iv fields, the metadata info
// is not properly decrypted.
if ((meta_info.key_pad[0] != 0x00) ||
(meta_info.iv_pad[0] != 0x00))
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
return false;
}
// Perform AES-CTR encryption on the metadata headers.
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
aes_setkey_enc(&aes, meta_info.key, 128);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
// Load the metadata header.
meta_hdr.Load(metadata_headers.get());
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
meta_shdr.emplace_back();
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
}
// Copy the decrypted data keys.
data_keys_length = meta_hdr.key_count * 0x10;
data_keys = std::make_unique<u8[]>(data_keys_length);
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
return true;
}
bool SELFDecrypter::DecryptData()
{
aes_context aes;
// Calculate the total data size.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
if (meta_shdr[i].encrypted == 3)
{
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
data_buf_length += meta_shdr[i].data_size;
}
}
// Allocate a buffer to store decrypted data.
data_buf = std::make_unique<u8[]>(data_buf_length);
// Set initial offset.
u32 data_buf_offset = 0;
// Parse the metadata section headers to find the offsets of encrypted data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
u8 data_key[0x10];
u8 data_iv[0x10];
// Check if this is an encrypted section.
if (meta_shdr[i].encrypted == 3)
{
// Make sure the key and iv are not out of boundaries.
if((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
// Seek to the section data offset and read the encrypted data.
self_f.seek(meta_shdr[i].data_offset);
self_f.read(buf.get(), meta_shdr[i].data_size);
// Zero out our ctr nonce.
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
// Copy the decrypted data.
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
// Advance the buffer's offset.
data_buf_offset += meta_shdr[i].data_size;
}
}
}
return true;
}
fs::file SELFDecrypter::MakeElf(bool isElf32)
{
// Create a new ELF file.
fs::file e = fs::make_stream<std::vector<u8>>();
// Set initial offset.
u32 data_buf_offset = 0;
if (isElf32)
{
// Write ELF header.
WriteEhdr(e, elf32_hdr);
// Write program headers.
for (u32 i = 0; i < elf32_hdr.e_phnum; ++i)
{
WritePhdr(e, phdr32_arr[i]);
}
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
// PHDR type.
if (meta_shdr[i].type == 2)
{
// Seek to the program header data offset and write the data.
e.seek(phdr32_arr[meta_shdr[i].program_idx].p_offset);
e.write(data_buf.get() + data_buf_offset, meta_shdr[i].data_size);
// Advance the data buffer offset by data size.
data_buf_offset += meta_shdr[i].data_size;
}
}
// Write section headers.
if (self_hdr.se_shdroff != 0)
{
e.seek(elf32_hdr.e_shoff);
for (u32 i = 0; i < elf32_hdr.e_shnum; ++i)
{
WriteShdr(e, shdr32_arr[i]);
}
}
}
else
{
// Write ELF header.
WriteEhdr(e, elf64_hdr);
// Write program headers.
for (u32 i = 0; i < elf64_hdr.e_phnum; ++i)
{
WritePhdr(e, phdr64_arr[i]);
}
// Write data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
// PHDR type.
if (meta_shdr[i].type == 2)
{
// Decompress if necessary.
if (meta_shdr[i].compressed == 2)
{
/// Removed all wxWidget dependent code. Replaced with zlib functions.
/// Also changed local mallocs to unique_ptrs.
// Store the length in writeable memory space.
std::unique_ptr<uLongf> decomp_buf_length(new uLongf);
memcpy(decomp_buf_length.get(), &phdr64_arr[meta_shdr[i].program_idx].p_filesz, sizeof(uLongf));
/// Create a pointer to a buffer for decompression.
std::unique_ptr<u8[]> decomp_buf(new u8[phdr64_arr[meta_shdr[i].program_idx].p_filesz]);
// Create a buffer separate from data_buf to uncompress.
std::unique_ptr<u8[]> zlib_buf(new u8[data_buf_length]);
memcpy(zlib_buf.get(), data_buf.get(), data_buf_length);
// Use zlib uncompress on the new buffer.
// decomp_buf_length changes inside the call to uncompress, so it must be a pointer to correct type (in writeable mem space).
int rv = uncompress(decomp_buf.get(), decomp_buf_length.get(), zlib_buf.get() + data_buf_offset, data_buf_length);
// Check for errors (TODO: Probably safe to remove this once these changes have passed testing.)
switch (rv)
{
case Z_MEM_ERROR: LOG_ERROR(LOADER, "MakeELF encountered a Z_MEM_ERROR!"); break;
case Z_BUF_ERROR: LOG_ERROR(LOADER, "MakeELF encountered a Z_BUF_ERROR!"); break;
case Z_DATA_ERROR: LOG_ERROR(LOADER, "MakeELF encountered a Z_DATA_ERROR!"); break;
default: break;
}
// Seek to the program header data offset and write the data.
e.seek(phdr64_arr[meta_shdr[i].program_idx].p_offset);
e.write(decomp_buf.get(), phdr64_arr[meta_shdr[i].program_idx].p_filesz);
}
else
{
// Seek to the program header data offset and write the data.
e.seek(phdr64_arr[meta_shdr[i].program_idx].p_offset);
e.write(data_buf.get() + data_buf_offset, meta_shdr[i].data_size);
}
// Advance the data buffer offset by data size.
data_buf_offset += meta_shdr[i].data_size;
}
}
// Write section headers.
if (self_hdr.se_shdroff != 0)
{
e.seek(elf64_hdr.e_shoff);
for (u32 i = 0; i < elf64_hdr.e_shnum; ++i)
{
WriteShdr(e, shdr64_arr[i]);
}
}
}
return e;
}
bool SELFDecrypter::GetKeyFromRap(u8* content_id, u8* npdrm_key)
{
// Set empty RAP key.
u8 rap_key[0x10];
memset(rap_key, 0, 0x10);
// Try to find a matching RAP file under exdata folder.
const std::string ci_str = reinterpret_cast<const char*>(content_id);
const std::string rap_path = "/dev_hdd0/home/00000001/exdata/" + ci_str + ".rap";
// Open the RAP file and read the key.
const fs::file rap_file(vfs::get(rap_path));
if (!rap_file)
{
LOG_FATAL(LOADER, "Failed to load RAP file: %s", rap_path);
return false;
}
LOG_NOTICE(LOADER, "Loading RAP file %s.rap", ci_str);
rap_file.read(rap_key, 0x10);
// Convert the RAP key.
rap_to_rif(rap_key, npdrm_key);
return true;
}
static bool IsSelfElf32(const fs::file& f)
{
if (!f) return false;
f.seek(0);
SceHeader hdr;
SelfHeader sh;
hdr.Load(f);
sh.Load(f);
// Locate the class byte and check it.
u8 elf_class[0x8];
f.seek(sh.se_elfoff);
f.read(elf_class, 0x8);
return (elf_class[4] == 1);
}
static bool CheckDebugSelf(fs::file& s)
{
if (s.size() < 0x18)
{
return false;
}
// Get the key version.
s.seek(0x08);
const u16 key_version = s.read<le_t<u16>>();
// Check for DEBUG version.
if (key_version == 0x80 || key_version == 0xc0)
{
LOG_WARNING(LOADER, "Debug SELF detected! Removing fake header...");
// Get the real elf offset.
s.seek(0x10);
// Start at the real elf offset.
s.seek(key_version == 0x80 ? +s.read<be_t<u64>>() : +s.read<le_t<u64>>());
// Write the real ELF file back.
fs::file e = fs::make_stream<std::vector<u8>>();
// Copy the data.
char buf[2048];
while (u64 size = s.read(buf, 2048))
{
e.write(buf, size);
}
s = std::move(e);
return true;
}
// Leave the file untouched.
return false;
}
extern fs::file decrypt_self(fs::file elf_or_self, u8* klic_key)
{
if (!elf_or_self)
{
return fs::file{};
}
elf_or_self.seek(0);
// Check SELF header first. Check for a debug SELF.
if (elf_or_self.size() >= 4 && elf_or_self.read<u32>() == "SCE\0"_u32 && !CheckDebugSelf(elf_or_self))
{
// Check the ELF file class (32 or 64 bit).
bool isElf32 = IsSelfElf32(elf_or_self);
// Start the decrypter on this SELF file.
SELFDecrypter self_dec(elf_or_self);
// Load the SELF file headers.
if (!self_dec.LoadHeaders(isElf32))
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
return fs::file{};
}
// Load and decrypt the SELF file metadata.
if (!self_dec.LoadMetadata(klic_key))
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
return fs::file{};
}
// Decrypt the SELF file data.
if (!self_dec.DecryptData())
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt SELF file data!");
return fs::file{};
}
// Make a new ELF file from this SELF.
return self_dec.MakeElf(isElf32);
}
return elf_or_self;
}
extern bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key)
{
if (!self)
return false;
self.seek(0);
if (self.size() >= 4 && self.read<u32>() == "SCE\0"_u32)
{
// Check the ELF file class (32 or 64 bit).
bool isElf32 = IsSelfElf32(self);
// Start the decrypter on this SELF file.
SELFDecrypter self_dec(self);
// Load the SELF file headers.
if (!self_dec.LoadHeaders(isElf32))
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
return false;
}
// Load and decrypt the SELF file metadata.
if (!self_dec.LoadMetadata(klic_key))
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
return false;
}
}
return true;
}
std::array<u8, 0x10> get_default_self_klic()
{
std::array<u8, 0x10> key;
std::copy(std::begin(NP_KLIC_FREE), std::end(NP_KLIC_FREE), std::begin(key));
return key;
}