[+] 添加特征码识别替换的版本范围判定
This commit is contained in:
210
RevokeMsgPatcher/Matcher/BoyerMooreMatcher.cs
Normal file
210
RevokeMsgPatcher/Matcher/BoyerMooreMatcher.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
namespace RevokeMsgPatcher.Matcher
|
||||
{
|
||||
public class BoyerMooreMatcher
|
||||
{
|
||||
private static int AlphabetSize = 256;
|
||||
|
||||
private static int Max(int a, int b) { return (a > b) ? a : b; }
|
||||
|
||||
static int[] PreprocessToBuildBadCharactorHeuristic(byte[] pattern)
|
||||
{
|
||||
int m = pattern.Length;
|
||||
int[] badCharactorShifts = new int[AlphabetSize];
|
||||
|
||||
for (int i = 0; i < AlphabetSize; i++)
|
||||
{
|
||||
//badCharactorShifts[i] = -1;
|
||||
badCharactorShifts[i] = m;
|
||||
}
|
||||
|
||||
// fill the actual value of last occurrence of a character
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
//badCharactorShifts[(int)pattern[i]] = i;
|
||||
badCharactorShifts[(int)pattern[i]] = m - 1 - i;
|
||||
}
|
||||
|
||||
return badCharactorShifts;
|
||||
}
|
||||
|
||||
static int[] PreprocessToBuildGoodSuffixHeuristic(byte[] pattern)
|
||||
{
|
||||
int m = pattern.Length;
|
||||
int[] goodSuffixShifts = new int[m];
|
||||
int[] suffixLengthArray = GetSuffixLengthArray(pattern);
|
||||
|
||||
for (int i = 0; i < m; ++i)
|
||||
{
|
||||
goodSuffixShifts[i] = m;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for (int i = m - 1; i >= -1; --i)
|
||||
{
|
||||
if (i == -1 || suffixLengthArray[i] == i + 1)
|
||||
{
|
||||
for (; j < m - 1 - i; ++j)
|
||||
{
|
||||
if (goodSuffixShifts[j] == m)
|
||||
{
|
||||
goodSuffixShifts[j] = m - 1 - i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m - 1; ++i)
|
||||
{
|
||||
goodSuffixShifts[m - 1 - suffixLengthArray[i]] = m - 1 - i;
|
||||
}
|
||||
|
||||
return goodSuffixShifts;
|
||||
}
|
||||
|
||||
static int[] GetSuffixLengthArray(byte[] pattern)
|
||||
{
|
||||
int m = pattern.Length;
|
||||
int[] suffixLengthArray = new int[m];
|
||||
|
||||
int f = 0, g = 0, i = 0;
|
||||
|
||||
suffixLengthArray[m - 1] = m;
|
||||
|
||||
g = m - 1;
|
||||
for (i = m - 2; i >= 0; --i)
|
||||
{
|
||||
if (i > g && suffixLengthArray[i + m - 1 - f] < i - g)
|
||||
{
|
||||
suffixLengthArray[i] = suffixLengthArray[i + m - 1 - f];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < g)
|
||||
{
|
||||
g = i;
|
||||
}
|
||||
f = i;
|
||||
|
||||
// find different preceded character suffix
|
||||
while (g >= 0 && pattern[g] == pattern[g + m - 1 - f])
|
||||
{
|
||||
--g;
|
||||
}
|
||||
suffixLengthArray[i] = f - g;
|
||||
}
|
||||
}
|
||||
|
||||
return suffixLengthArray;
|
||||
}
|
||||
|
||||
public static bool TryMatch(byte[] text, byte[] pattern, out int firstShift)
|
||||
{
|
||||
firstShift = -1;
|
||||
int n = text.Length;
|
||||
int m = pattern.Length;
|
||||
int s = 0; // s is shift of the pattern with respect to text
|
||||
int j = 0;
|
||||
|
||||
// fill the bad character and good suffix array by preprocessing
|
||||
int[] badCharShifts = PreprocessToBuildBadCharactorHeuristic(pattern);
|
||||
int[] goodSuffixShifts = PreprocessToBuildGoodSuffixHeuristic(pattern);
|
||||
|
||||
while (s <= (n - m))
|
||||
{
|
||||
// starts matching from the last character of the pattern
|
||||
j = m - 1;
|
||||
|
||||
// keep reducing index j of pattern while characters of
|
||||
// pattern and text are matching at this shift s
|
||||
while (j >= 0 && pattern[j] == text[s + j])
|
||||
{
|
||||
j--;
|
||||
}
|
||||
|
||||
// if the pattern is present at current shift, then index j
|
||||
// will become -1 after the above loop
|
||||
if (j < 0)
|
||||
{
|
||||
firstShift = s;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// shift the pattern so that the bad character in text
|
||||
// aligns with the last occurrence of it in pattern. the
|
||||
// max function is used to make sure that we get a positive
|
||||
// shift. We may get a negative shift if the last occurrence
|
||||
// of bad character in pattern is on the right side of the
|
||||
// current character.
|
||||
//s += Max(1, j - badCharShifts[(int)text[s + j]]);
|
||||
// now, compare bad char shift and good suffix shift to find best
|
||||
s += Max(goodSuffixShifts[j], badCharShifts[(int)text[s + j]] - (m - 1) + j);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int[] MatchAll(byte[] text, byte[] pattern)
|
||||
{
|
||||
int n = text.Length;
|
||||
int m = pattern.Length;
|
||||
int s = 0; // s is shift of the pattern with respect to text
|
||||
int j = 0;
|
||||
int[] shiftIndexes = new int[n - m + 1];
|
||||
int c = 0;
|
||||
|
||||
// fill the bad character and good suffix array by preprocessing
|
||||
int[] badCharShifts = PreprocessToBuildBadCharactorHeuristic(pattern);
|
||||
int[] goodSuffixShifts = PreprocessToBuildGoodSuffixHeuristic(pattern);
|
||||
|
||||
while (s <= (n - m))
|
||||
{
|
||||
// starts matching from the last character of the pattern
|
||||
j = m - 1;
|
||||
|
||||
// keep reducing index j of pattern while characters of
|
||||
// pattern and text are matching at this shift s
|
||||
while (j >= 0 && pattern[j] == text[s + j])
|
||||
{
|
||||
j--;
|
||||
}
|
||||
|
||||
// if the pattern is present at current shift, then index j
|
||||
// will become -1 after the above loop
|
||||
if (j < 0)
|
||||
{
|
||||
shiftIndexes[c] = s;
|
||||
c++;
|
||||
|
||||
// shift the pattern so that the next character in text
|
||||
// aligns with the last occurrence of it in pattern.
|
||||
// the condition s+m < n is necessary for the case when
|
||||
// pattern occurs at the end of text
|
||||
//s += (s + m < n) ? m - badCharShifts[(int)text[s + m]] : 1;
|
||||
s += goodSuffixShifts[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// shift the pattern so that the bad character in text
|
||||
// aligns with the last occurrence of it in pattern. the
|
||||
// max function is used to make sure that we get a positive
|
||||
// shift. We may get a negative shift if the last occurrence
|
||||
// of bad character in pattern is on the right side of the
|
||||
// current character.
|
||||
//s += Max(1, j - badCharShifts[(int)text[s + j]]);
|
||||
// now, compare bad char shift and good suffix shift to find best
|
||||
s += Max(goodSuffixShifts[j], badCharShifts[(int)text[s + j]] - (m - 1) + j);
|
||||
}
|
||||
}
|
||||
|
||||
int[] shifts = new int[c];
|
||||
for (int y = 0; y < c; y++)
|
||||
{
|
||||
shifts[y] = shiftIndexes[y];
|
||||
}
|
||||
|
||||
return shifts;
|
||||
}
|
||||
}
|
||||
}
|
||||
133
RevokeMsgPatcher/Matcher/FuzzyMatcher.cs
Normal file
133
RevokeMsgPatcher/Matcher/FuzzyMatcher.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RevokeMsgPatcher.Matcher
|
||||
{
|
||||
/// <summary>
|
||||
/// 对16进制数据进行通配符查找
|
||||
/// </summary>
|
||||
public class FuzzyMatcher
|
||||
{
|
||||
public const byte wildcard = 0x3F; // 通配符
|
||||
|
||||
/// <summary>
|
||||
/// 通配符匹配所有符合查找串的位置
|
||||
/// </summary>
|
||||
/// <param name="content">被查找对象</param>
|
||||
/// <param name="pattern">查找串</param>
|
||||
/// <returns></returns>
|
||||
public static int[] MatchAll(byte[] content, byte[] pattern)
|
||||
{
|
||||
byte[] head = GetHead(pattern);
|
||||
int[] indexs = BoyerMooreMatcher.MatchAll(content, head);
|
||||
// 头串和查找串相同则直接返回,不同则继续判断是否符合查询串
|
||||
if (head.Length == pattern.Length)
|
||||
{
|
||||
return indexs;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<int> res = new List<int>();
|
||||
foreach (int index in indexs)
|
||||
{
|
||||
if (IsEqual(content, index, pattern))
|
||||
{
|
||||
res.Add(index);
|
||||
}
|
||||
}
|
||||
return res.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通配符匹配所有符合查找串的位置,并排除已经替换的情况
|
||||
/// </summary>
|
||||
/// <param name="content">被查找对象</param>
|
||||
/// <param name="searchBytes">查找串</param>
|
||||
/// <param name="replaceBytes">替换串</param>
|
||||
/// <returns></returns>
|
||||
public static int[] MatchNotReplaced(byte[] content, byte[] searchBytes, byte[] replaceBytes)
|
||||
{
|
||||
byte[] head = GetHead(searchBytes);
|
||||
int[] indexs = BoyerMooreMatcher.MatchAll(content, head);
|
||||
// 头串和查找串相同则直接返回,不同则继续判断是否符合查询串
|
||||
List<int> res = new List<int>();
|
||||
if (head.Length != searchBytes.Length)
|
||||
{
|
||||
foreach (int index in indexs)
|
||||
{
|
||||
if (IsEqual(content, index, searchBytes))
|
||||
{
|
||||
res.Add(index);
|
||||
}
|
||||
}
|
||||
indexs = res.ToArray();
|
||||
}
|
||||
// 判断是否与替换串相同
|
||||
res = new List<int>();
|
||||
foreach (int index in indexs)
|
||||
{
|
||||
if (!IsEqual(content, index, replaceBytes))
|
||||
{
|
||||
res.Add(index);
|
||||
}
|
||||
}
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取头串
|
||||
/// </summary>
|
||||
/// <param name="whole">完整查找串</param>
|
||||
/// <returns></returns>
|
||||
private static byte[] GetHead(byte[] whole)
|
||||
{
|
||||
int len = whole.Length;
|
||||
for (int i = 0; i < whole.Length; i++)
|
||||
{
|
||||
if (whole[i] == wildcard)
|
||||
{
|
||||
len = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (len == 0)
|
||||
{
|
||||
throw new Exception("不正确的通配符位置!");
|
||||
}
|
||||
return whole.Take(len).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确认整个查找串是否匹配
|
||||
/// </summary>
|
||||
/// <param name="content">被查找对象</param>
|
||||
/// <param name="start">头串匹配位置</param>
|
||||
/// <param name="whole">完整查找串</param>
|
||||
/// <returns></returns>
|
||||
private static bool IsEqual(byte[] content, int start, byte[] whole)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < whole.Length; i++)
|
||||
{
|
||||
if (whole[i] == wildcard)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (content[start + i] != whole[i])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == whole.Length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,40 @@ namespace RevokeMsgPatcher.Modifier
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断版本是否处于版本范围,特殊版本的可以重载此方法
|
||||
/// </summary>
|
||||
/// <param name="version">当前版本</param>
|
||||
/// <param name="start">起始版本</param>
|
||||
/// <param name="end">结束版本,为空为不限制</param>
|
||||
/// <returns></returns>
|
||||
public bool IsInVersionRange(string version, string start, string end)
|
||||
{
|
||||
try
|
||||
{
|
||||
int v = Convert.ToInt32(version.Replace(".", ""));
|
||||
int s = Convert.ToInt32(start.Replace(".", ""));
|
||||
int e = 0;
|
||||
if (string.IsNullOrEmpty(end))
|
||||
{
|
||||
e = int.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = Convert.ToInt32(end.Replace(".", ""));
|
||||
}
|
||||
if (v >= s && v <= e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("判断版本范围时出错:" + e.Message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// a.初始化修改器
|
||||
/// </summary>
|
||||
@@ -115,6 +149,17 @@ namespace RevokeMsgPatcher.Modifier
|
||||
}
|
||||
}
|
||||
|
||||
// 多个版本范围,匹配通用的补丁替换方式
|
||||
foreach (CommonModifyInfo commonModifyInfo in config.FileCommonModifyInfos[editor.FileName])
|
||||
{
|
||||
// editor.FileVersion 在 StartVersion 和 EndVersion 之间
|
||||
if (IsInVersionRange(editor.FileVersion, commonModifyInfo.StartVersion, commonModifyInfo.EndVersion))
|
||||
{
|
||||
editor.FileCommonModifyInfo = commonModifyInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 补丁前SHA1匹配上,肯定是正确的dll
|
||||
if (matchingSHA1Before != null)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace RevokeMsgPatcher.Modifier
|
||||
|
||||
public string FileBakPath { get; set; }
|
||||
|
||||
private string fileReplacedPath;
|
||||
|
||||
private string version;
|
||||
public string FileVersion
|
||||
{
|
||||
@@ -45,27 +47,48 @@ namespace RevokeMsgPatcher.Modifier
|
||||
|
||||
public TargetInfo FileTargetInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通过比对SHA1得到的特定版本的修改信息
|
||||
/// </summary>
|
||||
public ModifyInfo FileModifyInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通过比对版本范围得到的通用查找替换的修改信息
|
||||
/// </summary>
|
||||
public CommonModifyInfo FileCommonModifyInfo { get; set; }
|
||||
|
||||
public FileHexEditor(string installPath, TargetInfo target)
|
||||
{
|
||||
FileTargetInfo = target.Clone();
|
||||
FileName = FileTargetInfo.Name;
|
||||
FilePath = Path.Combine(installPath, FileTargetInfo.RelativePath);
|
||||
FileBakPath = FilePath + ".h.bak";
|
||||
fileReplacedPath = FilePath + ".h.process";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 备份
|
||||
/// </summary>
|
||||
public void Backup()
|
||||
{
|
||||
File.Copy(FilePath, FileBakPath, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打补丁
|
||||
/// 优先使用特定的补丁信息(存在对应SHA1信息)
|
||||
/// 不存在补丁信息,使用通用版本替换方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Patch()
|
||||
{
|
||||
FileUtil.EditMultiHex(FilePath, FileModifyInfo.Changes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 还原
|
||||
/// </summary>
|
||||
public void Restore()
|
||||
{
|
||||
File.Copy(FileBakPath, FilePath, true);
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
<Compile Include="FormMain.Designer.cs">
|
||||
<DependentUpon>FormMain.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Matcher\BoyerMooreMatcher.cs" />
|
||||
<Compile Include="Matcher\FuzzyMatcher.cs" />
|
||||
<Compile Include="Model\App.cs" />
|
||||
<Compile Include="Model\Bag.cs" />
|
||||
<Compile Include="Model\Change.cs" />
|
||||
|
||||
Reference in New Issue
Block a user