mirror of
https://github.com/atom0s/Steamless.git
synced 2024-12-19 23:07:41 +01:00
411f4f711c
PE64: Replace manual PE checksum calculations with Win32 API call instead. I was trying to avoid using Win32 API calls in this project for the most part, however, this calculation has multiple conditions that can alter its result. Steamless was only producing proper checksums about 40% of the time as a result of this. Instead, the project will now use the system API call 'MapFileAndCheckSum' to calculate the checksum properly.
181 lines
No EOL
7 KiB
C#
181 lines
No EOL
7 KiB
C#
/**
|
|
* Steamless - Copyright (c) 2015 - 2022 atom0s [atom0s@live.com]
|
|
*
|
|
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to
|
|
* Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
|
*
|
|
* By using Steamless, you agree to the above license and its terms.
|
|
*
|
|
* Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were
|
|
* made. You must do so in any reasonable manner, but not in any way that suggests the licensor
|
|
* endorses you or your use.
|
|
*
|
|
* Non-Commercial - You may not use the material (Steamless) for commercial purposes.
|
|
*
|
|
* No-Derivatives - If you remix, transform, or build upon the material (Steamless), you may not distribute the
|
|
* modified material. You are, however, allowed to submit the modified works back to the original
|
|
* Steamless project in attempt to have it added to the original project.
|
|
*
|
|
* You may not apply legal terms or technological measures that legally restrict others
|
|
* from doing anything the license permits.
|
|
*
|
|
* No warranties are given.
|
|
*/
|
|
|
|
namespace Steamless.API.PE32
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
|
|
public class Pe32Helpers
|
|
{
|
|
/// <summary>
|
|
/// Converts a byte array to the given structure type.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="data"></param>
|
|
/// <param name="offset"></param>
|
|
/// <returns></returns>
|
|
public static T GetStructure<T>(byte[] data, int offset = 0)
|
|
{
|
|
var size = Marshal.SizeOf(typeof(T));
|
|
var ptr = Marshal.AllocHGlobal(size);
|
|
|
|
// Size can land up being bigger than our buffer..
|
|
if (size > data.Length)
|
|
size = Math.Min(data.Length, Math.Max(0, size));
|
|
|
|
Marshal.Copy(data, offset, ptr, size);
|
|
var obj = (T)Marshal.PtrToStructure(ptr, typeof(T));
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
return obj;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the given object back to a byte array.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="obj"></param>
|
|
/// <returns></returns>
|
|
public static byte[] GetStructureBytes<T>(T obj)
|
|
{
|
|
var size = Marshal.SizeOf(obj);
|
|
var data = new byte[size];
|
|
var ptr = Marshal.AllocHGlobal(size);
|
|
Marshal.StructureToPtr(obj, ptr, true);
|
|
Marshal.Copy(ptr, data, 0, size);
|
|
Marshal.FreeHGlobal(ptr);
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtains a section from the given file information.
|
|
/// </summary>
|
|
/// <param name="rawData"></param>
|
|
/// <param name="index"></param>
|
|
/// <param name="dosHeader"></param>
|
|
/// <param name="ntHeaders"></param>
|
|
/// <returns></returns>
|
|
public static NativeApi32.ImageSectionHeader32 GetSection(byte[] rawData, int index, NativeApi32.ImageDosHeader32 dosHeader, NativeApi32.ImageNtHeaders32 ntHeaders)
|
|
{
|
|
var sectionSize = Marshal.SizeOf(typeof(NativeApi32.ImageSectionHeader32));
|
|
var optionalHeaderOffset = Marshal.OffsetOf(typeof(NativeApi32.ImageNtHeaders32), "OptionalHeader").ToInt32();
|
|
var dataOffset = dosHeader.e_lfanew + optionalHeaderOffset + ntHeaders.FileHeader.SizeOfOptionalHeader;
|
|
|
|
return GetStructure<NativeApi32.ImageSectionHeader32>(rawData, dataOffset + (index * sectionSize));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the given files PE checksum value. (Path is assumed to be a 32bit PE file.)
|
|
/// </summary>
|
|
/// <param name="path"></param>
|
|
/// <returns></returns>
|
|
public static bool UpdateFileChecksum(string path)
|
|
{
|
|
// Obtain the proper checksum for the file..
|
|
var ret = NativeApi32.MapFileAndCheckSum(path, out uint HeaderSum, out uint Checksum);
|
|
if (ret != 0)
|
|
return false;
|
|
|
|
FileStream fStream = null;
|
|
var data = new byte[4];
|
|
|
|
try
|
|
{
|
|
// Open the file for reading/writing..
|
|
fStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
|
|
|
|
// Read the starting offset to the files NT headers..
|
|
fStream.Position = (int)Marshal.OffsetOf(typeof(NativeApi32.ImageDosHeader32), "e_lfanew");
|
|
fStream.Read(data, 0, 4);
|
|
|
|
var offset = BitConverter.ToUInt32(data, 0);
|
|
|
|
// Move to the files CheckSum position..
|
|
offset += 4 + (uint)Marshal.SizeOf(typeof(NativeApi32.ImageFileHeader32)) + (uint)Marshal.OffsetOf(typeof(NativeApi32.ImageOptionalHeader32), "CheckSum").ToInt32();
|
|
fStream.Position = offset;
|
|
|
|
// Overwrite the file checksum..
|
|
data = BitConverter.GetBytes(Checksum);
|
|
fStream.Write(data, 0, 4);
|
|
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
fStream?.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scans the given data for the given pattern.
|
|
///
|
|
/// Notes:
|
|
/// Patterns are assumed to be 2 byte hex values with spaces.
|
|
/// Wildcards are represented by ??.
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <param name="pattern"></param>
|
|
/// <returns></returns>
|
|
public static long FindPattern(byte[] data, string pattern)
|
|
{
|
|
try
|
|
{
|
|
// Trim the pattern from extra whitespace..
|
|
var trimPattern = pattern.Replace(" ", "").Trim();
|
|
|
|
// Convert the pattern to a byte array..
|
|
var patternMask = new List<bool>();
|
|
var patternData = Enumerable.Range(0, trimPattern.Length).Where(x => x % 2 == 0)
|
|
.Select(x =>
|
|
{
|
|
var bt = trimPattern.Substring(x, 2);
|
|
patternMask.Add(!bt.Contains('?'));
|
|
return bt.Contains('?') ? (byte)0 : Convert.ToByte(bt, 16);
|
|
}).ToArray();
|
|
|
|
// Scan the given data for our pattern..
|
|
for (var x = 0; x < data.Length; x++)
|
|
{
|
|
if (!patternData.Where((t, y) => patternMask[y] && t != data[x + y]).Any())
|
|
return (uint)x;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
catch
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
} |