某低代码平台 逆向分析(三)【收费插件解码/对DLL打补丁/暗桩】完结
某低代码平台 逆向分析(三)【收费插件解码/对DLL打补丁/暗桩】完结
前情提要
经过前两期的洗礼
某低代码平台 逆向分析(一)【验证逻辑分析和实践】
某低代码平台 逆向分析(二)【客户端注册/发布/插件】
大伙儿应该基本知道要怎么手动处理了。
但是手动处理好像挺麻烦的。
通过咱的其他帖子,大伙儿应该知道可以对我们自己应用中的DLL进行Hook
那么 有没有用什么办法 在别人的程序里面对别人的DLL进行Hook呢?
类似 DLL注入?
这个呢也是有的。。。不过不是DLL注入,而是类似静态修改了。
我们可以写一个 Patch.dll,然后 静态修改别人的DLL引用我们的 Patch
之后 在别人的DLL里面 反射调用 Patch.dll 内的函数。
实现把我们的Hook 附加到其他应用中。
虽然没有C++那种dll改个名字放着就能自动注入强,但是至少也算个法子不是吗?
准备工作
我们根据前两篇帖子的分析,我们知道 要改RSA的点有几个。
服务端 ForguncyServer
CommonUtilities.dll
Forguncy.Server2.dll
Forguncy.UserService2.dll
客户端 Forguncy 8
CommonUtilities.dll
Forguncy.exe
其中基本 所有DLL 都引用了 CommonUtilities.dll
那么我们写个 PatchLib 让 CommonUtilities.dll 引用然后 调用里面的Hook 函数,是不是其他的dll 都会自动生效?
那咱们开工吧~
对DLL打补丁
先把之前 所有的 改动全部还原,没备份就重装一下。
先看目标
目标是 .NET Standard 2.0,那么我们也创建一个 .NET Standard 2.0 的项目 就叫 HuozigePatchLibHuozigePatchLib 的作用就是被目标调用 并且 对目标进行Hook。
引入 Harmony 的包。
先简单写个类


这样一个Hook的DLL就写好了。
那么 为了要将 代码置入到 别的DLL,我们还需要 再写个 访问我们DLL的类,这个类将用于直接放入目标的DLL中。
我们再创建一个 .NET Standard 2.0 的项目 叫 HuozigePatchLoaderHuozigePatchLoader 的作用就是被植入到目标中,用于调用我们写的 HuozigePatchLib
这里就完全是无任何引用,全反射实现。


到这里 基本准备就做好了。
接下来 我们要做的就是 把 HuozigePatchLoader 内创建的类 置入到 目标中。
有两种方式 先说第一种
dnspy 置入法
直接在 dnspy 上对目标dll 右键 添加类,然后 点击 编译。


然后 找到 共通函数 并在顶部置入 Call
我这里找的是 CommonUtilities.CommonUtilitiesWindows.Init()

然后 改代码或者改IL(看哪种方便能成功选哪种)
这次 因为有混淆 所以我们用IL 实现。
顶部 加出一行 来

然后 操作码选 call 后面内容选

保存后 成功的话 效果如下图

我们保存修改然后调试看看。
记得先把我们编译好的Lib 放到 要读取的目录里面。

我们直接调试 ForguncyWorkerService






成功!Hook 生效了!
dnlib 编程法 实现 AutoPatch
https://github.com/0xd4d/dnlib
又一.Net利器,专门拿来静态编辑DLL。
我们再随便起一个控制台项目 引入 dnlib
然后将之前生成的 HuozigePatchLoader 放到这个项目里,我这边叫 PatchLoader.dll


保存后的DLL大概样子

插件解码
根据插件的配置文件PluginConfig.json分析后能找到解码的位置

复制代码 隐藏代码
//new r().ainternal string a(string A_0){
return d.a(A_0, ap.e());
}//ap.e()internal static string e(){
return "-----BEGIN PUBLIC KEY-----\r\n MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPS5BZhNjUHiu6qrUQ8UDf5wja\r\n uRSYporrb+ONWem6ZDNI7mf1JT9IdgDmLjcmjLKfhwRYqMERCBofJjtnDctv/LvX\r\n wPFU05Ww7Pjfx1S287jFYIt2grCBHTU6RnvEr17WOH1Y8GG6mASRm5WTcu3FcKyD\r\n 63l0ZGvtTd4xgUrUmwIDAQAB\r\n -----END PUBLIC KEY-----";
}//d.ainternal static string a(string A_0, string A_1){
string[] array = A_0.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
List<byte> list = new List<byte>();
using (StringReader stringReader = new StringReader(A_1))
{
Pkcs1Encoding pkcs1Encoding = new Pkcs1Encoding(new RsaEngine());
AsymmetricKeyParameter asymmetricKeyParameter = (AsymmetricKeyParameter)new PemReader(stringReader).ReadObject();
pkcs1Encoding.Init(false, asymmetricKeyParameter);
for (int i = 0; i < array.Length; i++)
{
byte[] array2 = Convert.FromBase64String(array[i].Trim());
list.AddRange(pkcs1Encoding.ProcessBlock(array2, 0, array2.Length));
}
}
return Encoding.UTF8.GetString(list.ToArray());
}算法有了直接根据DLL解密即可,解完了 将 Base64 转成 byte[] 另存为 .dll 后缀就行了。
关于暗桩
Forguncy.UserService2 里面有个类,内容如下
复制代码 隐藏代码
namespace Forguncy.UserService2{
// Token: 0x020000D1 RID: 209 internal class b {
// Token: 0x06000427 RID: 1063 RVA: 0x0001C318 File Offset: 0x0001A518 public static void h() {
}
// Token: 0x06000428 RID: 1064 RVA: 0x0001C364 File Offset: 0x0001A564 private static void a(object A_0) {
try {
if (Forguncy.UserService2.b.g())
{
Forguncy.UserService2.b.c();
Forguncy.UserService2.b.d();
}
}
catch (Exception ex)
{
TraceHelper.TraceException(ex, "ServerInfoChecker");
}
}
// Token: 0x06000429 RID: 1065 RVA: 0x0001C3A4 File Offset: 0x0001A5A4 private static bool g() {
int num = 30;
return !(Forguncy.UserService2.b.a().AddDays((double)30f) > DateTime.Now) && !(Forguncy.UserService2.b.e().AddDays((double)num) > DateTime.Now);
}
// Token: 0x0600042A RID: 1066 RVA: 0x00004109 File Offset: 0x00002309 private static string f() {
return Path.Combine(SystemConfigDef.TempLogFilePath, "D29A8C27-93CB-484A-863A-8FA9216C9E8C");
}
// Token: 0x0600042B RID: 1067 RVA: 0x0001C3F4 File Offset: 0x0001A5F4 private static DateTime e() {
string text = Forguncy.UserService2.b.f();
if (File.Exists(text))
{
DateTime dateTime2;
try {
long num = long.Parse(File.ReadAllText(text));
DateTime dateTime = new DateTime(num);
dateTime2 = ((dateTime > DateTime.Now) ? DateTime.MinValue : dateTime);
}
catch (Exception ex)
{
TraceHelper.TraceException(ex, "ServerInfoChecker.GetLastCheckTime");
goto IL_4D;
}
return dateTime2;
}
IL_4D:
return DateTime.MinValue;
}
// Token: 0x0600042C RID: 1068 RVA: 0x0001C464 File Offset: 0x0001A664 private static void d() {
string text = Forguncy.UserService2.b.f();
FileUtilities.MakeFileNotReadOnly(text);
File.WriteAllText(text, DateTime.Now.Ticks.ToString());
}
// Token: 0x0600042D RID: 1069 RVA: 0x0001C498 File Offset: 0x0001A698 private static void c() {
IServiceVisitor serviceVisitor = ServiceVisitor.CreateServiceVisitor("https://campus.grapecity.com.cn/LicenseInfoReport");
string text = "<RSAKeyValue><Modulus>pk1y4UKCa2xmgNfD2mwMUKboQk6KgovhZ1D4iwjOa2dd6LUjM2BBr0X6W/1kO1diMoHhoyLsZxbLqrvLH2CyIFCXoTUpL8bgYXQzQyN2dlI6G7po7D4tu/HtJrLuBnew1R/q7FA/eibwXrGedNdqrLrFjwRtANGyl6P/8gidEGuvmuV7I+ZszIASl33v37V0scEs36aHBIOk2p8dmICJw7wjWx7irptO9BAVupxi65yfkMPHqA/YRVZPx12uiqaks6Kmk15gdebLSt9P5pNsKf9W4EWoVgiNQR5e3oD/bgywg6+T+kiwstj3I7BhxdutwX1rtUJMYyTB367YKKFQhQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
string text2 = Forguncy.UserService2.b.b();
text2 = EncryptDecryptHelper.EncryptByXmlPublicKey(text2, text);
serviceVisitor.CallMethod("customApi/api/Report", Encoding.UTF8.GetBytes(text2));
}
// Token: 0x0600042E RID: 1070 RVA: 0x0001C4DC File Offset: 0x0001A6DC private static string b() {
List<f.a> list = Forguncy.UserService2.Controllers.f.g(null);
string text = "8.0.105.0 ";
List<object> list2 = new List<object>();
foreach (f.a a in list)
{
a3 a2 = a.License;
a4 a3 = ((a2 != null) ? a2.SerialKey : null);
string text2 = ((a3 != null) ? a3.Key : null);
if (!string.IsNullOrEmpty(text2) && text2.Length > 6)
{
text2 = text2.Substring(0, text2.Length - 6);
}
List<object> list3 = new List<object>
{
text2,
Math.Abs(Convert.ToInt32((a3 != null) ? new SerialKeyType?(a3.SerialKeyType) : null)),
string.IsNullOrEmpty((a3 != null) ? a3.ExtendInfos : null) ? "-1" : a3.ExtendInfos,
text
};
list2.Add(string.Join<object>(",", list3));
}
if (list2.Count == 0)
{
return ",4,-1," + text;
}
return string.Join<object>("|", list2);
}
// Token: 0x0600042F RID: 1071 RVA: 0x0001C630 File Offset: 0x0001A830 private static DateTime a() {
DateTime dateTime2;
try {
DateTime dateTime = y.a();
dateTime2 = ((dateTime > DateTime.Now) ? DateTime.MinValue : dateTime);
}
catch {
dateTime2 = DateTime.MinValue;
}
return dateTime2;
}
// Token: 0x06000430 RID: 1072 RVA: 0x000026B3 File Offset: 0x000008B3 public b() {
}
// Token: 0x04000322 RID: 802 private static Timer a;
}
}启动后 根据 安装时间(好像超过30天之后)周期性上报license信息。
所以别干坏事,会被查到哟。
还有第三种可以实现像C++那样放两个文件 Patch.dll和xxx.exe.config 文件实现自动劫持
添加修改xxx.exe.config文件,类似于
<runtime> <appDomainManagerType value="HuozigePatchLoader.HuozigePatchLoaderLocal" /> <appDomainManagerAssembly value="HuozigePatchLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </runtime>上一篇:某低代码平台 逆向分析(二)【客户端注册/发布/插件】
没有最新的文章了...
