好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 qands 于 2021-4-1 04:48 编辑
写的目的1是记录自己的研究过程,2是有人问问题,我还是想着写详细点点,不过我其实也是新手,共同学习吧.
申明:仅学习逆向过程,不发成品.
首先还是搞没虚拟的版本19.1以前随便吧,我用的7.1.0.写上3行代码dnspy跑起来
[C#] 纯文本查看 复制代码 Workbook wb = new Workbook();
wb.Worksheets[0].Cells[1, 1].PutValue("test");
wb.Save("123.xls");
在save上下断,然后跟下去.思路是观察worksheet数量,变2就停下(第二个是试用版水印的sheet).
最终找到了这个位置
[C#] 纯文本查看 复制代码 public void Save(Stream stream, SaveOptions saveOptions)
{
...
...
...
if (stream.CanSeek)
{
new global::\u0002.\u001F(this).\u0008\u0002(stream);
}
...
...
...
}
跟进去第三行
[C#] 纯文本查看 复制代码 this.\u0001 = A_1;
this.\u0001 = A_1.Worksheets;
this.\u0001.\u000E\u0002();
再跟进去
[Asm] 纯文本查看 复制代码 internal void \u000E\u0002()
{
if (global::\u0017.\u007F.\u0008\u0002() != global::\u0017.\u001F.\u0001)
{
return;
}
这里要是不return,下面就是干加水印的事.
进来之后右键return设为下一条语句,观察下生成的文件,没水印了.
点下\u0008\u0002() 进去看看,末尾2个return,不同的2个值
(注:记住这句4096,下面用的着,)
[C#] 纯文本查看 复制代码 if (\u007F.\u0001 != null && \u007F.\u0001.\u0001 != \u001F.\u0001 && \u001D.\u0008\u0002() != 4096)
{
return \u001F.\u0002;
}
return \u001F.\u0001;
}
继续运行跑到了下面的return,那么正确的应该是if为true.
观察一下这个IF语句,点击\u007F.\u0001,它被定义static,类型和我们所处的类是一样的.然后看看第二个\u007F.\u0001.\u0001,是个emun,第三个目前看不出来啥.
(注:第三个的类其实是校验注册信息中Signature和Data加密值是否匹配的,不要问我为什么知道的,我在一堆乱码中绕了几天了...)
然后观察下这个类,在上面找到了注册时校验的过程.
[C#] 纯文本查看 复制代码 // Token: 0x06001A96 RID: 6806 RVA: 0x001AC3C0 File Offset: 0x001A89C0
internal void \u0008\u0002(Stream A_1)
{
if (A_1 == null)
{
throw new ArgumentNullException(\u007F.\u0001(30740));
}
\u001D.\u0008\u0002(0);
XmlDocument xmlDocument = new XmlDocument();
XmlDocument xmlDocument2;
if (true)
{
xmlDocument2 = xmlDocument;
}
xmlDocument2.Load(A_1);
this.\u0008\u0002(xmlDocument2);
Hashtable u = \u007F.\u0001;
Hashtable hashtable;
if (true)
{
hashtable = u;
}
if (hashtable == null)
{
Hashtable hashtable2 = \u007F.\u0008\u0002();
if (true)
{
hashtable = hashtable2;
}
\u007F.\u0001 = hashtable;
}
if (hashtable.ContainsKey(this.\u0001))
{
throw new InvalidOperationException(\u007F.\u0001(30749));
}
if (\u001D.\u000E\u0002() > 0)
{
return;
}
bool flag = false;
bool flag2;
if (true)
{
flag2 = flag;
}
string[] u2 = this.\u0001;
string[] array;
if (true)
{
array = u2;
}
int num = 0;
int i;
if (true)
{
i = num;
}
while (i < array.Length)
{
string text = array[i];
if (text.Equals(\u007F.\u0001(30846)) || text.Equals(\u007F.\u0001(30863)) || text.Equals(\u007F.\u0001(14954)) || text.Equals(\u007F.\u0001(30892)) || text.Equals(\u007F.\u0001(30929)) || text.Equals(\u007F.\u0001(30966)))
{
flag2 = true;
break;
}
i++;
}
if (!flag2)
{
throw new InvalidOperationException(\u007F.\u0001(30983));
}
DateTime t = DateTime.ParseExact(\u007F.\u0001(31040), \u007F.\u0001(31057), CultureInfo.InvariantCulture);
if (t > this.\u0001)
{
throw new InvalidOperationException(string.Format(\u007F.\u0001(31074), this.\u0001.ToString(\u007F.\u0001(31340), CultureInfo.InvariantCulture), t.ToString(\u007F.\u0001(31340), CultureInfo.InvariantCulture)));
}
if (DateTime.Now > this.\u0002)
{
throw new InvalidOperationException(\u007F.\u0001(31357));
}
this.\u0001 = \u001F.\u0002;
\u007F.\u0001 = this;
}
重点还是最后两句,是不是和上面的if很像.好了,我们现在翻译一下这个if:如果注册信息不为空,且注册成功,那么你很屌
这就简单了.在if哪里的最后一个return右键,IL指令
[Asm] 纯文本查看 复制代码 66 00E4 ldc.i4.1
67 00E5 ret
68 00E6 ldc.i4.0
69 00E7 ret
ldc.i4.0改ldc.i4.1,dnspy自动变代码了,然后保存模块,验证,问题无.收工.
好了下一个版本,19.1,同样的方法,找第二个sheet,到这里
(注:这里也记下,要考, using 和他的下一句)
[C#] 纯文本查看 复制代码 using (\u000E\u200B u000E_u200B = \u0008\u2004\u2000.\u0002(this, fileFormatType, saveOptions))
{
u000E_u200B.\u000E\u200B\u2005\u200B\u0002(stream);
}
if (saveOptions.ClearData)
{
this.\u0002();
}
然后这里
[C#] 纯文本查看 复制代码 this.\u0002 = \u0002;
this.\u0003 = \u0002.Worksheets;
this.\u000E = \u0003;
[color=#ff8c00] \u0002\u2005\u2004.\u0003(this.\u0003);[/color]
\u000F\u2001\u2002 u = this.\u0003.\u0002(\u0003);
这里
[C#] 纯文本查看 复制代码 string text = \u0002.\u0006\u2002().\u0008\u2000();
这里
[C#] 纯文本查看 复制代码 if (\u000E\u2009\u2009.\u0002(this.Settings.\u0003\u2008))
{
return Workbook.\u0002(Workbook.\u000E\u2001);
}
[C#] 纯文本查看 复制代码 public static bool \u0002(bool \u0002)
{
object[] u = new object[]
{
\u0002
};
return (bool)\u000F\u200A\u2008\u2003.\u0005\u200B\u2008\u2003().\u0002(\u000F\u200A\u2008\u2003.\u0008\u200B\u2008\u2003(), "r;HV%q\"a^^", u);
}
然后小样虚化了. 我就不带着大家跟进去绕了,感兴趣可以进去看看,不过最终你会得到个结论:System.Reflection.RuntimeMethodInfo的UnsafeInvokeInternal里下断
下断后F5,观察this的值,要是你认识的,那多半正常操作,不认识的,就得进去看看.不过第一次F5,就不认识了...
于是跟进去,然后就我x了.你好歹虚化的认真点....
[C#] 纯文本查看 复制代码 internal static \u000E\u2008\u2002\u2000 \u0002()
{
switch (0)
{
case 0:
{
string text = \u0006\u200A\u2008\u2003.\u0002(1761156178);
break;
}
case 1:
{
string text = \u0006\u200A\u2008\u2003.\u0002(1761156287);
break;
}
default:
throw new InvalidOperationException(\u0006\u200A\u2008\u2003.\u0002(1761156238));
}
switch (0)
{
case 0:
{
string text2 = \u0006\u200A\u2008\u2003.\u0002(1761156178);
break;
}
case 1:
{
string text2 = \u0006\u200A\u2008\u2003.\u0002(1761156287);
break;
}
default:
throw new InvalidOperationException(\u0006\u200A\u2008\u2003.\u0002(1761156238));
}
bool flag = \u0006\u2008\u2002\u2000.\u000F == null || \u0006\u2008\u2002\u2000.\u000F.\u000E == (\u000E\u2008\u2002\u2000)0 || \u000F\u2002\u2002\u2002.\u0003() == 4096;
if (!flag)
{
DateTime t = string.IsNullOrEmpty(\u0006\u2008\u2002\u2000.\u000F.\u0006) ? DateTime.MaxValue : DateTime.ParseExact(\u0006\u2008\u2002\u2000.\u000F.\u0006, \u0006\u200A\u2008\u2003.\u0002(1761155646), CultureInfo.InvariantCulture);
flag = (t < DateTime.Now);
}
if (!flag)
{
return (\u000E\u2008\u2002\u2000)1;
}
return (\u000E\u2008\u2002\u2000)0;
}
if里面那个return右键设置成下一条,跑起来,点开Excel文件,没问题,这个版本收工了...
下一个版本19.2,调试起来依旧是熟悉的味道,依旧找到了
[C#] 纯文本查看 复制代码 if (!flag)
{
return (\u000E\u2008\u2002\u2000)1;
}
return (\u000E\u2008\u2002\u2000)0;
可是问题来了,与上个版本不同,这里被执行了2次,然后,2次跳过判断后生成的文件是错误的.
捡个懒,有注册码时我不是已经破了一边了,那么看看正确的应该怎么走.
vs里的代码把注册的信息加上,string.compare里下断,flag是0还是1那个地方也下断,还有保存文件加sheet的下一句也下断(就是提示要考的那句),跑起来,就到了比较函数里,值改成2099年
继续跑,到了,2次断在flag,均返回的1,和我们跳2次的操作一样,那肯定下面有坑.
继续跑,就到了加sheet的下一句
[Asm] 纯文本查看 复制代码 u000E_u200B.\u000E\u200B\u2005\u2009\u0002(stream);
然后F10,程序断在了string的比较函数里面.让他返回,然后又到了熟悉的代码,这是虚拟化以前版本的注册判定过程.
[C#] 纯文本查看 复制代码 string text2 = array[0] + array[1] + array[2];
if (string.Compare(text2, this.\u0008) > 0)
{
return;
}
if (this.\u0006 != null)
{
string strA = DateTime.UtcNow.ToLocalTime().ToString(\u0005\u2001\u2009\u2003.\u0002(-1118133632));
if (string.Compare(strA, text2) < 0 || string.Compare(strA, this.\u0006) > 0)
{
return;
}
}
this.\u000E = (\u0005\u2002\u2002)1;
\u0003\u2002\u2002.\u000F = this;
嗯,catch you baby!
然后继续F10,返回到了这里
[Asm] 纯文本查看 复制代码 internal sealed class \u0003\u200B\u2004
{
// Token: 0x06003589 RID: 13705 RVA: 0x001EA35C File Offset: 0x001E855C
internal static bool \u0002()
{
if (\u0006\u200A\u2002\u2000.\u0002 != null)
{
if (\u000F\u2008\u2002\u2000.\u0003)
{
lock (\u0006\u200A\u2002\u2000.\u0002)
{
new \u0003\u2002\u2002().\u0002();
\u000F\u2008\u2002\u2000.\u0003 = false;
}
}
return \u0003\u2002\u2002.\u0003() == (\u0005\u2002\u2002)0;
}
return false;
}
大致猜猜他的作用,没注册信息,直接返回false,有注册信息,就再判断一次注册了没.
vs里删除注册那句,再跑到这里.好吧,我猜错了...他跑到了if里.然后到了这里
[C#] 纯文本查看 复制代码 return \u0003\u2002\u2002.\u0003() == (\u0005\u2002\u2002)0;
跟进去吧
[C#] 纯文本查看 复制代码
...
...
...
if (\u0003\u2002\u2002.\u000F != null && \u0003\u2002\u2002.\u000F.\u000E != (\u0005\u2002\u2002)0 && \u0006\u2005\u2002\u2002.\u0003() != 4096)
{
return (\u0005\u2002\u2002)1;
}
return (\u0005\u2002\u2002)0;
熟悉不?怎么改不用说了吧,然后让程序跑起来,结束,检查Excel文件,没问题了.
所以,它虚拟化判断次注册没,再不虚拟判断次注册.
该下个版本了,但别急着收工,要每次加注册信息找位置好麻烦啊...找找特征,.一个程序块返回后到这里.
[C#] 纯文本查看 复制代码 if (!this.\u0005.\u0005\u2000 && \u0003\u200B\u2004.\u0002())
{
byte[] array2 = Convert.FromBase64String(\u0006\u2002\u2002.\u0002);
\u0002.\u0002(array2, array2.Length);
return;
}
有个Convert.FromBase64String,记下来,估计用得着.
按照有注册码搞它的时候,19.2和最新版本应该差不多.我们就直接最新版本21.3了
还是同样的方法,跑起来和19.2没区别,熟门熟路的着到了第一个地方.然后再跑到进虚化的下一句.
按照刚才的经验,base64那里去下断,果然断在了想断的地方,代码都一样.base64句的上句点几下,就又来到了熟悉的第二个地方,代码就不贴了,和上面的一模一样.
2个地方return前的IL指令,ldc.i4.0改ldc.i4.1 ,最新版收工.
最后,请大家还是以学习为主,好用请支持正版.
|
免费评分
-
查看全部评分
|