【.NET逆向】逆向分析 不是人玩的CM系列(一)

作者論壇賬號(hào):JemmyloveJenny
前言
這個(gè)CM是 @wwh1004??編寫的,第一個(gè)做出來(lái)的人獎(jiǎng)勵(lì)500CB
題目在這里:https://www.52pojie.cn/thread-1241463-1-1.html
CM取名叫“不是人玩的CM系列”,然而我卻在這里發(fā)帖講我怎么玩的?這就是“我不是人系列”嗎?
感覺(jué)怪怪的233333
誒,這個(gè)CM是真的好難啊啊啊啊,我花了六個(gè)多小時(shí)才做出來(lái)……
@Hmily 版主讓我單獨(dú)開貼寫一下,那我就把全部的分析思路寫出來(lái),包括成功的和失敗的……
失敗的思路不想看就跳過(guò),左邊目錄直接跳轉(zhuǎn)
分析思路
先運(yùn)行一下看看
隨便輸一個(gè) 00112233445566778899

嗯…我失敗了
OK,知道錯(cuò)誤提示了,準(zhǔn)備開干!
上dnSpy直接干它(失敗)
出題的@wwh1004 大佬直接告訴你了,這是個(gè).Net程序,無(wú)殼無(wú)混淆
哦?那還等什么?無(wú)殼無(wú)混淆.Net不就相當(dāng)于裸奔嗎!直接拿dnSpy上去干??!
當(dāng)然,是我太天真了……
開局一看,unsafe,指針什么的都出來(lái)了

再仔細(xì)一看,額……Stack是啥?Tuple是啥?GClass0.Gclass1又是啥?怎么這么多變量啊啊啊

光是smethod_6
這一個(gè)函數(shù)就占了800+行
大佬就是大佬,無(wú)殼無(wú)混淆的CM都能這么花里胡哨
真·逆向分析(失?。?/h1>
瀏覽一下Main
函數(shù),發(fā)現(xiàn)提示失敗的只有一種情況,出現(xiàn)Exception會(huì)失敗

原本想著能爆破一下什么的,結(jié)果發(fā)現(xiàn)連if都么有,是靠Exception判斷的!這就是大佬出的題啊……
但是至少我們GET到了一個(gè)條件
條件1:運(yùn)行流程不能出現(xiàn)Exception
看到提示成功的地方

剛一看到這個(gè)ECB模式,還以為這邊會(huì)有什么密碼學(xué)的漏洞呢,但事實(shí)證明我想多了……
細(xì)看了一下,程序的運(yùn)行流程會(huì)生成AES的Key,而程序里給出的是密文。
大佬出題的那個(gè)帖子里面,可以看到一部分提示語(yǔ),我原本想猜測(cè)一下明文,反推Key的,也就是真·逆向分析
然而我仔細(xì)想了一下,AES是抗 已知明文攻擊 的,也就是說(shuō)無(wú)法利用明文和密文反推密鑰,只能放棄。
大致了解程序運(yùn)行流程
好吧,歪門邪道什么的面對(duì)大佬的CM完全無(wú)效,那就認(rèn)真分析了!
我連Eazfuscator.Net都分析過(guò),難道還會(huì)被一個(gè)沒(méi)有混淆的CM難???
先從Main入手,了解一下運(yùn)行流程:
因?yàn)槌鲱}大佬對(duì)這個(gè)CM進(jìn)行了內(nèi)聯(lián),所以能看到有很多重復(fù)的代碼
劃分內(nèi)聯(lián)代碼
比如說(shuō)一下這一段,內(nèi)容和smethod_0
完全一致

結(jié)合smethod_0
以及被調(diào)用的smethod_1
,smethod_2
可以大概知道,這段代碼的作用是把String轉(zhuǎn)換為GClass1,因此我們可以給這段代碼起名叫做String2GClass
還有這一段,一連串的類型判斷

這段代碼就是對(duì)上面得到的object進(jìn)行類型判斷,最終轉(zhuǎn)換為一個(gè)Double類型,因此可以給這段代碼取名Object2Double
區(qū)分開內(nèi)聯(lián)代碼之后,腦子里就應(yīng)該對(duì)Main的執(zhí)行流程有一個(gè)大致了解了
我用偽代碼寫一下(實(shí)際上我沒(méi)有寫,只是為了讓大家都清楚)
清理過(guò)的 Main.cs
private unsafe static void Main(){ ? ?? ???Console.Title = "CrackMe by wwh1004 2020/08/07"; ? ?? ???Console.WriteLine("輸入你的答案:"); ? ?? ???try? ?? ???{ ? ?? ?? ?? ?? ? String input = Console.WriteLine(); ? ?? ?? ?? ?? ? GClass1 gclass = String2GClass(input); ? ?? ?? ?? ?? ? double num = 0.0; ? ?? ?? ?? ?? ? /* 以 GClass0.smethod_6 為主的一串代碼 */? ?? ?? ?? ?? ? object obj = gclass; ? ?? ?? ?? ?? ? object obj2 = 1; ? ?? ?? ?? ?? ? object obj3 = obj2; ? ?? ?? ?? ?? ? object obj4 = obj; ? ?? ?? ?? ?? ? GClass0.GClass1 gclass2 = obj4 as GClass0.GClass1; ? ?? ?? ?? ?? ? object obj5 = (gclass2 != null) ? GClass0.smethod_6(gclass2, new object[] ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?num ? ?? ?? ?? ?? ? }) : obj4; ? ?? ?? ?? ?? ? double num7 = Object2Double(obj5); ? ?? ?? ?? ?? ? /* 同上,以 GClass0.smethod_6 為主的一串代碼 */? ?? ?? ?? ?? ? GClass0.GClass1 gclass3 = obj3 as GClass0.GClass1; ? ?? ?? ?? ?? ? obj5 = ((gclass3 != null) ? GClass0.smethod_6(gclass3, new object[] ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?num ? ?? ?? ?? ?? ? }) : obj3); ? ?? ?? ?? ?? ? double num8 = Object2Double(obj5); ? ?? ?? ?? ?? ? /* 判斷語(yǔ)句,條件是莫名其妙的1E-05 */? ?? ?? ?? ?? ? GClass0.GClass1 gclass4; ? ?? ?? ?? ?? ? if (Math.Abs((num8 - num7) / num7) >= 1E-05) ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?gclass4 = null; ? ?? ?? ?? ?? ? } ? ?? ?? ?? ?? ? else? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?/* 構(gòu)造GClass1 */? ?? ?? ?? ?? ?? ?? ?? ?GClass0.GClass1 gclass5 = new GClass0.GClass1 ? ?? ?? ?? ?? ?? ?? ?? ?{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???byte_0 = 22, ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_0 = gclass, //把object_0修改為了input得到的gclass? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_1 = 0? ?? ?? ?? ?? ?? ?? ?? ?}; ? ?? ?? ?? ?? ?? ?? ?? ?text2 = "100412041404070000000000041004120002000000040700000000000003000000"; ? ?? ?? ?? ?? ?? ?? ?? ?GClass0.GClass1 gclass6 = String2GClass(text2); ? ?? ?? ?? ?? ?? ?? ?? ?gclass6.object_1 = gclass; //把object_1修改為了input得到的gclass? ?? ?? ?? ?? ?? ?? ?? ?for (double num9 = 0.0; num9 < 100.0; num9 += 0.1) ? ?? ?? ?? ?? ?? ?? ?? ?{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 以 GClass0.smethod_6 為主的一串代碼 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object obj6 = gclass5; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object obj7 = gclass6; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???num = num9; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj3 = obj7; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj4 = obj6; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???gclass2 = (obj4 as GClass0.GClass1); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj5 = ((gclass2 != null) ? GClass0.smethod_6(gclass2, new object[] ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? num ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}) : obj4); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???double num7 = Object2Double(obj5); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 以 GClass0.smethod_6 為主的一串代碼 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???gclass3 = (obj3 as GClass0.GClass1); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj5 = ((gclass3 != null) ? GClass0.smethod_6(gclass3, new object[] ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? num ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}) : obj3); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???double num11 = Object2Double(obj5) ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 判斷語(yǔ)句,條件也是莫名其妙的1E-05 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (Math.Abs((num11 - num7) / num7) >= 1E-05) ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? gclass4 = null; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? goto IL_3CC; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???} ? ?? ?? ?? ?? ?? ?? ?? ?} ? ?? ?? ?? ?? ?? ?? ?? ?gclass4 = gclass; ? ?? ?? ?? ?? ? } ? ?? ?? ?? ?? ? IL_3CC: ? ?? ?? ?? ?? ? GClass0.GClass1 gclass1_ = gclass4; ? ?? ?? ?? ?? ? byte[] bytes = BitConverter.GetBytes((double)GClass0.smethod_6(gclass1_, new object[] ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?20.0? ?? ?? ?? ?? ? })); ? ?? ?? ?? ?? ? byte[] bytes2 = BitConverter.GetBytes((double)GClass0.smethod_6(gclass1_, new object[] ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?20.0? ?? ?? ?? ?? ? })); ? ?? ?? ?? ?? ? byte[] bytes3 = BitConverter.GetBytes((double)GClass0.smethod_6(gclass1_, new object[] ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?8.0? ?? ?? ?? ?? ? })); ? ?? ?? ?? ?? ? Array bytes4 = BitConverter.GetBytes((double)GClass0.smethod_6(gclass1_, new object[] ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?7.0? ?? ?? ?? ?? ? })); ? ?? ?? ?? ?? ? byte[] array3 = new byte[32]; ? ?? ?? ?? ?? ? Buffer.BlockCopy(bytes, 0, array3, 0, 8); ? ?? ?? ?? ?? ? Buffer.BlockCopy(bytes2, 0, array3, 8, 8); ? ?? ?? ?? ?? ? Buffer.BlockCopy(bytes3, 0, array3, 16, 8); ? ?? ?? ?? ?? ? Buffer.BlockCopy(bytes4, 0, array3, 24, 8); ? ?? ?? ?? ?? ? byte[] bytes5; ? ?? ?? ?? ?? ? using (Aes aes = Aes.Create()) ? ?? ?? ?? ?? ? { ? ?? ?? ?? ?? ?? ?? ?? ?aes.Mode = CipherMode.ECB; ? ?? ?? ?? ?? ?? ?? ?? ?aes.Key = array3; ? ?? ?? ?? ?? ?? ?? ?? ?byte[] array4 = new byte[] ? ?? ?? ?? ?? ?? ?? ?? ?{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???······ ? ?? ?? ?? ?? ?? ?? ?? ?}; ? ?? ?? ?? ?? ?? ?? ?? ?using (ICryptoTransform cryptoTransform = aes.CreateDecryptor()) ? ?? ?? ?? ?? ?? ?? ?? ?{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???bytes5 = cryptoTransform.TransformFinalBlock(array4, 0, array4.Length); ? ?? ?? ?? ?? ?? ?? ?? ?} ? ?? ?? ?? ?? ?? ?? ?? ?goto IL_51B; ? ?? ?? ?? ?? ? } ? ?? ?? ?? ?? ? goto IL_503; ? ?? ?? ?? ?? ? IL_51B: ? ?? ?? ?? ?? ? Console.WriteLine("你成功了:" + Encoding.Unicode.GetString(bytes5)); ? ?? ?? ?? ?? ? goto IL_545; ? ?? ?? ?? ?? ? IL_503: ? ?? ?? ?? ?? ? throw new NotSupportedException(); ? ?? ???} ? ?? ???catch? ?? ???{ ? ?? ?? ?? ?? ? Console.WriteLine("你失敗了。"); ? ?? ???} ? ?? ???IL_545: ? ?? ???Console.ReadKey(true); }
至此,Main
中大部分的代碼都被我們給分類了,可以發(fā)現(xiàn)smethod_6
為主的代碼段非常重要,因此我們需要分析一下smethod_6
的具體作用!
仔細(xì)分析函數(shù)的作用
這個(gè)smethod_6
我剛看到直接就被閃瞎了眼……亂七八糟的根本就不想看
但是既然知道smethod_6
非常重要,我們就不得不面對(duì)它了
處理goto語(yǔ)句
這個(gè)smethod_6
有很多的goto語(yǔ)句到處跳轉(zhuǎn),讓人看的心煩。因此,我們首先吧goto處理掉。
先把整個(gè)smethod_6
的代碼復(fù)制出來(lái),找一個(gè)好用的文本編輯器貼進(jìn)去(我用的是Notepad++)
然后找一個(gè)有g(shù)oto需要處理的地方

看到這個(gè)goto IL_AB4
了吧?我們來(lái)找IL_AB4
對(duì)應(yīng)的代碼

找到對(duì)應(yīng)代碼之后,直接復(fù)制,粘貼到goto語(yǔ)句的位置

貼過(guò)去之后又找到有goto IL_A76
,如法炮制,繼續(xù)復(fù)制粘貼,直到你能看懂程序到底在做什么為止……
處理過(guò)程中要注意 復(fù)制的代碼中可能還有g(shù)oto語(yǔ)句 if之后的goto語(yǔ)句就有可能是else
總之,就是腦子清楚一點(diǎn),把邏輯弄正確就行了……
繼續(xù)清理內(nèi)聯(lián)代碼
清理完goto語(yǔ)句之后,把對(duì)付Main
的方法再用一次,把重復(fù)的代碼段用函數(shù)代替。
之后就可以得到一段 可讀性比較高的 代碼了!
解釋一下smethod_6的作用
清理完成后,我們就可以知道這個(gè)函數(shù)的具體作用了。
這個(gè)smethod_6
有點(diǎn)復(fù)雜,可能得多看幾次才能明白……
smethod_6
的作用就像是一個(gè)計(jì)算器,而輸入的算式使用類似于 波蘭表達(dá)式(前置表達(dá)式) 的形式表達(dá)的,而這個(gè)表達(dá)式在程序中,是被GClass1
存儲(chǔ)的。
先舉個(gè)栗子吧:
隨便寫一個(gè)運(yùn)算式?5*6
,然后我們把它改寫成先運(yùn)算符號(hào),再兩個(gè)操作數(shù)的形式
比如?5*6
改寫成{*;5;6}
,*
,5
,6
分別叫做運(yùn)算類型
,第一操作數(shù)
,第二操作數(shù)
,
表達(dá)式也可以成為操作數(shù),比如說(shuō)((2+3)*(x^4))/e^x
可以寫成{/;(2+3)*(x^4);e^x}
雖然寫可以這樣寫,但是實(shí)際運(yùn)算要符合數(shù)學(xué)運(yùn)算順序,不能先算外部再算內(nèi)部,因此,操作數(shù)
中含有非數(shù)字表達(dá)式
的波蘭表達(dá)式還需要再次展開。
繼續(xù)算剛才的表達(dá)式((2+3)*(x^4))/e^x
實(shí)際運(yùn)算是要先算value1={+;2;3}
,再算value2={^;x;4}
,然后計(jì)算value1={*;value1;value2}
,value2={^;e;x}
,最后計(jì)算result={/;value1;value2}
我們看一下GClass1的結(jié)構(gòu)
public class GClass1{
? ?? ???public byte byte_0;/* 運(yùn)算類型,具體運(yùn)算見smethod_6 */? ?? ???public object object_0;/* 第一操作數(shù),可以是數(shù)字,也可以是另一個(gè)GClass1(表達(dá)式) */? ?? ???public object object_1;/* 第二操作數(shù),可以是數(shù)字,也可以是另一個(gè)GClass1(表達(dá)式) */}
我們可以把GClass1
理解為例子中的大括號(hào),{類型;第一操作數(shù);第二操作數(shù)}
注意,操作數(shù)
可以是數(shù)字,也可以是另外一個(gè)GClass1
(表達(dá)式)
這個(gè)CM的主要內(nèi)容,便是用GClass的嵌套實(shí)現(xiàn)進(jìn)行運(yùn)算的目的
清理之后的 smethod_6
我對(duì)smethod_6
清理了goto,清理了內(nèi)聯(lián)代碼,然后對(duì)代碼進(jìn)行了一些注釋。
建議是先讀懂上面說(shuō)的波蘭表達(dá)式
,再來(lái)看smethod_6
的代碼,這樣比較好理解……
不想看的直接左邊目錄跳到下一節(jié)!
private static object smethod_6(GClass0.GClass1 gclass1_0, params object[] object_0){
? ?? ???Stack<Tuple<GClass0.GClass1, object[], object, object, bool, int>> stack = new Stack<Tuple<GClass0.GClass1, object[], object, object, bool, int>>();
? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, null, null, false, 0));
? ?? ???object obj = null;
? ?? ???for (;;)
? ?? ???{
? ?? ?? ?? ?? ? Tuple<GClass0.GClass1, object[], object, object, bool, int> tuple = stack.Pop();
? ?? ?? ?? ?? ? gclass1_0 = tuple.Item1;
? ?? ?? ?? ?? ? object_0 = tuple.Item2;
? ?? ?? ?? ?? ? object obj2 = tuple.Item3;
? ?? ?? ?? ?? ? object obj3 = tuple.Item4;
? ?? ?? ?? ?? ? bool flag = tuple.Item5;
? ?? ?? ?? ?? ? int item = tuple.Item6;
? ?? ?? ?? ?? ? switch (item)
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ? case 0:
? ?? ?? ?? ?? ?? ?? ?? ?/* item=0時(shí),GClass1中的操作數(shù)分別放到第一操作數(shù)(obj2),第二操作數(shù)(obj3),前一步驟的運(yùn)算結(jié)果(obj)不處理 */? ?? ?? ?? ?? ?? ?? ?? ?obj2 = gclass1_0.object_0;
? ?? ?? ?? ?? ?? ?? ?? ?obj3 = gclass1_0.object_1;
? ?? ?? ?? ?? ?? ?? ?? ?flag = (obj2 is GClass0.GClass1);
? ?? ?? ?? ?? ?? ?? ?? ?/* 判斷第一個(gè)操作數(shù)(obj2)是數(shù)字,還是表達(dá)式 */? ?? ?? ?? ?? ?? ?? ?? ?if (flag)
? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 如果obj2是表達(dá)式,先Push自身item=1(結(jié)果放入第一操作數(shù)的位置),并把obj2的表達(dá)式展開Push到stack上 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 1));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj2, object_0, obj2, obj3, flag, 0));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???goto IL_A66;/* 直接開始循環(huán) */? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?flag = (obj3 is GClass0.GClass1);
? ?? ?? ?? ?? ?? ?? ?? ?/* 如果obj2是表達(dá)式,會(huì)在`goto IL_A66`句直接跳轉(zhuǎn),因此執(zhí)行到這里,obj2不是表達(dá)式 */? ?? ?? ?? ?? ?? ?? ?? ?/* 第一個(gè)操作數(shù)(obj2)不是表達(dá)式,接下來(lái)判斷第二個(gè)操作數(shù)(obj3)是數(shù)字,還是表達(dá)式 */? ?? ?? ?? ?? ?? ?? ?? ?if (flag)
? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 如果obj3是表達(dá)式,先Push自身item=2(結(jié)果放入第二操作數(shù)的位置),并把obj3的表達(dá)式展開Push到stack上 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 2));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj3, object_0, obj2, obj3, flag, 0));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???goto IL_A66;/* 直接開始循環(huán) */? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_1E;/* 如果都不是表達(dá)式,開始運(yùn)算 */? ?? ?? ?? ?? ? case 1:
? ?? ?? ?? ?? ?? ?? ?? ?if (flag)
? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj2 = obj;/* 如果上一步驟運(yùn)算的東西是個(gè)表達(dá)式,把結(jié)果從obj放入obj2(因?yàn)閕tem=1) */? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?flag = (obj3 is GClass0.GClass1);
? ?? ?? ?? ?? ?? ?? ?? ?if (flag)
? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 同上,把obj3表達(dá)式展開Push到stack上 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 2));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj3, object_0, obj2, obj3, flag, 0));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???goto IL_A66;
? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_1E;/* 如果都不是表達(dá)式,開始運(yùn)算 */? ?? ?? ?? ?? ? case 2:
? ?? ?? ?? ?? ?? ?? ?? ?if (flag)
? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj3 = obj;/* 如果上一步驟運(yùn)算的東西是個(gè)表達(dá)式,把結(jié)果從obj放入obj3(因?yàn)閕tem=2) */? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_1E;
? ?? ?? ?? ?? ? case 3:
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_1E;/* 不改變obj2,obj3,還保留了上一步的結(jié)果obj,僅有case22用到 */? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? goto Block_101;
? ?? ?? ?? ?? ? IL_A66:
? ?? ?? ?? ?? ? if (stack.Count == 0)/* 如果stack上的東西都算完了 */? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?return obj;/* 返回運(yùn)算結(jié)果 */? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? continue;
? ?? ?? ?? ?? ? IL_1E:
? ?? ?? ?? ?? ? switch (gclass1_0.byte_0)/* switch 運(yùn)算類型 */? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ? /* 類型判斷被我精簡(jiǎn)過(guò)了,含義應(yīng)該自己就能看懂 */? ?? ?? ?? ?? ? /* 一元運(yùn)算 */? ?? ?? ?? ?? ? case 0:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Int(obj2);/* 把第一操作數(shù)(Tuple.Item3)轉(zhuǎn)為Int放到結(jié)果(obj)內(nèi) */? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 1:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Long(obj2);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 2:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Float(obj2);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 3:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Double(obj2);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? /* 有關(guān)傳入自變量(x)的運(yùn)算(主要就是取出自變量的值) */? ?? ?? ?? ?? ? case 4:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = (int)object_0[Object2Int(obj2)];/* 理論上,obj2可以是任何整數(shù),但是由于傳入的object_0都是只含有一個(gè)元素,所以第一操作數(shù)(obj2)必須為0 */? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 5:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = (long)object_0[Object2Int(obj2)];
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 6:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = (float)object_0[Object2Int(obj2)];
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 7:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = (double)object_0[Object2Int(obj2)];
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? /* 接下來(lái)是二元的加減乘除 */? ?? ?? ?? ?? ? case 16:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Double(obj2) + Object2Double(obj3);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 17:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Double(obj2) - Object2Double(obj3);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 18:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Double(obj2) * Object2Double(obj3);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 19:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?obj = Object2Double(obj2) / Object2Double(obj3);
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? /* 乘方類運(yùn)算 */? ?? ?? ?? ?? ? case 20:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?/* 計(jì)算e^obj2,e為自然底數(shù) */? ?? ?? ?? ?? ?? ?? ?? ?obj = Math.Exp(Object2Double(obj2));
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? case 21:
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?/* 計(jì)算乘方 obj2^obj3 */? ?? ?? ?? ?? ?? ?? ?? ?obj = Math.Pow(Object2Double(obj2), Object2Double(obj3));
? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? /* case22相當(dāng)于計(jì)算了(f(x+10^-8)-f(x))/(10^-8),也就是計(jì)算f(x)的導(dǎo)數(shù) */? ?? ?? ?? ?? ? case 22:/* 對(duì)于case22來(lái)說(shuō),第一操作數(shù)必須是表達(dá)式,Main里傳遞過(guò)來(lái)的是表達(dá)式f(x) */? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?object obj7;
? ?? ?? ?? ?? ?? ?? ?? ?if (item != 3)/* 判斷是否展開過(guò)求導(dǎo)運(yùn)算 */? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* item!=3表明沒(méi)有展開過(guò),開始展開運(yùn)算 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object[] object_1 = Copy(object_0); /* Copy是偽代碼,復(fù)制一下傳入?yún)?shù)數(shù)組object_0,復(fù)制到object_1 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???obj7 = object_0[Object2Int(obj3)]; /* 把傳入?yún)?shù)中的第(obj3)個(gè)取出(obj3只能為0) */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_1[Object2Int(obj3)] = Object2Double(obj7) + 1E-08;/*??把取出的值加上1E-08, 即0.00000001,一個(gè)足夠小的值*/? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 3));/* 此時(shí)壓入f(x)表達(dá)式,item=3指示已經(jīng)展開過(guò)求導(dǎo)運(yùn)算 */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)gclass1_0.object_0, object_1, obj2, obj3, flag, 0));
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* 把f(x)展開后壓入,由于object_1中的自變量被加上了1E-08,因此實(shí)際運(yùn)算的是f(x+1E-08) */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???goto IL_A66;
? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?obj = (Object2Double(obj) - Object2Double(obj2)) / 1E-08;/* obj是 后Push的f(x+1E-08)的值;obj2是 先Push的f(x)展開式的值 */? ?? ?? ?? ?? ?? ?? ?? ?/* 最終求得導(dǎo)數(shù)f'(x) */? ?? ?? ?? ?? ?? ?? ?? ?goto IL_A66;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? break;
? ?? ???}
}
回頭繼續(xù)分析Main
分析完了十分重要的smethod_6
,我們就是回頭來(lái)繼續(xù)看Main
我們知道,輸入的字符串會(huì)被String2GClass
(實(shí)際為smethod_0
)轉(zhuǎn)換為GClass1
而GClass1
是波蘭表達(dá)式形式的表達(dá)式(突然覺(jué)得我沒(méi)學(xué)過(guò)語(yǔ)文XD)
所以我們輸入的東西,實(shí)際上是一個(gè)函數(shù),我們可以把它記作f(x)
剛才分析過(guò)了smethod_6
,知道它的作用是把波蘭表達(dá)式完全展開,求出結(jié)果。Main
當(dāng)中調(diào)用smethod_6
的地方就可以理解為計(jì)算表達(dá)式的值了!
我們來(lái)看第一次調(diào)用smethod_6
的地方
? ?? ?? ?? ?? ? object obj5 = (gclass2 != null) ? GClass0.smethod_6(gclass2, new object[]
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?num /* 此時(shí)num=0.0 */? ?? ?? ?? ?? ? }) : obj4
這個(gè)gclass2是我們輸入的f(x)
GClass1
傳入的num是自變量x
,這就相當(dāng)于obj5 = f(0)
接下來(lái)是一串類型轉(zhuǎn)換Object2Double
后面再次出現(xiàn)了熟悉的smethod_6
和Object2Double
,不過(guò)這次參與運(yùn)算的表達(dá)式不是f(x)
,而是null(因?yàn)閲L試把int 1轉(zhuǎn)換為GClass1,轉(zhuǎn)換失敗得到了null)
這次smethod_6
運(yùn)算的結(jié)果是1
來(lái)到后面的判斷語(yǔ)句
? ?? ?? ?? ?? ? /* num8 = `1`, num7 = `f(0)` */? ?? ?? ?? ?? ? if (Math.Abs((num8 - num7) / num7) >= 1E-05)
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?gclass4 = null;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? else? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?······
? ?? ?? ?? ?? ? }
如果if成立,那么就會(huì)執(zhí)行g(shù)class4=null,會(huì)導(dǎo)致后面出現(xiàn)NullPointerException
根據(jù)條件1
,出現(xiàn)Exception會(huì)導(dǎo)致失敗,所以if不可以成立
這個(gè)語(yǔ)句看起來(lái)是在要求|(1-f(0))/f(0)|<0.00001
,但是后來(lái)想想1E-05貌似也沒(méi)什么意義,應(yīng)該是判斷等于0的意思
后來(lái)出題大佬也解釋了,由于double精度不夠,所以采取了這種判斷方式。
所以利用數(shù)學(xué)知識(shí)可以解得一個(gè)函數(shù)需要滿足的條件f(0)=1
構(gòu)造一個(gè)符合f(0)=1
的表達(dá)式之后,就可以進(jìn)入else分支
看到程序內(nèi)構(gòu)造了一個(gè)GClass1
? ?? ?? ?? ?? ?? ?? ?? ?GClass0.GClass1 gclass5 = new GClass0.GClass1
? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???byte_0 = 22,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_0 = gclass,/* gclass就是輸入的f(x) */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_1 = 0/* 必須為0 */? ?? ?? ?? ?? ?? ?? ?? ?};
這個(gè)byte_0=22是求導(dǎo)的意思,具體展開方式都在smethod_6
當(dāng)中,還算是比較清楚的!
所以得到了此處的表達(dá)式d/dx(f(x))
接下來(lái)又出現(xiàn)了一串字符串,這個(gè)字符串會(huì)被處理成GClass(就和我們輸入的內(nèi)容一樣)
注意,這里把gclass1中的object_1(第二操作數(shù))替換成了我們輸入的f(x)
根據(jù)?波蘭表達(dá)式
?解讀GClass便可以得到第二個(gè)表達(dá)式?e^x*(2x+3)+f(x)
之后就進(jìn)入了一個(gè)for循環(huán),x的取值遍歷了0-100之間每個(gè)0.1,共1000個(gè)值d/dx(f(x))
和e^x*(2x+3)+f(x)
會(huì)被賦予自變量x的值,并且求出這兩個(gè)表達(dá)式的具體結(jié)果
對(duì)這兩個(gè)表達(dá)式的具體結(jié)果進(jìn)行判斷
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* num11 = `e^x*(2x+3)+f(x)` */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???/* num7 = `d/dx(f(x))` */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (Math.Abs((num11 - num7) / num7) >= 1E-05)
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? gclass4 = null;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? goto IL_3CC;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
這里的if同樣不可以成立,并且由于if處于for循環(huán)內(nèi)部,也就是說(shuō)對(duì)于任意滿足0<x<100的x都要有?((e^x*(2x+3)+f(x))-(d/dx(f(x))))/(d/dx(f(x))) < 0.00001
?成立
這個(gè)1E-05也是等于0的意思,因此條件可以化簡(jiǎn)為?e^x*(2x+3)+f(x) = d/dx(f(x))
做數(shù)學(xué)題
整理一下,我們目前有兩個(gè)條件:f(0) = 1
e^x*(2x+3)+f(x) = d/dx(f(x))
接下來(lái)需要求出f(x)
的表達(dá)式
雖然我和出題大佬同樣都是今年高三畢業(yè),但是我們高中并沒(méi)有教導(dǎo)數(shù)23333333
然后…這個(gè)東西我就不會(huì)做了
一個(gè)錯(cuò)誤的想法
我最開始在做這道題的時(shí)候,并沒(méi)想到(f(x+10^-8)-f(x))/(10^-8)
的意思是d/dx(f(x))
因此,我的做法就變成了f(x+10^-8)=e^x*(2x+3)*10^-8+(1+10^-8)*f(x)
,把這個(gè)式子理解為遞推公式,加上已知的f(0)=1
,嘗試推算出通項(xiàng)公式(類似于數(shù)列的思想)
然而通項(xiàng)我自己求不出來(lái),去WolframAlpha上算了一下,如果帶上f(0)=1
,它告訴我無(wú)解
不帶f(0)=1
的話,解出來(lái)的結(jié)果直接把我嚇傻了……

所以有沒(méi)有人能幫我解答一下,我這個(gè)想法錯(cuò)在哪里?
(高三結(jié)束的暑假,老師和同學(xué)都無(wú)心學(xué)習(xí),問(wèn)不到學(xué)霸了……)
偷懶的做法
由于d/dx(f(x))
可以寫成f'(x)
,因此到網(wǎng)上搜一下e^x*(2x+3)+f(x)=f'(x)
從百度知道的問(wèn)題當(dāng)中獲得題目圖片一張

但是解答里面并沒(méi)有f(x)
的表達(dá)式
看不懂導(dǎo)數(shù)的我只能小猿搜題拍一下

在一串不太能看懂的解答里找到一行救命稻草:f(x)=(x^2+3x+1)*e^x
好了,數(shù)學(xué)題的部分就結(jié)束了(無(wú)知地跳過(guò)了……)
構(gòu)造答案表達(dá)式
所以我們只要構(gòu)造出這個(gè)表達(dá)式的GClass,然后調(diào)用CM里的smethod_3轉(zhuǎn)化為字符串就行了
演示一下:
? ?? ?? ?? ?? ?? ?? ?? ?new GClass1() {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???byte_0 = 18,/* 乘法 {*;x^2+3x+1;e^x} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_0 = new GClass1() {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? byte_0=16,/* 加法 {+;x^2;3x+1} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? object_0= new GClass1()
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?byte_0 = 21,/* 乘方 {^;x;2} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?object_0 = new GClass1()
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???byte_0 = 7,/* 取值 {獲取x的值} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_0 = 0,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_1 = null? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?},
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?object_1 = 2? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? },
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? object_1=new GClass1() {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?byte_0=16,/* 加法 {+;1;3x} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?object_0=1,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?object_1=new GClass1() {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???byte_0=18,/* 乘法 {*;3;x} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_0=3,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_1=new GClass1()
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? byte_0 = 7,/* 取值 {獲取x的值} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? object_0 = 0? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???},
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???object_1= new GClass1()
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? byte_0 = 20,/* e乘方 {e^;x} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? object_0 = new GClass1()
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?byte_0 = 7,/* 取值 {獲取x的值} */? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?object_0 = 0,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?object_1 = null? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?}
這個(gè)GClass轉(zhuǎn)換成字符串的結(jié)果就是1204100415040700000000000002000000041000010000000412000300000004070000000000041404070000000000
輸入到程序里面驗(yàn)證一下

OK,可以收工了!
原文地址:https://www.52pojie.cn/thread-1242221-1-1.html
--官方論壇
www.52pojie.cn