Freearc And Dark Souls 3
In my free time I work on lots of small projects. One of those is called DarkSouls3.TextViewer and it lets you view all dialogue and item descriptions in Dark Souls 3:
To do this you have to extract the contents of Data1.bdt
, which can be done with BinderTool by Atvaark. However, recently I got interested in possible changes made to item descriptions during updates, so I went on a hunt for all versions of Data1.bdt
. Because Steam does not allow you to downgrade the game versions I started looking for pirated releases and updates to try to piece everything together. I downloaded Dark.Souls.III.The.Ringed.City-CODEX
to get started, but then I noticed that I was too low on disk space to install the game…
InnoSetup
A quick look at setup.exe
with ProtectionID reveals that it is an InnoSetup installer:
There are several free tools available (innoextract, innounp) to see what’s inside:
>innoextract.exe -l e:\setup.exe
Listing "Dark Souls III The Ringed City" - setup data version 5.5.0.1 (unicode)
- "tmp\ISDone.dll" [temp] (446 KiB)
- "tmp\english.ini" [temp] (15.4 KiB)
- "tmp\Style.vsf" [temp] (44.7 KiB)
- "tmp\VclStylesinno.dll" [temp] (1.95 MiB)
- "tmp\BASS.dll" [temp] (107 KiB)
- "tmp\bp.dll" [temp] (129 KiB)
- "tmp\wintb.dll" [temp] (27.5 KiB)
- "tmp\Music.ogg" [temp] (2.34 MiB)
- "tmp\Play1.bmp" [temp] (540 B)
- "tmp\Play2.bmp" [temp] (540 B)
- "tmp\Play3.bmp" [temp] (540 B)
- "tmp\Pause1.bmp" [temp] (540 B)
- "tmp\Pause2.bmp" [temp] (540 B)
- "tmp\Pause3.bmp" [temp] (540 B)
- "tmp\trackBkg.bmp" [temp] (776 B)
- "tmp\trackbtn1.bmp" [temp] (344 B)
- "tmp\trackbtn2.bmp" [temp] (344 B)
- "tmp\trackbtn3.bmp" [temp] (344 B)
- "tmp\unarc.dll" [temp] (368 KiB)
Warning: "setup-1.bin" is not part of the installer!
Use the --gog option to try listing the contents of this file.
Done with 1 warning.
The setup-1.bin
file starts with ArC
, so I checked the exports of unarc.dll
and one that stands out is called FreeArcExtract
, which points to FreeArc.
I tried to list the files in the archive, but the file format appears to be customized (or an older version):
>unarc l e:\setup-1.bin
FreeArc 0.67 unpacker
ERROR: archive structure corrupted (descriptor failed CRC check)
FreeArc
Then I thought, perhaps I can use unarc.dll
from the setup to extract the relevant files? The lead is the name of the export FreeArcExtract
. A bit of Googlefoo pointed to the relevant code, which looks like this:
int __cdecl FreeArcExtract (cbtype *callback, ...)
{
va_list argptr;
va_start(argptr, callback);
int argc=0;
char *argv[1000] = {"c:\\unarc.dll"}; //// Здесь будет искаться arc.ini!
for (int i=1; i<1000; i++)
{
argc = i;
argv[i] = va_arg(argptr, char*);
if (argv[i]==NULL || argv[i][0]==0)
{argv[i]=NULL; break;}
}
va_end(argptr);
COMMAND command (argc, argv); // Распарсить команду
if (command.ok) { // Если парсинг был удачен и можно выполнить команду
command.Prepare();
CThread thread;
DLLUI *ui = new DLLUI(&command);
thread.Create (timer_thread, ui); // Спец. тред, вызывающий callback 100 раз в секунду
thread.Create (decompress_thread, ui); // Выполнить разобранную команду
for(;;)
{
ui->DoEvent.Lock();
if (strequ (ui->what, "quit"))
return ui->n1; // error code of command
ui->result = callback (ui->what, ui->n1, ui->n2, ui->str);
ui->EventDone.Signal();
}
thread.Wait();
}
return command.ok? FREEARC_OK : FREEARC_ERRCODE_GENERAL;
}
Basically what this does is forward all the input parameters as the argv
of unarc
. After a lot of fooling around with the awfulness of va_list
and lots of crashes I finally got the code to forward argv
to the FreeArcExtract
function:
#include <windows.h>
#include <cstdio>
#define whut(x) (strcmp(what, #x) == 0)
static int __stdcall cbExtract(char* what, int int1, int int2, char* str)
{
if(whut(read) || whut(write))
return 0; //filter out the plentiful "read" and "write" messages
printf("\"%s\", %d, %d, \"%s\"\n", what, int1, int2, str);
return 0;
}
typedef int __stdcall cbtype(char* what, int int1, int int2, char* str);
typedef int __cdecl pFreeArcExtract(cbtype* callback, ...);
int main(int argc, char* argv[])
{
auto hMod = LoadLibraryA("unarc.dll");
if(!hMod)
{
puts("Failed to load DLL: unarc.dll!");
return 1;
}
auto FreeArcExtract = (pFreeArcExtract*)GetProcAddress(hMod, "FreeArcExtract");
if(!FreeArcExtract)
{
puts("Failed to find export: FreeArcExtract");
return 1;
}
auto a = [&](int i)
{
return i < argc ? argv[i] : "";
};
return FreeArcExtract(cbExtract, a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8), a(9), a(10), nullptr);
}
First I tried to get the help with unarc_cmd.exe
, but this came up empty. Instead I asked unarc.exe
:
>unarc
FreeArc 0.67 unpacker http://freearc.org 2014-03-16
Usage: unarc command [options] archive[.arc] [filenames...]
Available commands:
l - display archive listing
v - display verbose archive listing
e - extract files into current directory
x - extract files with pathnames
t - test archive integrity
... (more irrelevant options)
Then I tried to list all the files in the archive, which did not give me the output I expected at all (but hey, at least no CRC errors):
>unarc_cmd.exe l e:\setup-1.bin
"total_files", 283, 0, ""
"origsize", 25527, 998151285, ""
"compsize", 25096, 545797223, ""
The v
option also came up empty, but the t
option had more promise:
>unarc_cmd t e:\setup-1.bin
"total", 25096, 545800879, ""
"filename", 0, 810208, "Game\Data0.bdt"
"filename", 922, 967053390, "Game\Data1.bdt"
"filename", 2450, -1724920912, "Game\Data2.bdt"
... (it takes a few minutes to test all files)
To extract Data1.bdt
, BinderTool also needs a file with decryption keys called Data1.bhd
, so I used the following command to extract both those files:
>unarc_cmd x e:\setup-1.bin Data1.bdt Data1.bhd
"total", 25096, 545800879, ""
"filename", 0, 810208, "Game\Data0.bdt"
"overwrite?", 922, 967053390, "Game\Data1.bdt"
"filename", 922, 967053390, "Game\Data1.bdt"
"filename", 2450, -1724920912, "Game\Data2.bdt"
"filename", 1474, 1546563828, "Game\Data3.bdt"
"filename", 1172, 1229026224, "Game\Data4.bdt"
"filename", 13172, 927431435, "Game\Data5.bdt"
"filename", 1551, 1626443628, "Game\DLC1.bdt"
"filename", 2929, -1222753793, "Game\DLC2.bdt"
"filename", 0, 2212, "Game\Data0.bhd"
"filename", 0, 411904, "Game\Data1.bhd"
Conclusion
Well, I hope this was interesting to some people. It was just a 45 minute side project of mine that I decided to share.
If anyone has 魔法うんちく_dlc2.fmg
from before the description of the White Birch Bow was changed, please ping me (the CODEX
release is from after the update apparently).
Cmake Openssl And Mingw On Windows
If you found this you are probably having issues linking OpenSSL to MinGW-w64 using CMake (or CLion) on Windows. In this post I will give a quick overview on how to get this to work on a clean Windows machine…
The distribution I used to get it to work is Win64OpenSSL-1_2_2k.exe. The issue was that there are no MinGW-compatible link libaries. To solve this you can use my genlib tool to generate them:
cd c:\OpenSSL-Win64
set PATH=%PATH%;c:\genlib
genlib ssleay32.dll
genlib libeay32.dll
copy *.a lib\
The CMakeLists.txt
looks like this:
# Project configuration
cmake_minimum_required(VERSION 2.7)
project(OpenSSLTest)
# Use C++11
set(CMAKE_CXX_STANDARD 11)
# Project source files
set(SOURCE_FILES main.cpp)
add_executable(OpenSSLTest ${SOURCE_FILES})
# OpenSSL (find, include, link)
find_package(OpenSSL REQUIRED)
include_directories(${OPENSSL_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES})
And some simple test code:
#include <iostream>
#include <openssl/ssl.h>
int main() {
std::cout << "SSLeay Version: " << SSLeay_version(SSLEAY_VERSION) << std::endl;
SSL_library_init();
auto ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx) {
auto ssl = SSL_new(ctx);
if (ssl) {
std::cout << "SSL Version: " << SSL_get_version(ssl) << std::endl;
SSL_free(ssl);
} else {
std::cout << "SSL_new failed..." << std::endl;
}
SSL_CTX_free(ctx);
} else {
std::cout << "SSL_CTX_new failed..." << std::endl;
}
}
If everything is configured correctly this should print:
SSLeay Version: OpenSSL 1.0.2k 26 Jan 2017
SSL Version: TLSv1.2
If you cannot be bothered to run genlib
yourself, you can find a copy the required files here and get started immediately.
Hope this helped,
Duncan
My Inactivity
Currently I do not have much time to update this blog because I am writing for the x64dbg blog, check it out!
Duncan
Github Gpg
Hello everyone,
Today I saw this broadcast on Github which states that GPG signature verification was added to Github. It took me a bit of searching before I got it to work from both the command line and Git Extensions so in this guide I will explain how I did it.
Installing Git (Extensions)
The first thing to install is the latest (v2.0.0+) version of Git for Windows.
After will have to install Git Extensions. Make sure to select the -SetupComplete
but do not install MsysGit from there since you already installed a newer version.
Make sure you configure Git (Extensions) correctly so your identity is in sync with your Github email/username.
Installing GPG
You can download and install GPG from here. Next verify that you installed everything correctly:
C:\Users\Admin>git --version
git version 2.8.3.windows.1
C:\Users\Admin>gpg --version
gpg (GnuPG) 2.0.30 (Gpg4win 2.3.1)
libgcrypt 1.6.5
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Home: C:/Users/Admin/AppData/Roaming/gnupg
Supported algorithms:
Pubkey: RSA, RSA, RSA, ELG, DSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2
If you get any errors, make sure you added everything to your PATH
environment variable.
Generating GPG Keys
Follow this guide. In short:
C:\Users\Admin>gpg --gen-key
gpg (GnuPG) 2.0.30; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
Real name: githubgpgtest
Email address: githubgpgtest@gmail.com
Comment:
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
pub 4096R/6B84CA35 2016-05-30
Key fingerprint = DF55 D8E3 B4E5 9614 7ADF 8E6E E5B6 4A58 6B84 CA35
uid [ultimate] githubgpgtest <githubgpgtest@gmail.com>
sub 4096R/63BEB3EE 2016-05-30
Notice: I will be using 6B84CA35
as my identifier for my key, you should use your own in the upcoming commands.
Adding your key to Github
Follow this guide. In short:
C:\Users\Admin>gpg --list-keys
C:/Users/Admin/AppData/Roaming/gnupg/pubring.gpg
------------------------------------------------
pub 4096R/6B84CA35 2016-05-30
uid [ultimate] githubgpgtest <githubgpgtest@gmail.com>
sub 4096R/63BEB3EE 2016-05-30
C:\Users\Admin>gpg --armor --export 6B84CA35
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
mQINBFdMlawBEADUmBNVR8psLgeBQ1hz2N7VjVmiPiwbgpIK9VAToLX8BYl2ZPYB
...
=k1LJ
-----END PGP PUBLIC KEY BLOCK-----
Add your key to your Github account through this page:
Configuring Git
Set your globally installed GPG version in Git (make sure to alter this path if you installed gpg2.exe
in a different location):
git config --global gpg.program "C:/Program Files (x86)/GNU/GnuPG/gpg2.exe"
Then set your generated signing key:
git config --global user.signingkey 6B84CA35
These commands enable automatic GPG signing for commits and tags (which is required if you want this to work with Git Extensions):
git config --global commit.gpgsign true
git config --global tag.gpgsign true
Now when commiting the Git Extensions it should show you the following dialog:
After pushing to the repository Github shows your commits as verified:
Conclusion
That’s about it, your passphrase should cache for a while so you shouldn’t be bothered with entering your passphrase every single time you commit. You can configure your caching times here:
The first entry is the default-cache-ttl
option, the second max-cache-ttl
:
--default-cache-ttl n
Set the time a cache entry is valid to n seconds. The default is 600
seconds.
--max-cache-ttl n
Set the maximum time a cache entry is valid to n seconds. After this time a
cache entry will be expired even if it has been accessed recently. The
default is 2 hours (7200 seconds).
If you enjoyed this post, feel free to share it with your friends through social media.
Duncan
Dynamic Menu Builder
Hello folks,
While on the plane back home I decided to write another little blog post. This time I will be showing you a nice class I came up with for x64dbg to manage menu items.
The problem
As with every abstraction it starts with a problem you are trying to solve. In this case the problem was code duplication and general tediousness with the construction of the context (right click) menus in x64dbg.
The general idea of Qt is great. From my understanding, every context menu is a QMenu
with a bunch of QAction
or other QMenu
items in it. When a user right-clicks in the widget a signal will be emitted and the widget can connect to the signal, construct the QMenu
and ‘execute’ the menu on the mouse position. Each QAction
has a signal called triggered()
which you can connect to a slot in your widget to handle the click event.
If there is no variation in the menu everything works perfectly fine. You just create all the actions, menus and connections in the constructor and store the final QMenu
item in the class. Then when you need the menu you do mMenu->exec(position)
and you are done with it.
In x64dbg the menus are based on the context, so the static approach doesn’t work. What we did was create and connect all the QAction
items in the constructor and then dynamically create the menu. What this did was create a lot of fragmentation. All the actions had to be declared in the header, the slots for the actions had to be declared in the header and the source and adding a new action would result in a lot of code that looked exactly like this:
mFollowAddress = new QAction("&Follow in Disassembler", this);
connect(mFollowAddress, SIGNAL(triggered()), this, SLOT(followAddress()));
For actions with a shortcut and an icon it was even worse:
mToggleBreakpoint = new QAction("Toggle Breakpoint", this);
mToggleBreakpoint->setShortcutContext(Qt::WidgetShortcut);
mToggleBreakpoint->setIcon(QIcon(":/images/icons/breakpoint.png"));
addAction(mToggleBreakpoint);
connect(mToggleBreakpoint, SIGNAL(triggered()), this, SLOT(toggleBreakpoint()));
Shortcuts also require setting the actual shortcut in a dedicated slot called refreshShortcutsSlot()
which is connected to the settings dialog so shortcuts are updated when the user updates the settings:
void ReferenceView::refreshShortcutsSlot()
{
mToggleBreakpoint->setShortcut(ConfigShortcut("ActionToggleBreakpoint"));
mToggleBookmark->setShortcut(ConfigShortcut("ActionToggleBookmark"));
}
Finally the menu is created in contextMenuEvent
like this:
if(!DbgMemIsValidReadPtr(addr))
return;
wMenu->addAction(mFollowAddress);
wMenu->addAction(mFollowDumpAddress);
if(apiAddressFromString(mCurList->getCellContent(mCurList->getInitialSelection(), 1)))
wMenu->addAction(mFollowApiAddress);
wMenu->addSeparator();
wMenu->addAction(mToggleBreakpoint);
wMenu->addAction(mToggleBookmark);
As you can imagine, adding an action with an icon, a shortcut and some context-dependent behaviour was a very tedious process and this needed to change.
MenuBuilder
Part of the solution is a MenuBuilder
class. This is a recursive datatype (like QMenu
) but it lazily builds the menu, which allows for proper context-awareness.
To achieve context-awareness, each QAction/QMenu/MenuBuilder
you add to a MenuBuilder
is paired with an std::function
. If the callback returns true, the item is added to the final QMenu
, otherwise it is ommitted. This allows for constructs like this:
mBuilder->addAction(followAction, [this](QMenu* menu)
{ //only add followAction if the selected address is readable.
return DbgMemIsValidReadPtr(this->selectedAddress());
});
The followAction
will only be added to the final QMenu
if the currently selected address is a valid memory address. This is a huge save in code, the menu creation slot can be replaced with:
QMenu menu;
mBuilder->build(&menu);
menu.exec(pos);
There are some extra features (like using the menu
parameter of the lambda to add extra actions to the final QMenu
, but if you want more details, read the code here.
Actions
The next problem to solve is the creation of the QAction
and QMenu
items. The solution was to create a few simple helper methods in the base class (AbstractTableView
):
template<typename T>
inline QAction* makeAction(const QString & text, T slot)
{
return connectAction(new QAction(text, this), slot);
}
inline QAction* connectAction(QAction* action, const char* slot)
{
connect(action, SIGNAL(triggered(bool)), this, slot);
return action;
}
inline QAction* connectAction(QAction* action, QActionLambda::TriggerCallback callback)
{
auto lambda = new QActionLambda(action->parent(), callback);
connect(action, SIGNAL(triggered(bool)), lambda, SLOT(triggeredSlot()));
return action;
}
The makeAction
uses a template because I added lambda support to the actions. This is not in Qt 4 and rather simple to implemented so I decided to add it:
class QActionLambda : public QObject
{
Q_OBJECT
public:
typedef std::function<void()> TriggerCallback;
QActionLambda(QObject* parent, TriggerCallback callback)
: QObject(parent),
_callback(callback)
{
}
public slots:
void triggeredSlot()
{
if(_callback)
_callback();
}
private:
TriggerCallback _callback;
};
Now to create an action you’d write:
makeAction("Selection (&No Bytes)", SLOT(copySelectionNoBytesSlot()))
And similarly an action with shortcut and icon:
makeShortcutAction(QIcon(":/icons/images/highlight.png"), "&Highlighting mode", SLOT(enableHighlightingModeSlot()), "ActionHighlightingMode")
Final words
I guess that’s about it for this blog post. If you want to see what the final menu creation code looks like, check out the code here. For reference, the old code is available here, as you can tell it is a great improvement.
Finally, I know reader interaction has been practically non-existent on this blog so far, however it would be nice if you could send me parts of x64dbg you’d like to get insight in development-wise. Any other topics (reversing/programming) related are also welcome!
Greetings,
Duncan