implemented key extraction from executable resources for encrypted INT archives.

This commit is contained in:
morkt 2015-11-20 01:01:25 +04:00
parent 5a5c10d91a
commit 33e83b196d
7 changed files with 276 additions and 24 deletions

View File

@ -27,9 +27,9 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.ComponentModel;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using Simias.Encryption; using Simias.Encryption;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using GameRes.Formats.Strings; using GameRes.Formats.Strings;
@ -346,5 +346,82 @@ namespace GameRes.Formats.CatSystem
} }
} }
} }
/// <summary>
/// Parse certain executable resources for encryption passphrase.
/// Returns null if no passphrase found.
/// </summary>
public static string GetPassFromExe (string filename)
{
var exe = NativeMethods.LoadLibraryEx (filename, IntPtr.Zero, 0x20); // LOAD_LIBRARY_AS_IMAGE_RESOURCE
if (IntPtr.Zero == exe)
throw new Win32Exception (Marshal.GetLastWin32Error());
try
{
var code = GetResource (exe, "DATA", "V_CODE2");
if (null == code || code.Length < 8)
return null;
var key = GetResource (exe, "KEY", "KEY_CODE");
if (null != key)
{
for (int i = 0; i < key.Length; ++i)
key[i] ^= 0xCD;
}
else
{
key = Encoding.ASCII.GetBytes ("windmill");
}
var blowfish = new Blowfish (key);
blowfish.Decipher (code, code.Length/8*8);
int length = Array.IndexOf<byte> (code, 0);
if (-1 == length)
length = code.Length;
return Encodings.cp932.GetString (code, 0, length);
}
finally
{
NativeMethods.FreeLibrary (exe);
}
}
static byte[] GetResource (IntPtr exe, string name, string type)
{
var res = NativeMethods.FindResource (exe, name, type);
if (IntPtr.Zero == res)
return null;
var glob = NativeMethods.LoadResource (exe, res);
if (IntPtr.Zero == glob)
return null;
uint size = NativeMethods.SizeofResource (exe, res);
var src = NativeMethods.LockResource (glob);
if (IntPtr.Zero == src)
return null;
var dst = new byte[size];
Marshal.Copy (src, dst, 0, dst.Length);
return dst;
}
}
static internal class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static internal extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hReservedNull, uint dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static internal extern bool FreeLibrary (IntPtr hModule);
[DllImport( "kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static internal extern IntPtr FindResource (IntPtr hModule, string lpName, string lpType);
[DllImport("Kernel32.dll", SetLastError = true)]
static internal extern IntPtr LoadResource (IntPtr hModule, IntPtr hResource);
[DllImport("Kernel32.dll", SetLastError = true)]
static internal extern uint SizeofResource (IntPtr hModule, IntPtr hResource);
[DllImport("kernel32.dll")]
static internal extern IntPtr LockResource (IntPtr hResData);
} }
} }

View File

@ -1,10 +1,21 @@
<Grid x:Class="GameRes.Formats.GUI.WidgetINT" <StackPanel x:Class="GameRes.Formats.GUI.WidgetINT"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:GameRes.Formats.Strings" xmlns:s="clr-namespace:GameRes.Formats.Strings"
xmlns:fmt="clr-namespace:GameRes.Formats.CatSystem" xmlns:fmt="clr-namespace:GameRes.Formats.CatSystem"
xmlns:local="clr-namespace:GameRes.Formats.GUI" xmlns:local="clr-namespace:GameRes.Formats.GUI"
MaxWidth="260"> MaxWidth="280" Orientation="Vertical">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Name="ExeMessage" Grid.Column="0" Text="{x:Static s:arcStrings.INTMessage1}" TextWrapping="Wrap" Margin="0,0,10,10"/>
<Button Content="{x:Static s:arcStrings.INTExeButton}" Grid.Column="1" Grid.Row="0" Width="75" Height="25"
HorizontalAlignment="Left" Margin="0,0,0,10" Click="Check_Click"/>
</Grid>
<TextBlock Grid.Column="0" Text="{x:Static s:arcStrings.INTMessage2}" Margin="0,0,0,10" TextWrapping="Wrap"/>
<Grid>
<Grid.Resources> <Grid.Resources>
<local:KeyConverter x:Key="keyConverter"/> <local:KeyConverter x:Key="keyConverter"/>
<Style TargetType="{x:Type TextBox}"> <Style TargetType="{x:Type TextBox}">
@ -21,13 +32,13 @@
<ColumnDefinition MinWidth="130" Width="*"/> <ColumnDefinition MinWidth="130" Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition/> <RowDefinition/>
<RowDefinition/> <RowDefinition/>
<RowDefinition/> <RowDefinition/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Content="{x:Static s:arcStrings.INTLabelNumericKey}" Target="{Binding ElementName=Passkey}" <Label Content="{x:Static s:arcStrings.INTLabelNumericKey}" Target="{Binding ElementName=Passkey}"
Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right"/> Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right"/>
<TextBox Name="Passkey" Grid.Column="1" Grid.Row="0" Margin="0,3,0,3"> <TextBox Name="Passkey" Grid.Column="1" Grid.Row="2" Margin="0,3,0,3">
<TextBox.Text> <TextBox.Text>
<Binding Path="Key" Converter="{StaticResource keyConverter}" UpdateSourceTrigger="PropertyChanged"> <Binding Path="Key" Converter="{StaticResource keyConverter}" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules> <Binding.ValidationRules>
@ -51,9 +62,10 @@
<TextBox Name="Passphrase" Grid.Column="1" Grid.Row="1" Margin="0,3,0,3" <TextBox Name="Passphrase" Grid.Column="1" Grid.Row="1" Margin="0,3,0,3"
Text="{Binding Path=Password}"/> Text="{Binding Path=Password}"/>
<Label Content="{x:Static s:arcStrings.LabelScheme}" Target="{Binding ElementName=EncScheme}" <Label Content="{x:Static s:arcStrings.LabelScheme}" Target="{Binding ElementName=EncScheme}"
Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right"/> Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right"/>
<ComboBox Name="EncScheme" Grid.Column="1" Grid.Row="2" Margin="0,3,0,0" <ComboBox Name="EncScheme" Grid.Column="1" Grid.Row="0" Margin="0,3,0,0"
ItemsSource="{Binding Source={x:Static fmt:IntOpener.KnownSchemes}, Path=Keys, Mode=OneWay}" ItemsSource="{Binding Source={x:Static fmt:IntOpener.KnownSchemes}, Path=Keys, Mode=OneWay}"
Width="{Binding ElementName=Passkey, Path=ActualWidth}" Width="{Binding ElementName=Passkey, Path=ActualWidth}"
SelectedValue="{Binding Path=Scheme}"/> SelectedValue="{Binding Path=Scheme}"/>
</Grid> </Grid>
</StackPanel>

View File

@ -1,15 +1,42 @@
using System; //! \brief Code-behind for INT encryption query widget.
//
// Copyright (C) 2014-2015 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.Globalization; using System.Globalization;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using GameRes.Formats.CatSystem; using GameRes.Formats.CatSystem;
using GameRes.Formats.Strings;
using Microsoft.Win32;
using System.Windows;
using System.IO;
namespace GameRes.Formats.GUI namespace GameRes.Formats.GUI
{ {
/// <summary> /// <summary>
/// Interaction logic for WidgetINT.xaml /// Interaction logic for WidgetINT.xaml
/// </summary> /// </summary>
public partial class WidgetINT : Grid public partial class WidgetINT : StackPanel
{ {
public WidgetINT () public WidgetINT ()
{ {
@ -51,6 +78,36 @@ namespace GameRes.Formats.GUI
} }
} }
} }
private void Check_Click (object sender, System.Windows.RoutedEventArgs e)
{
var dlg = new OpenFileDialog {
CheckFileExists = true,
CheckPathExists = true,
Multiselect = false,
Title = arcStrings.INTChooseExe,
Filter = arcStrings.INTExeFiles+"|*.exe",
FilterIndex = 1,
InitialDirectory = Directory.GetCurrentDirectory(),
};
if (!dlg.ShowDialog (Window.GetWindow (this)).Value)
return;
try
{
var pass = IntOpener.GetPassFromExe (dlg.FileName);
if (null != pass)
{
this.ExeMessage.Text = arcStrings.INTMessage1;
Passphrase.Text = pass;
}
else
this.ExeMessage.Text = string.Format (arcStrings.INTKeyNotFound, Path.GetFileName (dlg.FileName));
}
catch (Exception X)
{
this.ExeMessage.Text = X.Message;
}
}
} }
[ValueConversion(typeof(uint?), typeof(string))] [ValueConversion(typeof(uint?), typeof(string))]

View File

@ -178,6 +178,15 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to Choose game executable file.
/// </summary>
public static string INTChooseExe {
get {
return ResourceManager.GetString("INTChooseExe", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Encrypted archives creation is not implemented.. /// Looks up a localized string similar to Encrypted archives creation is not implemented..
/// </summary> /// </summary>
@ -196,6 +205,33 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to Check EXE.
/// </summary>
public static string INTExeButton {
get {
return ResourceManager.GetString("INTExeButton", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Executable Files.
/// </summary>
public static string INTExeFiles {
get {
return ResourceManager.GetString("INTExeFiles", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Key not found within {0}..
/// </summary>
public static string INTKeyNotFound {
get {
return ResourceManager.GetString("INTKeyNotFound", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Numeric key should be a 32-bit hexadecimal integer. /// Looks up a localized string similar to Numeric key should be a 32-bit hexadecimal integer.
/// </summary> /// </summary>
@ -215,9 +251,25 @@ namespace GameRes.Formats.Strings {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Archive directory is encrypted. /// Looks up a localized string similar to Press &quot;Check EXE&quot; button to look for key within game executable file..
///Enter archive encryption key or choose /// </summary>
///predefined encryption scheme.. public static string INTMessage1 {
get {
return ResourceManager.GetString("INTMessage1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Alternatively, enter archive encryption key or choose one of the predefined encryption schemes..
/// </summary>
public static string INTMessage2 {
get {
return ResourceManager.GetString("INTMessage2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Archive directory is encrypted..
/// </summary> /// </summary>
public static string INTNotice { public static string INTNotice {
get { get {

View File

@ -161,9 +161,7 @@
<value>숫자 값</value> <value>숫자 값</value>
</data> </data>
<data name="INTNotice" xml:space="preserve"> <data name="INTNotice" xml:space="preserve">
<value>아카이브 폴더가 암호화됨. <value>아카이브 폴더가 암호화됨.</value>
아카이브 암호값을 입력하거나
미리정의된 암호체계를 고르세요.</value>
</data> </data>
<data name="KogadoDescription" xml:space="preserve"> <data name="KogadoDescription" xml:space="preserve">
<value>Kogado 게임 엔진 리소스 아카이브</value> <value>Kogado 게임 엔진 리소스 아카이브</value>
@ -339,4 +337,28 @@
암호값 추측이 실패하였습니다. 암호값 추측이 실패하였습니다.
적절한 암호체계를 선택하세요.</value> 적절한 암호체계를 선택하세요.</value>
</data> </data>
<data name="INTChooseExe" xml:space="preserve">
<value>Choose game executable file</value>
<comment>translation pending</comment>
</data>
<data name="INTExeButton" xml:space="preserve">
<value>Check EXE</value>
<comment>translation pending</comment>
</data>
<data name="INTExeFiles" xml:space="preserve">
<value>Executable Files</value>
<comment>translation pending</comment>
</data>
<data name="INTKeyNotFound" xml:space="preserve">
<value>Key not found within {0}.</value>
<comment>translation pending</comment>
</data>
<data name="INTMessage1" xml:space="preserve">
<value>Press "Check EXE" button to look for key within game executable file.</value>
<comment>translation pending</comment>
</data>
<data name="INTMessage2" xml:space="preserve">
<value>Alternatively, enter archive encryption key or choose one of the predefined encryption schemes.</value>
<comment>translation pending</comment>
</data>
</root> </root>

View File

@ -161,9 +161,7 @@ Choose appropriate encryption scheme.</value>
<value>Numeric key</value> <value>Numeric key</value>
</data> </data>
<data name="INTNotice" xml:space="preserve"> <data name="INTNotice" xml:space="preserve">
<value>Archive directory is encrypted. <value>Archive directory is encrypted.</value>
Enter archive encryption key or choose
predefined encryption scheme.</value>
</data> </data>
<data name="KogadoDescription" xml:space="preserve"> <data name="KogadoDescription" xml:space="preserve">
<value>Kogado game engine resource archive</value> <value>Kogado game engine resource archive</value>
@ -342,4 +340,22 @@ Choose appropriate encryption scheme.</value>
<data name="QLIEDefaultScheme" xml:space="preserve"> <data name="QLIEDefaultScheme" xml:space="preserve">
<value>Use default encryption scheme</value> <value>Use default encryption scheme</value>
</data> </data>
<data name="INTChooseExe" xml:space="preserve">
<value>Choose game executable file</value>
</data>
<data name="INTExeButton" xml:space="preserve">
<value>Check EXE</value>
</data>
<data name="INTExeFiles" xml:space="preserve">
<value>Executable Files</value>
</data>
<data name="INTMessage1" xml:space="preserve">
<value>Press "Check EXE" button to look for key within game executable file.</value>
</data>
<data name="INTMessage2" xml:space="preserve">
<value>Alternatively, enter archive encryption key or choose one of the predefined encryption schemes.</value>
</data>
<data name="INTKeyNotFound" xml:space="preserve">
<value>Key not found within {0}.</value>
</data>
</root> </root>

View File

@ -148,19 +148,35 @@
<data name="DPKKeys" xml:space="preserve"> <data name="DPKKeys" xml:space="preserve">
<value>Ключи шифрования</value> <value>Ключи шифрования</value>
</data> </data>
<data name="INTChooseExe" xml:space="preserve">
<value>Выберите исполняемый файл</value>
</data>
<data name="INTCreationNotice" xml:space="preserve"> <data name="INTCreationNotice" xml:space="preserve">
<value>Создание зашифрованных архивов не реализовано.</value> <value>Создание зашифрованных архивов не реализовано.</value>
</data> </data>
<data name="INTExeButton" xml:space="preserve">
<value>Искать EXE</value>
</data>
<data name="INTExeFiles" xml:space="preserve">
<value>Исполняемые файлы</value>
</data>
<data name="INTKeyNotFound" xml:space="preserve">
<value>В файле {0} ключа не обнаружено.</value>
</data>
<data name="INTKeyRequirement" xml:space="preserve"> <data name="INTKeyRequirement" xml:space="preserve">
<value>Цифровой ключ должен быть 32-битным шестнадцатиричным числом</value> <value>Цифровой ключ должен быть 32-битным шестнадцатиричным числом</value>
</data> </data>
<data name="INTLabelNumericKey" xml:space="preserve"> <data name="INTLabelNumericKey" xml:space="preserve">
<value>Цифровой ключ</value> <value>Цифровой ключ</value>
</data> </data>
<data name="INTMessage1" xml:space="preserve">
<value>Нажмите кнопку "Искать EXE", чтобы попытаться найти ключ в исполняемом файле игры.</value>
</data>
<data name="INTMessage2" xml:space="preserve">
<value>Или же выберите один из известных методов шифрования, либо введите ключ вручную.</value>
</data>
<data name="INTNotice" xml:space="preserve"> <data name="INTNotice" xml:space="preserve">
<value>Заголовок архива зашифрован. <value>Оглавление архива зашифровано.</value>
Введите ключ шифрования или выберите
один из предопределённых вариантов.</value>
</data> </data>
<data name="ISFIgnoreEncryption" xml:space="preserve"> <data name="ISFIgnoreEncryption" xml:space="preserve">
<value>Игнорировать шифрование</value> <value>Игнорировать шифрование</value>