Hello everyone, Today I'll provide you with a highly technical post.
大家好,今天我将为大家提供一个高度技术性的帖子。
Maybe someone with the technical expertise that can figure out the issue that I'm having will read this.
也许有技术专长的人能够找出我所遇到的问题,会看到这篇文章。
The problem
问题所在
In EFT 0.13.0.x, EFT introduced a modernized comminucation system for handling active connecions and making requests over the HTTPS and WSS protocols.
在EFT 0.13.0.x中,EFT引入了一个现代化的通信系统,用于处理主动连接和通过HTTPS和WSS协议提出请求。
This system assumes that general comminucation happens over HTTPS protocol.
这个系统假设一般的交流是通过HTTPS协议进行的。
Attempt 1: SSL certificate
尝试1:SSL证书
My intial idea was to generate a self-signed X509 certificate with SSL server privileges and then manually enroll the certificate:
我最初的想法是用SSL服务器权限生成一个自签名的X509证书,然后手动注册该证书:
using System;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace Haru.CertEnroll
{
class Program
{
static void Main(string[] args)
{
Load(“Haru SSL Authority”);
Console.ReadKey();
}
public static void Load(string subject)
{
using (var store = new X509Store(StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName, subject, false);
// remove expired certificates
if (certs.Count > 0 && certs[0].NotAfter < DateTime.Now.AddDays(1))
{
store.Certificates.Remove(certs[0]);
}
// generate new certificate
if (certs.Count == 0)
{
var start = DateTime.UtcNow.AddDays(–1);
var end = DateTime.UtcNow.AddMonths(1);
var cert = GenerateSelfSigned(subject, start, end);
store.Certificates.Add(cert);
}
}
}
private static X509Certificate2 GenerateSelfSigned(string name, DateTime start, DateTime end)
{
using (RSA rsa = RSA.Create(2048))
{
// allow multiple lookups
var builder = new SubjectAlternativeNameBuilder();
builder.AddIpAddress(IPAddress.Loopback);
builder.AddDnsName(“localhost”);
// create request for SSL server
var req = new CertificateRequest($”CN={name} SSL Authority”, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
{
CertificateExtensions = {
new X509BasicConstraintsExtension(false, false, 0, false),
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false),
new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(“1.3.6.1.5.5.7.3.1”) }, false),
builder.Build()
}
};
req.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
// create certificate with both public and private key
var cert = req.CreateSelfSigned(start, end);
// export certificate
var password = string.Empty;
var exportable = cert.Export(X509ContentType.Pfx, password);
return new X509Certificate2(exportable, password, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
}
}
}
}
The problem is that I could never bind it to netsh since I do not have access to EFT's appid.
问题是,我无法将其与netsh绑定,因为我无法访问EFT的appid。
I do not know another method of binding the certificate to a port on windows.
我不知道在WINDOWS上将证书绑定到一个端口的其他方法。
Attempt 2: Harmony transpile patch
尝试2:和声转码补丁
The second try was to modify the code instructions directly using a transpile patch.
第二次尝试是使用转译补丁直接修改代码指令。
The original obfuscated method is as follows:
原始的混淆方法如下:
// Token: 0x06002012 RID: 8210 RVA: 0x001BF6DC File Offset: 0x001BD8DC
public static \uE2c2 CreateFromLegacyParams(\uE2C3 legacyParams)
{
string[] array = legacyParams.Url.Replace(\uED30.\uE000(11698), “”).Split(new char[]
{
'/'
}, 2);
string backendName = array[0];
string backendMethod = \uED30.\uE000(31052) + array[1];
return new \uE2c2
{
// code stripped
// 剥离的代码
};
}
The resulting string of \uED30.\uE000(11698) is “https://”.
结果字符串 \uED30.\uE000(11698) 是 “https://”。
In order to fix it, we just need to replace the call for the value with our own:
为了解决这个问题,我们只需要用我们自己的值来取代对该值的调用:
protected static IEnumerable<CodeInstruction> Patch(ILGenerator generator, IEnumerable<CodeInstruction> instructions)
{
var codes = new List<CodeInstruction>(instructions);
// change instructions
codes[2] = new CodeInstruction(OpCodes.Nop);
codes[3] = new CodeInstruction(OpCodes.Ldstr, “http://”);
// apply changes
return codes.AsEnumerable();
}
Turns out I was wrong.
事实证明我错了.
[Error : Unity Log] InvalidProgramException:
Invalid IL code in DMD<CreateFromLegacyParams>?1322007296:_::CreateFromLegacyParams ():
IL_0040: call 0x0a000004
[Error : Unity Log] 无效的程序异常:
DMD中无效的IL代码<CreateFromLegacyParams>?1322007296:_::CreateFromLegacyParams ():
IL_0040: call 0x0a000004
The problem is that HarmonyX nor Mono.Cecil cannot restore the right type name due to usage of unknown UTF8 codes as type names.
问题是,由于使用未知的UTF8代码作为类型名称,HarmonyX和Mono.Cecil都不能恢复正确的类型名称。
Manually constructing the type through reflection also failed due to the type name issue.
由于类型名称问题,通过反射手动构建类型也失败了。
I need your help!
我需要你的帮助!
I need HARU to either patch out the code or to have the server work with HTTPS certificates.
我需要HARU来修补代码或让服务器与HTTPS证书一起工作。
Restriction is that only net472 code may be used, external libraries are permitted if I review their code to be maintainable.
限制是只能使用net472的代码,如果我审查他们的代码是可维护的,则允许使用外部库。
Appendix A: Full instructions of \uE2c2.CreateFromLegacyParams(\uE2C3 legacyParams)
附录A:完整的指示 \uE2c2.CreateFromLegacyParams(\uE2C3 legacyParams)
ldarg.0 NULL
ldfld string ::Url
ldc.i4 11698
call static string ::(int )
ldstr “”
callvirt string string::Replace(string oldValue, string newValue)
ldc.i4.1 NULL
newarr System.Char
dup NULL
ldc.i4.0 NULL
ldc.i4.s 47
stelem.i2 NULL
ldc.i4.2 NULL
callvirt string[] string::Split(char[] separator, int count)
br Label1
ldloc.0 NULL [Label3]
ldc.i4.0 NULL
ldelem.ref NULL
stloc.1 NULL
br Label2
stloc.0 NULL [Label1]
br Label3
ldc.i4 31052 [Label2]
call static string ::(int )
ldloc.0 NULL
ldc.i4.1 NULL
ldelem.ref NULL
call static string string::Concat(string str0, string str1)
stloc.2 NULL
newobj void ::.ctor()
dup NULL
ldloc.1 NULL
stfld string ::BackendName
dup NULL
ldloc.2 NULL
stfld string ::BackendMethod
dup NULL
ldnull NULL
stfld string[] ::FallbackBackendNames
dup NULL
ldarg.0 NULL
ldfld bool ::UseCache
stfld bool ::ShouldUseCache
dup NULL
ldarg.0 NULL
ldfld string ::CacheId
stfld string ::OverrideCacheId
dup NULL
ldarg.0 NULL
ldfld object ::DefaultObjectInCaseOfCache
stfld object ::DefaultObjectInCaseOfCache
dup NULL
ldarga.s 0
call object ::get_Params()
stfld object ::Params
dup NULL
ldarg.0 NULL
ldfld byte? ::Retries
stfld byte? ::Retries
dup NULL
ldarg.0 NULL
ldfld int ::TimeoutSeconds
ldc.i4.0 NULL
bgt Label4
call static int ::get_DefaultTimeoutSeconds()
br Label5
ldarg.0 NULL [Label4]
ldfld int ::TimeoutSeconds
stfld int ::TimeoutSeconds [Label5]
dup NULL
ldarg.0 NULL
ldfld ERequestHandlingMode ::HandlingMode
stfld ERequestHandlingMode ::HandlingMode
ret NULL
看天书听天问。。。。。。。。。。。。。。。。
I think I found a solution, but it's not without sacrifice. It looks like running the server embedded into EFT is causing me the headache, UnityEngine has many lmitiations in it's runtime which is causing me problems. Using a different HTTP server (STA's WebSocketSharp) and running the server standalone resolved the issue for me. The problem now is that modding is no longer using the same framework, I'll have to revisit the problem later.
我想我找到了一个解决方案,但它不是没有牺牲的。看起来运行嵌入EFT的服务器让我很头疼,UnityEngine在它的运行时有许多lmitiations,这给我带来了问题。使用不同的HTTP服务器(STA的WebSocketSharp)和独立运行服务器为我解决了这个问题。现在的问题是,modding不再使用相同的框架,我必须在以后重新审视这个问题。
看不懂顶
世界因你而精彩