WCTF2018 truth

TL;DR

WCTF2018的一道.NET逆向題硝全,利用了.NET即時(shí)編譯(JIT)機(jī)制批糟,動(dòng)態(tài)修改了Func3函數(shù)的函數(shù)指針赚爵,寫了一段脫殼代碼進(jìn)去,異或解密了了一段匯編并將Func4和Func5的函數(shù)指針指過去村怪,從而修改了Func4和Func5的邏輯秽浇。

Solve

注:得到了FAKEFLAG的話可以跳過PART1

PART1 FAKEFLAG

使用dnSpy反編譯程序,發(fā)現(xiàn)在Main函數(shù)中調(diào)用了Lib.Verify函數(shù)驗(yàn)證了讀取的字符串:

private static void Main()
{
    Console.Write("Enter your flag: ");
    if (Lib.Verify(Console.ReadLine().Trim()))
    {
        Console.WriteLine("Great :)");
        return;
    }
    Console.WriteLine("Wrong :(");
}

進(jìn)一步觀察Lib.Verify函數(shù):

public static bool Verify(string s)
{
    byte[] bytes = Encoding.ASCII.GetBytes(s);
    if (bytes.Length != 32)
    {
        return false;
    }
    byte[] array = Lib.Func2();
    Lib.Func3(array, bytes);
    Lib.Func4(array, bytes);
    Lib.Func5(array, bytes);
    for (int i = 0; i < 32; i++)
    {
        if (bytes[i] != array[3104 + i])
        {
            return false;
        }
    }
    return true;
}

我們的輸入保存在bytes中甚负,首先驗(yàn)證了其長(zhǎng)度要為32柬焕,然后通過Func2函數(shù)生成了一個(gè)byte數(shù)組array,之后依次調(diào)用了Func3梭域、Func4斑举、Func5三個(gè)函數(shù)對(duì)bytesarray進(jìn)行了操作,最后將bytes的最終值與array后面的一段進(jìn)行了比較碰辅。
于是我們首先看Func2函數(shù)是如何生成array數(shù)組的:

public static byte[] Func2()
{
    AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider();
    aesCryptoServiceProvider.BlockSize = 128;
    aesCryptoServiceProvider.KeySize = 128;
    aesCryptoServiceProvider.Mode = CipherMode.CBC;
    aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
    byte[] result;
    using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(Resources.bin)))
    {
        aesCryptoServiceProvider.IV = binaryReader.ReadBytes(16);
        aesCryptoServiceProvider.Key = binaryReader.ReadBytes(16);
        using (CryptoStream cryptoStream = new CryptoStream(binaryReader.BaseStream, aesCryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Read))
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                cryptoStream.CopyTo(memoryStream);
                result = memoryStream.ToArray();
            }
        }
    }
    return result;
}

可以看出懂昂,這段代碼其中就是讀取了一段資源(Resources.bin),并對(duì)其進(jìn)行了AES解密没宾。解密用的IV和key也在其中凌彬。我們可以自己寫代碼解密,也可以直接動(dòng)態(tài)調(diào)試拿到循衰。
之后我們?cè)倏戳硗馊齻€(gè)Func:

public static void Func3(byte[] b, byte[] x)
{
    byte[] array = new byte[32];
    for (int i = 0; i < 32; i++)
    {
        array[i] = (byte)b.Skip(i * 32).Take(32).Zip(x, (byte x1, byte x2) => (int)(x1 * x2)).Sum();
    }
    Array.Copy(array, x, 32);
}
public static void Func4(byte[] b, byte[] x)
{
    byte[] array = new byte[32];
    for (int i = 0; i < 32; i++)
    {
        array[i] = (byte)b.Skip(1024 + i * 32).Take(32).Zip(x, (byte x1, byte x2) => (int)(x1 * x2)).Sum();
    }
    Array.Copy(array, x, 32);
}
public static void Func5(byte[] b, byte[] x)
{
    byte[] array = new byte[32];
    for (int i = 0; i < 32; i++)
    {
        array[i] = (byte)(b.Skip(2048 + i * 32).Take(32).Zip(x, (byte x1, byte x2) => (int)(x1 * x2)).Sum() + (int)b[3072 + i]);
    }
    Array.Copy(array, x, 32);
}

這三個(gè)函數(shù)的邏輯都非常清晰铲敛,在32次循環(huán)中,每次從b數(shù)組(即之前aes解密出的array)讀取32個(gè)byte会钝,分別與x(即我們輸入的bytes)的對(duì)應(yīng)byte做乘法伐蒋,用最終的sum構(gòu)建一個(gè)新數(shù)組,最后復(fù)制回x數(shù)組迁酸。所以很明顯先鱼,這就是將array中的一段數(shù)據(jù)作為一個(gè)32*32矩陣,將我們的輸入作為一個(gè)32*1的向量奸鬓,做了一個(gè)矩陣乘法焙畔。注意Func5最后在結(jié)果上又加了一個(gè)向量。
有了這些邏輯串远,我們就很容易實(shí)現(xiàn)出一個(gè)逆過程宏多,求出三個(gè)矩陣的逆儿惫,把過程反過來進(jìn)行一次就好了。然而伸但,最后我們的程序得到了一段FAKEFLAGFAKEFLAGFAKEFLAGFAKEFLAG肾请,嘗試在程序中輸入也無法通過。那么是哪里出了問題呢更胖?

PART2 - REAL FLAG

為了尋找問題所在铛铁,我們首先嘗試用DNSPY動(dòng)態(tài)調(diào)試,發(fā)現(xiàn)Func3的返回值雖然與我們預(yù)期的一樣函喉,但是在步入Func3時(shí)卻沒有進(jìn)Func3避归,反而是進(jìn)了Func2荣月。另外Func4和Func5中的斷點(diǎn)都沒有進(jìn)去管呵,函數(shù)的返回結(jié)果也與我們的預(yù)期不一樣。
于是我們猜測(cè)哺窄,一定有某個(gè)地方對(duì)這幾個(gè)函數(shù)做了手腳捐下。于是我們從頭把代碼看了一遍,最終在Resources.Resources()中看到了一段可疑的代碼:

unsafe static Resources()
{
    IntPtr intPtr = ldftn(Func) - 16;
    long num = *intPtr;
    IntPtr intPtr2 = ldftn(Func) - 8;
    long num2 = *intPtr2;
    ref long ptr = ldftn(Func) - 16;
    IntPtr intPtr3 = ldftn(Func) + 5;
    long num3 = (long)(*(intPtr3 + 1));
    ptr = *(intPtr3 + (IntPtr)(((int)(*(intPtr3 + 2)) << 3) + 3)) + (num3 << 3);
    ref long ptr2 = ldftn(Func) - 8;
    object obj = *(ldftn(Func) - 16);
    object obj2;
    for (;;)
    {
        obj2 = obj;
        if (*obj2 == 5)
        {
            break;
        }
        obj = obj2 + 16;
    }
    ptr2 = *(obj2 + 8);
    long num4 = *(ldftn(Func) - 8);
    *num4 = 6293447916875450697L;
    long num5 = num4 + 8L;
    *num5 = 996842507592L;
    long num6 = num5 + 8L;
    *num6 = -5023708761407594752L;
    long num7 = num6 + 8L;
    *num7 = 2247216228701921188L;
    long num8 = num7 + 8L;
    *num8 = 5195160555404404409L;
    long num9 = num8 + 8L;
    *num9 = 543045289092056715L;
    long num10 = num9 + 8L;
    *num10 = 612363414786457928L;
    long num11 = num10 + 8L;
    *num11 = 5245003925894368584L;
    long num12 = num11 + 8L;
    *num12 = 3816147333L;
    long num13 = num12 + 8L;
    object obj3 = *(ldftn(Func) - 16);
    object obj4;
    for (;;)
    {
        obj4 = obj3;
        if (*obj4 == 6)
        {
            break;
        }
        obj3 = obj4 + 16;
    }
    *(obj4 + 8) = *(ldftn(Func) - 8);
    object obj5 = *(ldftn(Func) - 16);
    object obj6;
    for (;;)
    {
        obj6 = obj5;
        if (*obj6 == 7)
        {
            break;
        }
        obj5 = obj6 + 16;
    }
    *(obj6 + 8) = *(ldftn(Func) - 8) + 89L;
    object obj7 = *(ldftn(Func) - 16);
    object obj8;
    for (;;)
    {
        obj8 = obj7;
        if (*obj8 == 8)
        {
            break;
        }
        obj7 = obj8 + 16;
    }
    *(obj8 + 8) = *(ldftn(Func) - 8) + 170L;
    *intPtr2 = num2;
    *intPtr = num;
}

首先我們可以注意到9個(gè)奇怪的long萌业,這些數(shù)值被寫到了*(ldftn(Func) - 8)開始的一段地址中坷襟。
之后的3個(gè)for循環(huán),分別從*(ldftn(Func) - 16)開始生年,找到06婴程、07和08開始的16個(gè)byte,并把后8byte分別改為*(ldftn(Func) - 8)抱婉、*(ldftn(Func) - 8) + 89*(ldftn(Func) - 8) + 170的地址档叔。聯(lián)想到之前Func3、Func4蒸绩、Func5函數(shù)無法正常調(diào)試且邏輯被修改衙四,我們就可以自然地想到就是在這里修改了這三個(gè)函數(shù)的指針。而且06患亿、07传蹈、08也分別對(duì)應(yīng)了Func3、Func4步藕、Func5 token的最后一個(gè)byte:

image.png

進(jìn)一步分析函數(shù)指針被修改后指向的位置惦界,F(xiàn)unc3的指針被改到了*(ldftn(Func) - 16),而這就是之前9個(gè)long寫到的地方咙冗≌赐幔可以猜測(cè)這9個(gè)long就是Func3函數(shù)的實(shí)際代碼。我們?cè)儆?jì)算一下這9個(gè)long的總長(zhǎng)度:9*8=72乞娄,而Func4指向的是*(ldftn(Func) - 8) + 89瞬逊,顯然是在修改的9個(gè)long的后面显歧。那這部分的代碼是從哪里來的呢?為了搞清具體邏輯确镊,我們?cè)龠M(jìn)行動(dòng)態(tài)調(diào)試士骤,把斷點(diǎn)下在Resources構(gòu)造函數(shù)后面(不知道為什么Resources函數(shù)里面無法下斷點(diǎn)),在內(nèi)存中搜索找到這9個(gè)long的地址:

我們發(fā)現(xiàn)了一段疑似的匯編代碼蕾域。重新調(diào)試一次拷肌,把斷點(diǎn)設(shè)在Resources函數(shù)前面,將這段內(nèi)存的內(nèi)容前后對(duì)比一下:

發(fā)現(xiàn)確實(shí)就是修改了這段匯編代碼的開頭旨巷,并且把三個(gè)函數(shù)的指針指到了這段匯編代碼中巨缘。
于是我們Dump下這段內(nèi)存,用IDA查看采呐,發(fā)現(xiàn)這段匯編其實(shí)就是Func2函數(shù)(那個(gè)AES解密函數(shù))若锁,只是修改了開頭的一部分。但是很顯然這兩部分并無法拼合在一起斧吐,而且也無法想象Func4和Func5其實(shí)只是執(zhí)行Func2的一部分又固,所以這段代碼應(yīng)該不是最終運(yùn)行的代碼。
于是我們這次把斷點(diǎn)下載Func4和Func5執(zhí)行后煤率,然后再嘗試Dump這段匯編:

發(fā)現(xiàn)后面的代碼被更新了仰冠,然后Func4和Func5的入口看起來也比較正常了。這次我們?cè)儆肐DA打開蝶糯,邏輯就十分清晰了:

Func3前面一段代碼不會(huì)影響返回結(jié)果(實(shí)際上是脫殼代碼洋只,后面會(huì)詳細(xì)分析這部分),只在最后做了一個(gè)矩陣乘法昼捍,跟原來的Func3邏輯是一樣的识虚。

Func4對(duì)矩陣做了轉(zhuǎn)置,并進(jìn)行了5次矩陣乘法端三,并在最后將把向量里的值都異或了0x5A舷礼。

Func5中則是將向量異或了Func4中矩陣的前32個(gè)Byte,再進(jìn)行矩陣乘法郊闯。這兩個(gè)操作循環(huán)重復(fù)10次妻献。
整體邏輯跟之前相差不大,貼一份robin大佬的解題代碼团赁,運(yùn)行得到真Flag:flag{884RN4SoqUt9Cu87pVSPG0ndA8}

I=Integers(256)

def func1(mat,input):
    
    return mat*input.T

def func1_inv(mat,input):
    return (~mat)*input

def func2(mat,input):
    for i in range(5):
        input=mat.T*input
    input=[int(x[0]) for x in input]
    return [x^^0x5a for x in input]

def func2_inv(mat,input):
    input=[x^^0x5a for x in input]
    input=matrix(I,input)
    input=input.T
    for i in range(5):
        input=(~mat.T)*input
    return input
def func3(mat,input,iv):
    
    for i in range(10): 
        input=[x^^y for x,y in zip(input,iv)]
        input=matrix(I,input)
        input=mat*input.T
        input=[int(x[0]) for x in input]
    return input
def func3_inv(mat,input,iv):
    for i in range(10):
        input=matrix(I,input)
        input=(~mat)*(input.T)
        input=[int(x[0]) for x in input]
        input=[x^^y for x,y in zip(input,iv)]
    return input
f=open("bin","rb")
data=f.read()
f.close()
m1=map(ord,data[:1024])
m2=map(ord,data[1024:1024*2])
m3=map(ord,data[1024*2:1024*3])
m4=map(ord,data[1024*3:1024*3+32])
m5=map(ord,data[1024*3+32:1024*32+64])
input=[48]*32
input=matrix(I,input)
ma1=[m1[i*32:i*32+32] for i in range(32)]
ma2=[m2[i*32:i*32+32] for i in range(32)]
ma3=[m3[i*32:i*32+32] for i in range(32)]
ma1=matrix(I,ma1)
ma2=matrix(I,ma2)
ma3=matrix(I,ma3)
rm1= func1(ma1,input)
assert( input.T == func1_inv(ma1,rm1))
rm2=func2(ma2, rm1)
assert( func2_inv(ma2,rm2)==rm1)
rm3=func3(ma3,rm2,[int(x) for x in ma2[0]])
assert( func3_inv(ma3,rm3,[int(x) for x in ma2[0]])==rm2)

re2=func3_inv(ma3,m5,[int(x) for x in ma2[0]])
re1=func2_inv(ma2,re2)
re0=func1_inv(ma1,re1)
re=[int(x[0]) for x in re0]
flag=map(chr,re)
print "".join(flag)

Appendix

這道題雖然解決了育拨,但其中還有一些細(xì)節(jié)我們還是沒有搞懂。例如為什么.NET程序的函數(shù)最終指向一段匯編欢摄,F(xiàn)unc5和Func6的匯編代碼是從哪里出來等熬丧。在比賽后,我們又進(jìn)行了一點(diǎn)簡(jiǎn)單的探索怀挠。

.NET即時(shí)編譯機(jī)制

.NET中的C#析蝴、VB.NET害捕、F#等語(yǔ)言的編譯過程并不是像C/C++一樣直接編譯出原生代碼,而是首先編譯成IL中間語(yǔ)言闷畸,在運(yùn)行時(shí)尝盼,再由JIT (Just-In-Time) compiler根據(jù)需要,將IL編譯成匯編代碼佑菩。我們運(yùn)行的.NET的exe中實(shí)際上保存的就是IL盾沫,而不是匯編代碼:


image.png

實(shí)際執(zhí)行中,函數(shù)的調(diào)用需要通過Method Table來找到method的地址殿漠。在程序運(yùn)行的開始赴精,函數(shù)指針指向一個(gè)Stub。在函數(shù)被調(diào)用時(shí)绞幌,JIT compiler會(huì)編譯這個(gè)method并將原本指向Stub的指針指向編譯好的代碼蕾哟。于是,之后調(diào)用該函數(shù)就直接執(zhí)行這段匯編代碼啊奄。
我們可以使用WinDbg工具來幫助我們理解這一過程:

> !DumpMT -md 0007ff9ec805b00
EEClass:         00007ff9ec992068
Module:          00007ff9ec804118
Name:            WCTF2018Rev.Lib
mdToken:         0000000002000003
File:            D:\ctf\wctf\WCTF2018Rev_Release.exe
BaseSize:        0x18
ComponentSize:   0x0
Slots in VTable: 11
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007ffa4ae3c000 00007ffa4a997fb8 PreJIT System.Object.ToString()
00007ffa4aea40f0 00007ffa4a997fc0 PreJIT System.Object.Equals(System.Object)
00007ffa4af14490 00007ffa4a997fe8 PreJIT System.Object.GetHashCode()
00007ffa4ae6f390 00007ffa4a998000 PreJIT System.Object.Finalize()
00007ff9ec9100c0 00007ff9ec805af8   NONE WCTF2018Rev.Lib..ctor()
00007ff9ec910500 00007ff9ec805a98    JIT WCTF2018Rev.Lib.Verify(System.String)
00007ff9ec910098 00007ff9ec805aa8   NONE WCTF2018Rev.Lib.Func(Int32)
00007ff9ec9100a0 00007ff9ec805ab8   NONE WCTF2018Rev.Lib.Func2()
00007ff9ec9100a8 00007ff9ec805ac8   NONE WCTF2018Rev.Lib.Func3(Byte[], Byte[])
00007ff9ec9100b0 00007ff9ec805ad8   NONE WCTF2018Rev.Lib.Func4(Byte[], Byte[])
00007ff9ec9100b8 00007ff9ec805ae8   NONE WCTF2018Rev.Lib.Func5(Byte[], Byte[])

觀察Verify函數(shù)中渐苏,在運(yùn)行Func2之前的Method Table掀潮。內(nèi)存中Method Table的格式是每個(gè)函數(shù)8個(gè)byte的MethodDesc加上后面8個(gè)byte的Method Entry(就是函數(shù)指針菇夸,之后稱Method Entry):

00007ff9`ec805a98 03 00 00 21 05 00 28 00 00 05 91 ec f9 7f 00 00  (Verify)
00007ff9`ec805aa8 04 00 02 20 06 00 28 00 98 00 91 ec f9 7f 00 00  (Func)
00007ff9`ec805ab8 05 00 04 20 07 00 28 20 a0 00 91 ec f9 7f 00 00  (Func2)
00007ff9`ec805ac8 06 00 06 20 08 00 28 00 a8 00 91 ec f9 7f 00 00  (Func3)
00007ff9`ec805ad8 07 00 08 20 09 00 28 00 b0 00 91 ec f9 7f 00 00  (Func4)
00007ff9`ec805ae8 08 00 0a 20 0a 00 28 20 b8 00 91 ec f9 7f 00 00  (Func5)

還沒有被JIT編譯的函數(shù),其Method Entry指向一個(gè)Stub(在WinDbg中顯示為NONE)

> dd 00007ff9ec910098
00007ff9`ec910098  614313e8 05025e5f (Stub of Func) 61430be8 04045e5f (Stub of Func2)
00007ff9`ec9100a8  614303e8 03065e5f (Stub of Func3) 6142fbe8 02085e5f (Stub of Func4)
00007ff9`ec9100b8  6142f3e8 010a5e5f (Stub of Func5) 6142ebe8 000c5e5f

由于Verify函數(shù)已經(jīng)被執(zhí)行仪吧,所以已經(jīng)被JIT編譯庄新,其Method Entry指向其匯編實(shí)現(xiàn),而其他函數(shù)都指向Stub薯鼠。
如果我們完整地運(yùn)行完程序(Func3择诈、Func4、Func5均已執(zhí)行完):

> !DumpMT -md 00007ff9ec835b00
EEClass:         00007ff9ec9c2068
Module:          00007ff9ec834118
Name:            WCTF2018Rev.Lib
mdToken:         0000000002000003
File:            D:\ctf\wctf\WCTF2018Rev_Release.exe
BaseSize:        0x18
ComponentSize:   0x0
Slots in VTable: 11
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007ffa4ae3c000 00007ffa4a997fb8 PreJIT System.Object.ToString()
00007ffa4aea40f0 00007ffa4a997fc0 PreJIT System.Object.Equals(System.Object)
00007ffa4af14490 00007ffa4a997fe8 PreJIT System.Object.GetHashCode()
00007ffa4ae6f390 00007ffa4a998000 PreJIT System.Object.Finalize()
00007ff9ec9400c0 00007ff9ec835af8   NONE WCTF2018Rev.Lib..ctor()
00007ff9ec940500 00007ff9ec835a98    JIT WCTF2018Rev.Lib.Verify(System.String)
00007ff9ec940098 00007ff9ec835aa8   NONE WCTF2018Rev.Lib.Func(Int32)
00007ff9ec9405c0 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2()
00007ff9ec9405c0 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2() (shoule be Func3)
00007ff9ec940619 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2() (shoule be Func4)
00007ff9ec94066a 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2() (shoule be Func5)

這里由于Method Table被改掉了出皇,WinDbg顯示有點(diǎn)問題羞芍。不過Entry還是可以正常看郊艘,說明我們之前的分析結(jié)果是正確的:
修改之后荷科,F(xiàn)unc3指向了Func2的匯編地址(Func2匯編的前一部分是被9個(gè)long修改過的),F(xiàn)unc4指向了Func2 + 0x59纱注, Func5指向了Func2 + 0xaa畏浆。
到這里我們的第一個(gè)問題就解決了。順便附上WinDbg用到的相關(guān)命令:

  1. 加載SOS.dll
> .load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll
  1. 設(shè)斷點(diǎn)并運(yùn)行到斷點(diǎn)
> bp 7ffa9bcece5d
> g
  1. 加載sos模塊
> .loadby sos clr
  1. 找到Method Table地址
> !Name2EE * WCTF2018Rev.Lib
--------------------------------------
Module:      00007ff9ec834118
Assembly:    WCTF2018Rev_Release.exe
Token:       0000000002000003
MethodTable: 00007ff9ec835b00
EEClass:     00007ff9ec9c2068
Name:        WCTF2018Rev.Lib
--------------------------------------

  1. 打印Method Table
!DumpMT -md 00007ff9ec835b00
EEClass:         00007ff9ec9c2068
Module:          00007ff9ec834118
Name:            WCTF2018Rev.Lib
mdToken:         0000000002000003
File:            D:\ctf\wctf\WCTF2018Rev_Release.exe
BaseSize:        0x18
ComponentSize:   0x0
Slots in VTable: 11
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007ffa4ae3c000 00007ffa4a997fb8 PreJIT System.Object.ToString()
00007ffa4aea40f0 00007ffa4a997fc0 PreJIT System.Object.Equals(System.Object)
00007ffa4af14490 00007ffa4a997fe8 PreJIT System.Object.GetHashCode()
00007ffa4ae6f390 00007ffa4a998000 PreJIT System.Object.Finalize()
00007ff9ec9400c0 00007ff9ec835af8   NONE WCTF2018Rev.Lib..ctor()
00007ff9ec940500 00007ff9ec835a98    JIT WCTF2018Rev.Lib.Verify(System.String)
00007ff9ec940098 00007ff9ec835aa8   NONE WCTF2018Rev.Lib.Func(Int32)
00007ff9ec9405c0 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2()
00007ff9ec9405c0 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2()
00007ff9ec940619 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2()
00007ff9ec94066a 00007ff9ec835ab8    JIT WCTF2018Rev.Lib.Func2()
  1. 打印Method Description
> !DumpMD 00007ff9ec835ab8
Method Name:  WCTF2018Rev.Lib.Func2()
Class:        00007ff9ec9c2068
MethodTable:  00007ff9ec835b00
mdToken:      0000000006000005
Module:       00007ff9ec834118
IsJitted:     yes
CodeAddr:     00007ff9ec9405c0
Transparency: Critical

參考:
淺談.NET中的IL代碼
Debugging .NET with WinDbg

Method加殼

另一個(gè)問題就是Func4和Func5的匯編是如何生成的狞贱。
我們已經(jīng)知道Method在運(yùn)行時(shí)刻获,根據(jù)Method Entry找到Method的匯編代碼。而在Resources函數(shù)中瞎嬉,F(xiàn)unc4和Func5的Method Entry指針都被指向了Func2中一段無意義代碼中蝎毡,只有Func3指向的是一段修改過的代碼(看起來比較像脫殼代碼)厚柳。所以我們?cè)賮碜屑?xì)看一下這段代碼的實(shí)現(xiàn):


我們可以看到,在for循環(huán)中沐兵,v5指向的地址被修改了共38*8=0x130個(gè)字節(jié)草娜,而v5指向的恰恰就是Func3函數(shù)的結(jié)束部分(從0x44開始):
脫殼前后對(duì)比

修改的這部分就是Func4和Func5的匯編代碼。而這些代碼是從哪里來的呢痒筒?
我們繼續(xù)觀察脫殼代碼宰闰,發(fā)現(xiàn)其實(shí)就是把a(bǔ)rray數(shù)組從0x8開始的部分與一個(gè)常數(shù)進(jìn)行了0x1F2FB740F5455FA4異或解密(實(shí)際上每次異或都會(huì)把這個(gè)常數(shù)按位循環(huán)移動(dòng)一下)。
就是說簿透,這個(gè)array數(shù)組(就是resource.bin那個(gè)資源)不僅構(gòu)造出了個(gè)FAKEFLAG移袍,構(gòu)造出了個(gè)真FLAG,還特么藏了兩個(gè)函數(shù)在里面(你以為我是Flag老充,其實(shí)我還是個(gè)函數(shù))葡盗。不得不說出題人還真的有點(diǎn)想法(可惜線下賽基本被所有隊(duì)做出來了?_?)。

未解決問題

有一個(gè)沒搞明白的地方啡浊,就是在Resources函數(shù)中觅够,遍歷Method Table的指針:

IntPtr intPtr = ldftn(Func) + 5;
long num = (long)(*(intPtr + 1));
ptr = *(intPtr + (IntPtr)(((int)(*(intPtr + 2)) << 3) + 3)) + (num << 3);

這個(gè)ptr指針是如何計(jì)算出來的,還是沒弄明白……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巷嚣,一起剝皮案震驚了整個(gè)濱河市喘先,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌廷粒,老刑警劉巖窘拯,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異坝茎,居然都是意外死亡涤姊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門嗤放,熙熙樓的掌柜王于貴愁眉苦臉地迎上來思喊,“玉大人,你說我怎么就攤上這事次酌『蘅危” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵和措,是天一觀的道長(zhǎng)庄呈。 經(jīng)常有香客問我,道長(zhǎng)派阱,這世上最難降的妖魔是什么诬留? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上文兑,老公的妹妹穿的比我還像新娘盒刚。我一直安慰自己,他們只是感情好绿贞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布因块。 她就那樣靜靜地躺著,像睡著了一般籍铁。 火紅的嫁衣襯著肌膚如雪涡上。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天拒名,我揣著相機(jī)與錄音吩愧,去河邊找鬼。 笑死增显,一個(gè)胖子當(dāng)著我的面吹牛雁佳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播同云,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糖权,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了炸站?” 一聲冷哼從身側(cè)響起星澳,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎武契,沒想到半個(gè)月后募判,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咒唆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了释液。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片全释。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖误债,靈堂內(nèi)的尸體忽然破棺而出浸船,到底是詐尸還是另有隱情,我是刑警寧澤寝蹈,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布李命,位于F島的核電站,受9級(jí)特大地震影響箫老,放射性物質(zhì)發(fā)生泄漏封字。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阔籽。 院中可真熱鬧,春花似錦、人聲如沸喊废。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)在辆。三九已至证薇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匆篓,已是汗流浹背棕叫。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奕删,地道東北人俺泣。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像完残,于是被迫代替她去往敵國(guó)和親伏钠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355