吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5818|回复: 54
收起左侧

[.NET逆向] 某.net组件库 注册码逆向分析

  [复制链接]
pjy612 发表于 2023-3-28 18:01
本帖最后由 pjy612 于 2025-11-13 22:22 编辑

前情提要

在摸鱼时翻nuget,发现有个 组件库 ,组件还不少,感觉可以分析一波。

PS.发帖的时候 用MD 图片打算先占位,结果直接拦截,内容全丢了,导致重写了一次...


实惨!!!QAQ{:301_972:}

受害者 介绍

aHR0cHM6Ly93d3cucmViZXgubmV0L3RvdGFsLXBhY2sv

0.png

准备工作

//Rebex.Licensing.Key = RebexPatch.GetLicense();
using (var client = new Rebex.Net.Sftp())
{
    // connect and log in
    client.Connect("127.0.0.1",2222);
    client.Login("tester", "password");
    // download a file
    SftpItemCollection list = client.GetList();
}

分析

然后没加Key 跑起来直接提示异常。
临时Key 可以在 下面地址申请。(好感+1

aHR0cHM6Ly93d3cucmViZXgubmV0L3N1cHBvcnQvdHJpYWwv

那么来看看它都干了啥吧。
首先当然是扔 dnspy 里面 看看
定位点是

Rebex.Licensing.Key

1.png
2.png
3.png
一般用到静态类和静态字段的话,鉴权一般也会放在对应的类里面。
所以简单看了下这个类 haqbo
发现了两个可疑的函数,然后查查他们的调用链
4.png
拥有相同的调用链
那么进去就看看
5.png
里面返回的是一个枚举,有两个值,然后 优化下函数逻辑后可知。

internal static gqkze ihbdt(avgel p0)
{
  sioyq hopul = p0.hopul;
  string nnstu = p0.nnstu;
  int wxeiw = p0.wxeiw;
  if (p0.vjlwo == hjedi.exnqw)
    return gqkze.kfryg;
  Dictionary<string, byte[]> eursq = haqbo.eursq;
  bool lockTaken = false;
  try
  {
    Monitor.Enter((object) eursq, ref lockTaken);
    fbqkl fbqkl1 = (fbqkl) null;
    fbqkl fbqkl2 = (fbqkl) null;
    foreach (byte[] p0_1 in haqbo.oripl)
    {
      switch ((char) p0_1[0])
      {
        case '#':
          if (fbqkl2 == null)
          {
            fbqkl2 = fbqkl.djjbo(nnstu);
            continue;
          }
          continue;
        case 'A':
        case 'T':
          fbqkl2 = haqbo.ufgeu(p0_1, nnstu, wxeiw);
          if (fbqkl2 == null)
            return gqkze.irmxg;
          continue;
        case 'F':
          fbqkl1 = haqbo.tjeki(p0_1, hopul, nnstu, wxeiw);
          if (fbqkl1 == null)
            return gqkze.kfryg;
          continue;
        default:
          continue;
      }
    }
    if (p0.vjlwo != hjedi.brafw && !ffppi.eztdl)
    {
      Assembly jovip = p0.jovip;
      if (jovip != (Assembly) null)
      {
        AssemblyName name = jovip.GetName();
        if (name != null && name.Name == nnstu)
        {
          fbqkl fbqkl3 = haqbo.eicwj(haqbo.raxrd(jovip), nnstu, wxeiw);
          if (fbqkl3 == null)
            return gqkze.irmxg;
          fbqkl2 = fbqkl2 ?? fbqkl3;
        }
      }
    }
    throw (fbqkl1 ?? fbqkl2) ?? fbqkl.djjbo(nnstu);
  }
  finally
  {
    if (lockTaken)
      Monitor.Exit((object) eursq);
  }
}

internal enum gqkze
{
    // Token: 0x04000074 RID: 116
    kfryg, //prod
    // Token: 0x04000075 RID: 117
    irmxg  //trial
}

我们可以直接在这个类 return gqkze.kfryg!
yeah!搞定收工!(bushi
继续分析...
其中有 fbqkl.djjbofbqkl.audqj
6.png
可知 fbqkl 类 是个异常类 所以后续可以忽略。
我们先瞅瞅 T 和 A 的逻辑。

private static void dvrgx(byte[] p0, out DateTime p1, out DateTime p2, out int p3, out int p4)
{
    int num = (int)p0[0] * 256 + (int)p0[1];
    p3 = (int)p0[2] * 256 + (int)p0[3];
    p4 = (int)p0[4];
    p2 = new DateTime(2010, 12, 31).AddDays((double)num);
    p1 = p2.AddDays(-64.0);
    //这里给了两个时间, 其中有个 -64天
    //p2=trial 到期时间
    //p1=trial - 64天
}
//p0 trialKey
//p1 程序集名
//p2 程序集版号
private static fbqkl ufgeu(byte[] p0, string p1, int p2)
{
    byte[] array = haqbo.kaueg(p0); // 下面函数 对trialKey 进行了一个处理
    if (array == null)
    {
        return new fbqkl("Invalid license key.");
    }
    DateTime dateTime;
    DateTime dateTime2;
    int num;
    int num2;
    haqbo.dvrgx(array, out dateTime, out dateTime2, out num, out num2);//处理完的数据 在上面 进行关键值提取
    if ((num2 & 1) != 0)
    {
        return fbqkl.djjbo(p1);//异常
    }
    if (num < 65535 && p2 > 0 && p2 < num) //trial 内的 num 如果比 程序集的 高
    {
        //异常
        return new fbqkl("Your trial version is too old. Please contact support@rebex.net to get a suitable trial key.");
    }
    DateTime now = DateTime.Now;
    if (now < dateTime) //这里的 dateTime 对应 上面函数的 p1,算是双重验证 到期
    {
        //异常
        return new fbqkl("System time is wrong, unable to check the trial license key.");
    }
    if (now > dateTime2)
    {
        //异常
        return fbqkl.audqj(p1);  //这里trial到期
    }
    return null;
}
private static byte[] kaueg(byte[] p0)
{
    RSAParameters rsaparameters = default(RSAParameters);
    rsaparameters.Modulus = new byte[]
    {
        182, 28, 156, 84, 172, 217, 205, 220, 65, 15,
        164, 101, 241, 26, 18, 244, 193, 44, 214, 186,
        203, 99, 154, 131, 134, 199, 190, 107, 233, 99,
        126, 147
    };
    rsaparameters.Exponent = new byte[] { 1, 0, 1 };
    RSAManaged rsamanaged = new RSAManaged(true);
    rsamanaged.ImportParameters(rsaparameters);
    byte[] array = new byte[p0.Length - 1];
    Array.Copy(p0, 1, array, 0, array.Length);
    byte[] array2;
    try
    {
        //说实话 这里为什么要用加密函数??? 这套trial 的逻辑 我反而没看懂
        //但是这里不是系统库,可能重写了什么RSA的逻辑,不过这不是这次的重点不深究了
        array2 = rsamanaged.EncryptValue(array); 
        array2 = RSAManaged.tclma(array2, 7, true);
    }
    catch (CryptographicException)
    {
        array2 = null;
    }
    return array2;
}

还是回到 F 的吧...

//p0 key 字节数组
//p1 鉴权枚举
//p2 程序集名
//p3 程序集版本
private static fbqkl tjeki(byte[] p0, sioyq p1, string p2, int p3)
{
    //这里知道 字节数组 长度要大于 45
    if (p0.Length < 45)
    {
        return new fbqkl("License key is corrupted.");
    }
    byte[] array = new byte[40];
    Array.Copy(p0, 1, array, 0, 40);
    byte[] array2 = new byte[p0.Length - 41];
    for (int i = 0; i < array2.Length; i++)
    {
        //这里有xor
        array2[i] = p0[i + 41] ^ p0[i % 40 + 1];
    }
    DateTime utcNow = DateTime.UtcNow;
    //有公钥 那么思路基本就是动态替换
    string text = "-----BEGIN PUBLIC KEY-----\r\nMIIBtzCCASsGByqGSM44BAEwggEeAoGBAMyn0CrBOcXnBfDqgbxcZaXMhwmiYvbv\r\ndyAlFmtjVIZMMHLotW7GX7rhC+iApKb22N0STewEpng+nvuZ/36gaAhT49RJ9zmM\r\nJ4Pqucw8QKzwYeqgloUkpwMRV/2TEsw6cJ7LGed9FwomvO/POdLNO2GBUk0/iol5\r\n/IdRRlWIZD2NAhUAwIyx9VZV0hnBnW20bMghBAz3WnkCgYAjiwdms1J3QQnm/kbn\r\nkv9gEtQGfc3EWIPqla8NrH5L3Uy+HkxU4YqjlN6bTDcmfUmQqqq7AT2LO2FswGiV\r\nIyH0mKQA8MKbV3JCN44zgn1kccuo07UPVEdy+c1sf4yhvqYj2AgNoojONcgjHUP8\r\nsft+gisdwmXsX9ExeujZoJF0CgOBhQACgYEAk0eKBhWKTbKUaXzNti+qdw/BK/jv\r\nvs1syvuXH4m7BaqynR2TRL/zwlKATdOaZ0qxlS/kMuw+do3PzUehJQn9Fl57mQsl\r\n7f+2D9T4sWAg90WHZ36WrQWcffIUl60z9FdSoLRoH2IrbpQdF0AYbHy5w7gUcwhe\r\n5nbqzXAnQQgsYz4=\r\n-----END PUBLIC KEY-----\r\n";
    PublicKeyInfo publicKeyInfo = new PublicKeyInfo(); //非系统类
    using (MemoryStream memoryStream = new MemoryStream(EncodingTools.ASCII.GetBytes(text)))
    {
        publicKeyInfo.Load(memoryStream);
    }
    IDisposable disposable = null; //接口
    gibml gibml = null;//接口    
    jidav jidav = lntty.yudnv("dsa1024");//接口
    if (jidav != null)
    {
        hhtwd hhtwd = jidav.dbmak(publicKeyInfo);//入公钥
        gibml = hhtwd as gibml;
        disposable = hhtwd as IDisposable;
    }
    try
    {
        if (gibml == null)
        {
            //如果系统不支持 上面的接口 则用系统的 DSA 库
            DSAManaged dsamanaged = new DSAManaged(true);
            DSAParameters dsaparameters = publicKeyInfo.GetDSAParameters();
            dsamanaged.ImportParameters(dsaparameters);
            gibml = dsamanaged;
        }
        byte[] array3 = HashingAlgorithm.ComputeHash(HashingAlgorithmId.SHA1, array2);
        //这里就是 鉴权了,至于用的哪个具体实现 后续再分析。
        if (!gibml.iygcq(array3, array, djrra.taiku(SignatureHashAlgorithm.SHA1, AsymmetricKeyAlgorithmId.DSA)))
        {
            return new fbqkl("License key is not valid.");
        }
    }
    finally
    {
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }
    //鉴权成功后 数据解析部分
    jveps jveps = new jveps(array2);
    jveps.wsbya(); //Q1
    bool flag = false;
    bool flag2 = false;
    while (jveps.hraeq < jveps.uqwkl) //Q2
    {
        int num = jveps.jywwn();      //Q3
        int num2 = (int)jveps.bbdwb();//Q4
        int num3 = (int)jveps.mepme();//Q5
        if (p1 != (sioyq)0 && (zlmlt.ttkju((vshmk)num) & p1) == p1) //Q6
        {
            switch (num2)
            {
                //两种情况 但是满足其一就跳转了。那么应该是要么 版本校验 要么时间校验
                case 1:
                  if (p3 <= num3) //版本校验
                    return (fbqkl) null;
                  flag1 = true;
                  continue;
                case 2:
                  DateTime dateTime = new DateTime(2010, 12, 31, 0, 0, 0, DateTimeKind.Utc).AddDays((double) num3);
                  if (utcNow <= dateTime)//时间校验
                    return (fbqkl) null;
                  flag2 = true;
                  continue;
                default:
                  continue;
            }
        }
    }
    //下面不用看 都是异常
    if (flag)
    {
        return new fbqkl("License key for " + p2 + " has not been renewed. See https://www.rebex.net/kb/license-keys/ for more information.");
    }
    if (flag2)
    {
        return new fbqkl("License key for " + p2 + " has expired. See https://www.rebex.net/kb/license-keys/ for more information.");
    }
    return new fbqkl("License key for " + p2 + " not available. See https://www.rebex.net/kb/license-keys/ for more information.");
}

有个明显的 DSAManaged 应该是用到一些DSA的逻辑。
7.png
分析一下 发现 接口实现挺多,但是总之应该 DSA 的鉴权。
先跳过,继续分析。。。。
这里有个 jveps类 用来解析 license 的真实数据。
8.png
其中用到的几个函数

public uint wsbya()
{
    this.kxkiy(this.kzdmt, 0, 4);
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse<byte>(this.kzdmt, 0, 4);
    }
    return BitConverter.ToUInt32(this.kzdmt, 0);
}
public int jywwn()
{
    this.kxkiy(this.kzdmt, 0, 4);
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse<byte>(this.kzdmt, 0, 4);
    }
    return BitConverter.ToInt32(this.kzdmt, 0);
}
public byte bbdwb()
{
    if (this.mcprv >= this.mbfba)
    {
        throw new fyvfn("Not enough data available.");
    }
    byte b = this.nfkkb[this.mcprv];
    this.mcprv++;
    return b;
}
public ushort mepme()
{
    this.kxkiy(this.kzdmt, 0, 2);
    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse<byte>(this.kzdmt, 0, 2);
    }
    return BitConverter.ToUInt16(this.kzdmt, 0);
}

直觉上来说就是 自制的 字节流处理类。类似 ByteArray BinaryReader 什么的。
那么 逆推 的 伪代码就是

var br = new BinaryReader();
br.ReadUInt32();
while (br.BaseStream.Position<br.BaseStream.Length)
{
    var num = br.ReadInt32();
    var num2 = br.ReadByte();
    var num3 = br.ReadUInt16();
}

其中我们能知道
num2 为 1或2
num3 越大越好
那么 这个 num 是什么呢?
继续看

[Flags]
internal enum sioyq
{
    // Token: 0x04000054 RID: 84
    rcuhg = 1,
    // Token: 0x04000055 RID: 85
    fibxo = 2,
    // Token: 0x04000056 RID: 86
    odpil = 4,
    // Token: 0x04000057 RID: 87
    ztmcw = 8,
    // Token: 0x04000058 RID: 88
    iuqnd = 16,
    // Token: 0x04000059 RID: 89
    nyqjp = 32,
    // Token: 0x0400005A RID: 90
    xsxcx = 64,
    // Token: 0x0400005B RID: 91
    gzzvp = 128,
    // Token: 0x0400005C RID: 92
    tvtws = 256,
    // Token: 0x0400005D RID: 93
    jalwj = 512,
    // Token: 0x0400005E RID: 94
    gnjbt = 1024,
    // Token: 0x0400005F RID: 95
    nfklw = 2048,
    // Token: 0x04000060 RID: 96
    nquoh = 4096,
    // Token: 0x04000061 RID: 97
    cocit = 8192,
    // Token: 0x04000062 RID: 98
    tcmnt = 16384,
    // Token: 0x04000063 RID: 99
    mbwum = 65536,
    // Token: 0x04000064 RID: 100
    ltklj = 131072,
    // Token: 0x04000065 RID: 101
    ejqlp = 262144,
    // Token: 0x04000066 RID: 102
    icusp = 524288,
    // Token: 0x04000067 RID: 103
    bvrug = 1048576,
    // Token: 0x04000068 RID: 104
    udpdo = 2097152,
    // Token: 0x04000069 RID: 105
    eynsu = 4194304,
    // Token: 0x0400006A RID: 106
    jncra = 8388608,
    // Token: 0x0400006B RID: 107
    evzga = 33554432
}

internal enum vshmk
{
    // Token: 0x0400007B RID: 123
    iwkpg = 1,
    // Token: 0x0400007C RID: 124
    cxbcm,
    // Token: 0x0400007D RID: 125
    olrse = 4,
    // Token: 0x0400007E RID: 126
    qnxpz = 8,
    // Token: 0x0400007F RID: 127
    oglpz = 16,
    // Token: 0x04000080 RID: 128
    oelig = 32,
    // Token: 0x04000081 RID: 129
    fsikk = 64,
    // Token: 0x04000082 RID: 130
    awqdf = 128,
    // Token: 0x04000083 RID: 131
    zugrh = 256,
    // Token: 0x04000084 RID: 132
    ojtis = 512,
    // Token: 0x04000085 RID: 133
    yckct = 1024,
    // Token: 0x04000086 RID: 134
    lacls = 2048,
    // Token: 0x04000087 RID: 135
    yjmkv = 4096,
    // Token: 0x04000088 RID: 136
    mnbep = 15728816,
    // Token: 0x04000089 RID: 137
    xcpuh = 251658296,
    // Token: 0x0400008A RID: 138
    brzmp = 2147483647
}

两个枚举 还有一个 [Flags]
配上函数
9.png
就是拼装权限了,那么能不能一个值就拿到最大权限呢?
写个demo 试试。。。

//evzga = 33554432
var foo = (2147483647 & 33554432) == 33554432;
//foo = true

那么三个值就都有了。
我们可以自己尝试去加,如果类自身包含写的逻辑也可以用反射去实现。
这里推荐反射,以彼之道还至彼身。
10.png
license 结构已经搞定,还差个Sign
我们先就用系统的DSA试试,只要没啥魔改。

DSA dsa = DSA.Create();
//备用 之后 有用
byte[] priKey = dsa.ExportPkcs8PrivateKey();
byte[] signByte = dsa.SignData(value,HashAlgorithmName.SHA1);

有了Sign那注册码的最终形态就呼之欲出了!

byte[] xor = new byte[value.Length];
for (int i = 0; i < value.Length; i++)
{
    xor[i] = (byte)(value[i] ^ signByte[i % 40]);//其实这里signByte 长度固定是40
}
var fullLicense = $"==F{Convert.ToBase64String(signByte.Concat(xor).ToArray())}==";

接着我们来处理 鉴权逻辑的处理。
如果用的是系统函数,那么我们True就行了。但是这次的受害者好像有自己的想法。
11.png
接口的实现是自制的。
先看看公钥部分 jidav.dbmak

//jidav.dbmak 的实现
hhtwd jidav.zmris(PublicKeyInfo p0)
{
    cvxbb cvxbb = this.kcaco.iyfql();
    cvxbb.fkqdt(p0);
    return cvxbb;
}
public virtual void fkqdt(PublicKeyInfo p0)
{
    this.fqoio();
    if (this.ihotv())
    {
        cvxbb.qapbb();
    }
    byte[] array = this.gjnry.vonlf(p0);//又是接口
    this.ymifo(array);
}
public byte[] vonlf(PublicKeyInfo p0)
{
    if (p0 == null)
    {
        throw new ArgumentNullException("publicKeyInfo");
    }
    DSAParameters dsaparameters = p0.GetDSAParameters();//这个函数可以入手
    return this.yrqtx(ref dsaparameters, false);
}
private byte[] yrqtx(ref DSAParameters p0, bool p1)
{
    nwyxq.prftd(ref p0, p1);
    if (p1)
    {
        nwyxq.bwert(ref p0);
    }
    int num = p0.P.Length;
    int num2 = Marshal.SizeOf(typeof(ogska.jajew)) + p0.P.Length + p0.G.Length + num;
    if (p1)
    {
        num2 += 20;
    }
    byte[] array = new byte[num2];
    jveps jveps = new jveps(array); //熟悉的流操作对象 
    int num3 = (p1 ? 1448104772 : 1112560452);
    jveps.jabtz(num3);
    jveps.jabtz(p0.P.Length);
    if (p0.Counter <= 0 || p0.Seed == null)
    {
        jveps.zaxlu(byte.MaxValue, 24);
    }
    else
    {
        jveps.jabtz(p0.Counter);
        jveps.nykai(p0.Seed);
    }
    jveps.nykai(p0.Q);//开始写入 DSA 的参数了 
    jveps.nykai(p0.P);//开始写入 DSA 的参数了 
    jveps.nykai(p0.G);//开始写入 DSA 的参数了 
    if (p1)
    {
        jveps.plujb(num);
        jveps.nykai(p0.X);
        return array;
    }
    jveps.nykai(p0.Y);
    return array;
}

鉴权的实现也是...

public virtual bool iygcq(byte[] p0, byte[] p1, djrra p2)
{
    this.fqoio();
    return this.tdrmf(p0, p1, p2);
}
protected virtual bool tdrmf(byte[] p0, byte[] p1, djrra p2)
{
    if (p0 == null)
    {
        throw new ArgumentNullException("hash");
    }
    if (p1 == null)
    {
        throw new ArgumentNullException("signature");
    }
    if (p2 == null)
    {
        throw new ArgumentNullException("signVerifyParameters");
    }
    this.oqsoc();
    bool flag;
    using (cvxbb.jzvtl jzvtl = this.ylokf(p2))
    {
        uint num = ogska.BCryptVerifySignature(this.fygoz, jzvtl.negbf, p0, p0.Length, p1, p1.Length, jzvtl.zrnyc);
        if (num == 3221266432U)
        {
            flag = false;
        }
        else if (num == 3221225485U)
        {
            flag = false;
        }
        else
        {
            ogska.kwiot(num);
            flag = true;
        }
    }
    return flag;
}
[DllImport("Bcrypt.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint BCryptVerifySignature(ogska.svapf hKey, IntPtr pPaddingInfo, byte[] hashBytes, int hashBytesSize, byte[] signatureBytes, int signatureBytesSize, uint flags);

所以下手点 就决定是你了 PublicKeyInfo.GetDSAParameters

开工

祭出神器 HarmonyLib
12.png

结果

13.png

搞定收工!

关于Hook用到的技术

可以参考 用Mod的思路去给第三方库打补丁(出处: 吾爱破解论坛)

HarmonyLib 的具体用法 可以 百度,谷歌和看官方文档。最好自己实际操作下

免费评分

参与人数 28吾爱币 +25 热心值 +26 收起 理由
masterkoko + 1 + 1 鼓励转贴优秀软件安全工具和文档!
Nice0508 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
allspark + 1 + 1 用心讨论,共获提升!
lvyiwuhen + 1 + 1 热心回复!
jgs + 1 + 1 谢谢@Thanks!
bugof52pj + 1 谢谢@Thanks!
gqdsc + 1 + 1 为了给你评分我换了个浏览器,容易么
37927 + 1 热心回复!
yixi + 1 + 1 谢谢@Thanks!
leaf0125 + 1 + 1 用心讨论,共获提升!
VXORBY + 1 + 1 我很赞同!
liangkuai + 1 + 1 我很赞同!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
vieing727360 + 1 + 1 我很赞同!
iopnm + 1 我很赞同!
MF1998 + 1 谢谢@Thanks!
DSUPER + 1 + 1 我很赞同!
N1san + 1 + 1 热心回复!
xlwllm + 1 + 1 谢谢@Thanks!
zhoumeto + 1 + 1 用心讨论,共获提升!
Gilbert1123 + 1 谢谢@Thanks!
ps122 + 1 + 1 用心讨论,共获提升!
owouwu + 1 + 1 我很赞同!
leigevip + 1 热心回复!
jacky520510 + 2 + 1 我很赞同!
1MajorTom1 + 1 热心回复!
夜泉 + 1 + 1 已经处理,感谢您对吾爱破解论坛的支持!
3yu3 + 1 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

gqdsc 发表于 2023-4-7 16:51
pjy612 发表于 2023-4-7 15:40
那明天等您的分分哟~

已经更换浏览器,给你评分了,这下满意了吧
 楼主| pjy612 发表于 2023-4-6 15:27
本帖最后由 pjy612 于 2023-4-6 15:30 编辑
E024 发表于 2023-4-6 13:47
如果是要连接  ftp 或者 sftp 有免费的可用,如:WinSCP 直接 nuget 搜一下就好。

没,这是个系列工具库,练手的只是随便挑了其中一个,作用是通过代码去连 SFTP FTP 或 构建S/FTP 服务端。

要免费的肯定也有其他开源的可以用,一般手工要连个啥肯定直接找个工具就连了。


(当然这些都不是重点,重点是怎么解剖)
 楼主| pjy612 发表于 2023-3-30 10:06
本帖最后由 pjy612 于 2023-3-30 10:07 编辑
jacky520510 发表于 2023-3-30 09:58
函数名都混淆了,看的有点头大

主要是 这种拿 de4dot 处理的话 没啥差别
method_数字 多了也眼花。。。

其实只要 不是那种 Unicode 或者 伪中文的,可以直接带着混淆看...
当然 反混淆后的 只是拿来分析用,实际用还是基于原版 进行 Hook

当然 有字符串混淆或者流程混淆的 一般是先处理一下再看
 楼主| pjy612 发表于 2023-3-28 21:33
坑爹的图片逻辑。。。搞得重写了一遍。。。坑死啊!!!
以后 坚决不用 []() 占位了。。。
沙发自己坐。
3yu3 发表于 2023-3-29 10:44
本帖最后由 3yu3 于 2023-3-29 11:22 编辑

大佬,膜拜中。。这种程序分支盘根错节,毫无可读性,一看分析的头都大。写出来这么多分析结果,实属不易,得仔细阅读学习修炼功力。

另外HarmonyLib功能强大啊,可否出一篇文章讲一下,比如 如何对已经加载的插件类软件或者运行起来的程序对内存进行patch,patch它的方法和一些常量。
yiting8 发表于 2023-3-29 11:07
哪里下载的,我来练练手
 楼主| pjy612 发表于 2023-3-29 13:03
yiting8 发表于 2023-3-29 11:07
哪里下载的,我来练练手

介绍里base64。
nuget上面也能找到。
我是拿了一个sftp的包弄的,你有兴趣可以去试试unzip组件包。核心鉴权都在一个common库里。
所以一般这种推荐内存补丁的形式去用。免的去操心强签名什么的
 楼主| pjy612 发表于 2023-3-29 13:18
本帖最后由 pjy612 于 2023-3-29 13:25 编辑
3yu3 发表于 2023-3-29 10:44
大佬,膜拜中。。这种程序分支盘根错节,毫无可读性,一看分析的头都大。写出来这么多分析结果, ...

其实 这些基本就是练 代码阅读理解 和 调试分析。
刚开始可能还要配合调试器看看,之后基本就是看代码片段,看调用链上下游。。。

直觉练出来之后,正常开发中定位问题会变得很快。。。
而且 一些逻辑不复杂的直接带着混淆看.... 前提是没加强壳
rz66 发表于 2023-3-30 08:08
文章写的不易,我直接读到结尾,HarmonyLib,我都不知道,希望有机会能读到更多相关文章。感谢
 楼主| pjy612 发表于 2023-3-30 08:23
rz66 发表于 2023-3-30 08:08
文章写的不易,我直接读到结尾,HarmonyLib,我都不知道,希望有机会能读到更多相关文章。感谢

因为这个一般配合bepinex 给u3d 游戏写mod...我只是写mod觉得很好用,想着应该也能拿来用在普通开发中...
jacky520510 发表于 2023-3-30 09:58
函数名都混淆了,看的有点头大
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-16 03:15

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表