shuai's profile娱乐精神PhotosBlog Tools Help

Blog


    12/14/2007

    强暴.Net程序集 之十三 (反汇编之反汇编输出)

     
    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
     
        首先, 得到当前的指令, 注意处理双字节的情况(以0xFE开始的字节)

        BYTE b = source[index];
        index++;
        if(IsPrefix(b))
        {
            b = source[index];
            index++;
            current = (opcode)twoByteOpcodes[b];
        }
        else
        {
            current = (opcode)oneByteOpcodes[b];
        }
     
        直接输出current的Instruction成员就是当前指令的字符串表示
     
        然后, 根据类型, 得到操作数

        switch(current.Type())
        {
        case Switch:
            switchCount = *((DWORD*)&source[index]);
            index += 4;
            operand = &source[index];
            index += switchCount * sizeof(DWORD);
            break;
        case Token:
            operand = &source[index];
            break;
        case Branch:
            operand = &source[index];
            index += current.OperandSize();
            break;
        case Index:
            operand = &source[index];
            index += current.OperandSize();
            break;
        case Value:
            operand = &source[index];
            index += current.OperandSize();
            break;
        }
     
        对于 Token和Value类型的操作数应该以16进制字符串形式输出, 可能引用了其它表比如MethodRef中的方法Token, 或是字符串流的Token
        对于Branch类型它是方法内的指令相对地址, 在ildasm以IL_000000X形式输出
     
        最后, 不断移动index, 得到下一条指令, 这样就可以完成整个方法的反汇编输出

     

    强暴.Net程序集 之十二 (反汇编之IL指令集)

     
    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
     
    在方法头之后就是一字节形式存储的IL指令,  详细的IL取值请见: Partition III CIL, 比如有:
    Opcode Instruction
    0x00    nop
    0x01    break
    0x02    ldarg.0
    ....
    0x26    pop
    0x27    jmp
    0x28    call
    0x29    calli
    0x2A    ret
    0x2B    br.s
    ...
    0xFE 0x00    arglist
    0xFE 0x01    ceq
    0xFE 0x02    cgt
    0xFE 0x03    cgt.un
    0xFE 0x04    clt
     
    如果按操作码的大小分类, 指令可以分为两组, 一组的操作码只占一个字节,  一组占两个字节, 并高位字节为0xFE
    如果按是否含有操作数(参数), 可以分为无操作数, 和有操作数, 有操作数又可以细分为:

        1) 操作数是一个Token, 比如 callvirt 指令
        2) 操作数是一个跳转的目标Label, 比如br指令
        3) 操作数是一个具体的值, 比如ldc.i4指令
        4) 操作数是一个方法形参的索引指, 比如ldarg.s指令
        5) 操作数是switch指令
     
        按操作数占用大小又分为:
        1) 1个字节, 比如br.s指令 ldc.i4.s指令
        2) 2个字节, 比如ldarg指令
        3) 4个字节, 比如br指令 ldc.i4指令
        4) 8个字节, 比如ldc.i8指令,
     
    程序需要针对不同的操作数类型, 以及大小进行不同的反汇编处理
     
    因为操作数类型, 以及大小是可以确定的, 所以程序中我用一个结构类型保存这些确定的值(先下面的OperandType枚举, 和opcode结构)
    然后以指令的字节形式为key保存在Map中

        操作码为一个字节的Map
        m[0x00] = opcode(L"nop");
        m[0x01] = opcode(L"break");
        m[0x02] = opcode(L"ldarg.0");
        ....
        m[0x26] = opcode(L"pop");
        m[0x27] = opcode(L"jmp", 4, Token);
        m[0x28] = opcode(L"call", 4, Token);
        m[0x29] = opcode(L"calli", 4, Token);
        m[0x2a] = opcode(L"ret");
        m[0x2b] = opcode(L"br.s", 1, Branch);
     
        操作码为两个字节的Map
        n[0x00] =  opcode(L"arglist");
        n[0x01] =  opcode(L"ceq");
        n[0x02] =  opcode(L"cgt");
        n[0x03] =  opcode(L"cgt.un");
        n[0x04] =  opcode(L"clt");
     
    typedef enum
    {
        Branch = 0,
        Token,
        Index,
        Value,
        Switch,
        None
    } OperandType;
    struct opcode
    {
    public:
        opcode()
        {
            operandSize = 0;
        }
        opcode(const std::wstring& instr) :
               instruction(instr),
               operandSize(0),
               operandType(None)
        {
        }
        opcode(const std::wstring& instr, int os, OperandType t) :
               instruction(instr),
               operandSize(os),
               operandType(t)
        {
        }
    private:
        std::wstring instruction;
        int operandSize;
        OperandType operandType;
    };
     
    12/11/2007

    强暴.Net程序集 之十一 (方法头的结构:窄头与宽头)

    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责

    MethodDef的Rva定义了一个方法的位置, 每个方法体之前都有一个方法头
    方法头分为tiny头 和 fat头两种格式, 方法的前两位定义是tiny还是fat头, Partition II Metadata文档描述如下:

    25.4.1    Method header type values
    The two least significant bits of the first byte of the method header indicate what type of header is present.  These 2 bits will be one and only one of the following:
    Value                                     Value    Description
    CorILMethod_TinyFormat    0x2    The method header is tiny (§25.4.2) .
    CorILMethod_FatFormat    0x3    The method header is fat (§25.4.3).

    tiny头的格式比较简单, 只有一个字节, 除了最低的0x2外, 其余6位保存IL代码的大小
    如果一个方法没有局部变量, 异常处理, 方法小于64字节, 存储栈深度为8时, 那么就使用tiny头
    Partition II Metadata文档描述如下:
    Start Bit    Count of Bits    Description
    0            2                Flags (CorILMethod_TinyFormat shall be set, see §25.4.4)
    2            6                Size, in bytes, of the method body immediately following this header.

    fat头的格式就比较复杂, 大小是12个字节, Partition II Metadata文档中描述如下:
    Offset     Size         Field       Description
    0          12 (bits)    Flags       Flags (CorILMethod_FatFormat shall be set in bits 0:1, see §25.4.4)
    12 (bits)  4 (bits)     Size        Size of this header expressed as the count of 4-byte integers occupied (currently 3)
    2          2            MaxStack    Maximum number of items on the operand stack
    4          4            CodeSize    Size in bytes of the actual method body
    8          4            LocalVarSigTok    Meta Data token for a signature describing the layout of the local variables for the method.  0 means there are no local variables present

    最开始的2个字节中低两位是表示fat头的0x3, 接下来的10是标志(见Partition II Metadata 的 25.4.4 Flags for method headers), 最高4位一直为3
    接下来的2个字节是MaxStack, 是存储栈最大深度
    之后的4个字节是CodeSize, 是IL代码的大小
    最后的4个字节是LocalVarSigTok,是局部变量的特征标识

    程序如下:
        PBYTE instructions = (PBYTE)GetPtrFromRva(rva, m_pNTHeader, m_pImageBase);
        if((instructions[0] & 3) == 0x2)
        {

            WCHAR lBuffer[64];
            methodSize = instructions[0] >> 2;
            codeSize = methodSize;
            maxstack = 8;
            localVariables = 0;
            exceptionHandler = false;
            initLocals = false;           
        }
        else if((instructions[0] & 3) == 0x3)
        {

                USHORT flagsSize = *((USHORT*)&instructions[0]);
                methodSize = ((flagsSize >> 12) & 0xf) * 4;

                if((flagsSize & 0x8) != 0)
                {
                    exceptionHandler = true;
                }
                else
                {
                    exceptionHandler = false;
                }

                if((flagsSize & 0x10) != 0)
                {
                    initLocals = true;
                }
                else
                {
                    initLocals = false;           
                }


                maxstack = *((USHORT*)&instructions[2]);
                codeSize = *((DWORD*)&instructions[4]);

                localVariables = *((DWORD*)&instructions[8]);
        }