shuai's profile娱乐精神PhotosBlog Tools Help

娱乐精神

是太阳就会发光的!
Photo 1 of 6
9/23/2008

基于Linq的通用业务层


最近研究了一下Linq
做了一个通用业务层

本人把数据访问层去掉了
首先因为我的目标是快速开发
不考虑性能和功能性
另外Linq在我眼中就是数据访问层
--在业务层写直接Linq已经足够简单单清晰

先看一下怎么使用
实现三层结构,只需要两步,只需写一行代码
1。用CodeSmith,或SqlMetal,或VS O/R Designer根据数据结构生成实体类
生成内容例如:
    [Table(Name = "ApplicationForm")]
    public partial class ApplicationForm
    {
        [Column(Name = "Id", IsPrimaryKey = true, IsDbGenerated = true)]
        public int Id { get; set; }

        [Column(Name = "Name")]
        public string Name { get; set; }
    }

2。业务层继承我的Service<>泛型类
形如:
    public class ApplicationFormService : Service<ApplicationForm>
    {

    }


就这么简单
Service<>的子类将提供了常用的CRUD,Count,Exist操作
并支持较复杂的强类型的查询条件

功能举例
1。查询
(强类型的查询条件)
    //按ID返回单个对象
    ApplicationForm list6 = ApplicationFormService.FindById(10);

    //返回单个对象。强类型的查询条件
    ApplicationForm list5 = ApplicationFormService.Find(p => p.IsInMarket == true && p.Course == 0);

    //返回多个对象,单个条件
    List<ApplicationForm> list1 = ApplicationFormService.FindAll(p => p.Id == 1);

    //返回多个对象,多个条件
    List<ApplicationForm> list2 = ApplicationFormService.FindAll(p => p.IsInMarket == true && p.Course == 0);

    //拼装查询条件
    Criteria<ApplicationForm> criteria3 = new Criteria<ApplicationForm>();
    criteria3.And(p => p.IsInMarket == true);
    if (couse != "")
        criteria3.And(p => p.Course == 0);            //查询条件
    criteria3.LimitNumber = 2;                        //返回个数
    criteria4.AddOrderBy(p => p.Course);            //排序
    criteria4.AddOrderByDescending(p => p.Id);        //第二排序字段降序
    List<ApplicationForm> list4 = ApplicationFormService.FindAll(criteria4);

    //分页
    int totalCount;
    List<ApplicationForm> list7 = ApplicationFormService.FindAll(0, 5, out totalCount);        //0表示startIndex
    List<ApplicationForm> list8 = ApplicationFormService.FindAll(p => p.IsInMarket == true, 5, 5, out totalCount);

2。创建
    ApplicationForm entity = new ApplicationForm();
    entity.Name = "";
    ApplicationFormService.Create(entity);


3。更新
    在这里Linq有个让我不爽的地方,就是不能指定只针对某一个对象更新
    entity.Name = "abc";
    ApplicationFormService.Update();


4。删除
    ApplicationFormService.DeleteById(100);
    ApplicationFormService.Delete(x => x.Id == 100 && x.Name == "abc");

5。Count 和 Exist
    ApplicationFormService.Count();
    ApplicationFormService.Count(x => x.Name == "abc");
    bool isExist = ApplicationFormService.Exist(x => x.Id == 100 && x.Name == "abc");


当然如果对Service<>产生的方法不满意你也可以重写这些方法
Service<>内部提供了DataContext,可以处理对数据库的操作

为了增加扩展性Service<>类提供了几个版本
Service<TEntity, TIdentiry, TOrderKeySelector>
TEntity表示实体类型
TIdentiry表示主键类型
TOrderKeySelector表示自定义默认排序字段

好了先写到这里
下一篇我将写一个处理对象关联情况的文章



6/11/2008

Programming Collective Intelligence 躺在我身边

Programming Collective Intelligence

上周花了40多RMB买的,还是打76折的,原价58
刚时还挺舍不得,没买过页价比这么高的书
毕竟才300多页,还是正反面页码都算上
实际上正确的算法是340/2=170自然页
合一张纸58/170 = 0.3元
58元买170张纸~  一张纸就三毛~
出版社的都穷疯了吧
当本帅没见过纸阿

不过拿到手里觉得实在高兴
因为我好久没买计算机书了
光买王小波看了
还不人性的想买卡尔维诺看
买点专业的东西就有点成就感
特像封建地主大资本家的感觉

再是满书的洋文
咱看的还不痛苦
虽然需要像盲人一像不断的查字典
还是一口气看下去了30多页
虽然我只有在看错字满页的盗版书时才会有这种龟速

O'Reilly的书还就是让人看的舒服
白白的书页就像女人的那个,软软的
从视觉和触觉上都非常的相似
还有就是传统的动物书封面
那几个裸体的企鹅也不知道是小妹妹还是小弟弟~
它们为什么不觉得脸红阿~
让我抱着书睡都在梦到企鹅

最后害的我突发神经
放假时去了半天的图书馆
专找O'Reilly的书看
主要是做了一件见不得人的事(此去得真事隐去)
累死本帅了

今天一看Chine-Pub八周年店庆,全场7-75折
我寒~


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]);
    }

11/29/2007

强暴.Net程序集 之十 (FieldDef 和 MethodDef 表结构)

 

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

FieldDef非常的简单, Partition II Metadata中描述为:

22.15    Field : 0x04
The Field table has the following columns:
•    Flags (a 2-byte bitmask of type FieldAttributes, §23.1.5)
•    Name (an index into the String heap)
•    Signature (an index into the Blob heap)

其中Flags 字段是一个位掩码, 描述了字段的属性
corhdl.h中有它的定义
typedef enum CorFieldAttr
{
    // member access mask - Use this mask to retrieve accessibility information.
    fdFieldAccessMask           =   0x0007,
    fdPrivateScope              =   0x0000,     // Member not referenceable.
    fdPrivate                   =   0x0001,     // Accessible only by the parent type.
    fdFamANDAssem               =   0x0002,     // Accessible by sub-types only in this Assembly.
    fdAssembly                  =   0x0003,     // Accessibly by anyone in the Assembly.
    fdFamily                    =   0x0004,     // Accessible only by type and sub-types.
    fdFamORAssem                =   0x0005,     // Accessibly by sub-types anywhere, plus anyone in assembly.
    fdPublic                    =   0x0006,     // Accessibly by anyone who has visibility to this scope.
    // end member access mask

    // field contract attributes.
    fdStatic                    =   0x0010,     // Defined on type, else per instance.
    fdInitOnly                  =   0x0020,     // Field may only be initialized, not written to after init.
    fdLiteral                   =   0x0040,     // Value is compile time constant.
    fdNotSerialized             =   0x0080,     // Field does not have to be serialized when type is remoted.

    fdSpecialName               =   0x0200,     // field is special.  Name describes how.

    // interop attributes
    fdPinvokeImpl               =   0x2000,     // Implementation is forwarded through pinvoke.

    // Reserved flags for runtime use only.
    fdReservedMask              =   0x9500,
    fdRTSpecialName             =   0x0400,     // Runtime(metadata internal APIs) should check name encoding.
    fdHasFieldMarshal           =   0x1000,     // Field has marshalling information.
    fdHasDefault                =   0x8000,     // Field has default.
    fdHasFieldRVA               =   0x0100,     // Field has RVA.
} CorFieldAttr;

可以通过如下程序得到所有Field的属性, t为FieldDef表

for(DWORD i = 0; i < t.Rows; ++i)
{
    row = t.Address + t.RowSize * index;

    // Flags
    flags = *((USHORT *)row);
    row += 2;

    // Name
    if(t.HeapSizes & 0x01)
    {
        name = *((ULONG *)row);
        row += 4;
    }
    else
    {
        name = *((USHORT *)row);
        row += 2;
    }

    // Signature
    if(t.HeapSizes & 0x04)
    {
        signature = *((ULONG *)row);
        row += 4;
    }
    else
    {
        signature = *((USHORT *)row);
        row += 2;
    }
}

MethodDef 表在Partition II Metadata中描述为:
22.26    MethodDef : 0x06
The MethodDef table has the following columns:
•    RVA (a 4-byte constant)
•    ImplFlags (a 2-byte bitmask of type MethodImplAttributes, §23.1.10)
•    Flags (a 2-byte bitmask of type MethodAttributes, §23.1.10)
•    Name (an index into the String heap)
•    Signature (an index into the Blob heap)
•    ParamList (an index into the Param table).  It marks the first of a contiguous run of Parameters owned by this method.  The run continues to the smaller of:
o    the last row of the Param table
o    the next run of Parameters, found by inspecting the ParamList of the next row in the MethodDef  table

对了, 别忘了Property(属性实际上就是get_和set_方法)
其中Flags标记保存Method的一些可访问性及其它属性
corhdr.h 中有它的定义
typedef enum CorMethodAttr
{
    // member access mask - Use this mask to retrieve accessibility information.
    mdMemberAccessMask          =   0x0007,
    mdPrivateScope              =   0x0000,     // Member not referenceable.
    mdPrivate                   =   0x0001,     // Accessible only by the parent type.
    mdFamANDAssem               =   0x0002,     // Accessible by sub-types only in this Assembly.
    mdAssem                     =   0x0003,     // Accessibly by anyone in the Assembly.
    mdFamily                    =   0x0004,     // Accessible only by type and sub-types.
    mdFamORAssem                =   0x0005,     // Accessibly by sub-types anywhere, plus anyone in assembly.
    mdPublic                    =   0x0006,     // Accessibly by anyone who has visibility to this scope.
    // end member access mask

    // method contract attributes.
    mdStatic                    =   0x0010,     // Defined on type, else per instance.
    mdFinal                     =   0x0020,     // Method may not be overridden.
    mdVirtual                   =   0x0040,     // Method virtual.
    mdHideBySig                 =   0x0080,     // Method hides by name+sig, else just by name.

    // vtable layout mask - Use this mask to retrieve vtable attributes.
    mdVtableLayoutMask          =   0x0100,
    mdReuseSlot                 =   0x0000,     // The default.
    mdNewSlot                   =   0x0100,     // Method always gets a new slot in the vtable.
    // end vtable layout mask

    // method implementation attributes.
    mdCheckAccessOnOverride     =   0x0200,     // Overridability is the same as the visibility.
    mdAbstract                  =   0x0400,     // Method does not provide an implementation.
    mdSpecialName               =   0x0800,     // Method is special.  Name describes how.

    // interop attributes
    mdPinvokeImpl               =   0x2000,     // Implementation is forwarded through pinvoke.
    mdUnmanagedExport           =   0x0008,     // Managed method exported via thunk to unmanaged code.

    // Reserved flags for runtime use only.
    mdReservedMask              =   0xd000,
    mdRTSpecialName             =   0x1000,     // Runtime should check name encoding.
    mdHasSecurity               =   0x4000,     // Method has security associate with it.
    mdRequireSecObject          =   0x8000,     // Method calls another method containing security code.

} CorMethodAttr;

值得说明的是ParamList字段
它记录了Param表的记录索引,  指明所属方法的参数的开始位置, 结束于下一个方法参数的开始
程序如下, t为MethodDef表
for(DWORD i = 0; i < t.Rows; ++i)
{
    row = t.Address + t.RowSize * index;

    // RVA
    rva = *((DWORD *)row);
    row += 4;

    // ImplFlags
    implFlags = *((USHORT *)row);
    row += 2;

    // Flags
    flags = *((USHORT *)row);
    row += 2;

    // Name
    if(t.HeapSizes & 0x01)
    {
        nameIndex = *((ULONG *)row);
        row += 4;
        wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
    }
    else
    {
        nameIndex = *((USHORT *)row);
        row += 2;
        wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
    }


    // Signature
    if(t.HeapSizes & 0x04)
    {
        signature = *((DWORD *)row);
        row += 4;
    }
    else
    {
        signature = *((USHORT *)row);
        row += 2;
    }

    // ParamList
    if(t.IndexSizes & 0x10)
    {
        params = *((DWORD *)row);

        if(index < t.Rows - 1)
            endparams = *((DWORD *)(row + t.RowSize));
        else
            endparams = 0;
        row += 4;
    }
    else
    {
        params = *((USHORT *)row);

        if(index < t.Rows - 1)
            endparams = *((USHORT *)(row + t.RowSize));
        else
            endparams = 0;
        row += 2;
    }
}

11/9/2007

强暴.Net程序集 之九 (TypeDef表结构)


警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
 
现在我们来看一个非常重要的元数据表TypeDef, 它定义了类型信息
在Partition II Metadata中描述了它的格式:
The TypeDef table has the following columns:
• Flags (a 4-byte bitmask of type TypeAttributes, §23.1.15)
• TypeName (an index into the String heap)
• TypeNamespace (an index into the String heap)
• Extends (an index into the TypeDef, TypeRef, or TypeSpec table; more precisely, a TypeDefOrRef  (§24.2.6) coded index)
• FieldList (an index into the Field table; it marks the first of a contiguous run of Fields owned by this Type).  The run continues to the smaller of:
o the last row of the Field table
o the next run of Fields, found by inspecting the FieldList of the next row in this TypeDef table
• MethodList (an index into the MethodDef table; it marks the first of a continguous run of Methods owned by this Type).  The run continues to the smaller of:
o the last row of the MethodDef table
o the next run of Methods, found by inspecting the MethodList of the next row in this TypeDef table
 
其中:

Flags 是一个位掩码, 描述了类型的一些属性信息, 文档中的23.1.15 Flags for types [TypeAttributes]一节中详情描述
在corhdr.h中也有一个定义, 下面是一个节选的列表:
typedef enum CorTypeAttr
{
    // Use this mask to retrieve the type visibility information.
    tdVisibilityMask        =   0x00000007,
    tdNotPublic             =   0x00000000,     // Class is not public scope.
    tdPublic                =   0x00000001,     // Class is public scope.
    // Use this mask to retrieve class semantics information.
    tdClassSemanticsMask    =   0x00000060,
    tdClass                 =   0x00000000,     // Type is a class.
    tdInterface             =   0x00000020,     // Type is an interface.
    // end semantics mask
    // Special semantics in addition to class semantics.
    tdAbstract              =   0x00000080,     // Class is abstract
    tdSealed                =   0x00000100,     // Class is concrete and may not be extended
    tdSpecialName           =   0x00000400,     // Class name is special.  Name describes how.
 //其它略
} CorTypeAttr;
 
并且corhdr.h还有一系列宏, 用于方便的查询类型的一些属性,比如
#define IsTdClass(x)                        (((x) & tdClassSemanticsMask) == tdClass)
 
TypeName和TypeNamespace 都是一个String 堆的索引值, 要注意它的长度随HeapSizes的大小而变
 
Extends 描述了类型的继承情况, 即指向基类的信息(接口为0), 但是这个值并不是一个元数据token, 而是一个对token进行压缩后的值, TypeDefOrRefEncoded一节中描述了这个值的算法
比如token为0x01000012,那么它的TypeDefOrRefEncoded 值为
 encoded = ( 0x000012 << 2 ) |  0x01  (0x01 指 TypeRef 在TypeDefOrRef中的编号)
            = 0x48 | 0x01
            = 0x49
其中TypeDefOrRef指将TypeDef, TypeRef, TypeSpec这一组号转为一个2位的编号:
TypeDefOrRef    Tag
TypeDef             0
TypeRef             1
TypeSpec           2
可以看出, 这个压缩后的值, 最后两位描述了基类所处于的元数据表在TypeDefOrRef中的编号, 编号之前的位是在该在表中的索引号
 
FieldList 和MethodList是一个指向Field和Method表中的起始索引的值, 从此开始遍历都是它的Field和Method, 结束于下一个类型的Field和Method表中的起始索引(相当不能超过RowSize), 在Partition II Metadata 的 Field 表一节中有详细的说明
比如FieldList值为0x01, 而下一个类型的FieldList为0x03, 那么0x01和0x02都是该类型的Field索引
 
 
下面我们通过代码看如何得到TypeDef 表中的这些值
 
        row = t.Address + t.RowSize * index; //每行的地址, index为类型在TypeDef中的索引

        // Flags
        flags = *((DWORD *)row);
        row += 4;

        // Name
        if(t.HeapSizes & 0x01)
        {
                nameIndex = *((ULONG *)row);
                row += 4;
                nameSpaceIndex = *((ULONG *)row);
                row += 4;
        }
        else
        {
                nameIndex = *((USHORT *)row);
                row += 2;
                nameSpaceIndex = *((USHORT *)row);
                row += 2;
        }
        //nameIndex 和nameSpaceIndex 为在String表中的索引

        // Extends
        if(t.IndexSizes & 0x08)
        {
                extends = *((DWORD *)row);
                row += 4;
        }
        else
        {
                extends = *((USHORT *)row);
                row += 2;
        }
        extendsTable = (BYTE)(extends & 0x3);
        extendsIndex = extends = extends >> 2;
        switch(extendsTable)
        {
        case 0:
                // TypeDef
                extends |= 0x02 << 24;
                break;
        case 1:
                // TypeRef
                extends |= 0x01 << 24;
                break;
        case 2:
                // TypeSpec
                extends |= 0x1b << 24;
                break;
        }
        // 现在extends 为Extends转成token的格式

        // FieldList
        if(t.IndexSizes & 0x08)
        {
                fields = *((DWORD *)row);
                if(index < t.Rows - 1)
                        endFields = *((DWORD *)(row + t.RowSize));
                else
                        endFields = 0;
                row += 4;
        }
        else
        {
                fields = *((USHORT *)row);
                if(index < t.Rows - 1)
                        endFields = *((USHORT *)(row + t.RowSize));
                else
                        endFields = 0;
                row += 2;
        }
        // fields 为该类型FieldList的起始索引, endFields 为结束索引

        // MethodList
        if(t.IndexSizes & 0x08)
        {
                methods = *((DWORD *)row);
                if(index < t.Rows - 1)
                        endMethods = *((DWORD *)(row + t.RowSize));
                else
                        endMethods = 0;
                row += 4;
        }
        else
        {
                methods = *((USHORT *)row);
                if(index < t.Rows - 1)
                        endMethods = *((USHORT *)(row + t.RowSize));
                else
                        endMethods = 0;
                row += 2;
        }
        // methods 为该类型MethodList的起始索引, endMethods 为结束索引


 

11/2/2007

强暴.Net程序集 之八 (元数据Token和指定程序入口方法)

 

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

一般的.net程序都是从一个静态的Main方法开法运行的,下面我将演示如何指定程序集中的另一个方法作为入口方法

假设有如下程序,我们要修改程序集让NewMain先运行
using System;
public class Program
{
 public static void Main()
 {
  Console.WriteLine("Program::Main()");
 }

 public static void NewMain()
 {
  Console.WriteLine("Program::NewMain()");
 }
}

回想一下IMAGE_COR20_HEADER的格式,字段EntryPointToken指定了程序入口

在Partition II Metadata中写道:
Entry point metadata token
The entry point token (§15.4.1.2) is always a MethodDef token (§22.26) or File token (§22.19 ) when the entry point for a multi-module assembly is not in the manifest assembly.  The signature and implementation flags in metadata for the method indicate how the entry is run

它的值不是一个地址,而是一个元数据Token
这个Token的格式是,前1个字节是元数据表的编号,最后一个字节是记录的索引
比如Token为0x06000001,0x06指Method表,0x01指Method表的第一个记录,这通常指向到Main方法
在元数据中大量使用了这种Token的方式,来引用其它表或流的项目

我们只需要修改此Token就能指定新的入口方法,在我们这个演示中改为NewMain方法的Token,即0x06000002

程序如下:
pCorHeader->EntryPointToken = _htoi(szNewEntryPointToken);


用ildasm打开后观察
.method public hidebysig static void  NewMain() cil managed
{
  .entrypoint
  // 代码大小       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Program::NewMain()"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Program::NewMain

可以看到.entrypoint伪指令已经在我们指定的NewMain方法中了

 

11/1/2007

强暴.Net程序集 之七 (强命名的移除)

 
警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
 
先让我们重新看一下IMAGE_COR20_HEADER头的格式:
typedef struct IMAGE_COR20_HEADER
{
    // Header versioning
    DWORD                   cb;             
    WORD                    MajorRuntimeVersion;
    WORD                    MinorRuntimeVersion;
   
    // Symbol table and startup information
    IMAGE_DATA_DIRECTORY    MetaData;       
    DWORD                   Flags;          
    DWORD                   EntryPointToken;
   
    // Binding information
    IMAGE_DATA_DIRECTORY    Resources;
    IMAGE_DATA_DIRECTORY    StrongNameSignature;
    // Regular fixup and binding information
    IMAGE_DATA_DIRECTORY    CodeManagerTable;
    IMAGE_DATA_DIRECTORY    VTableFixups;
    IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;
    // Precompiled image info (internal use only - set to zero)
    IMAGE_DATA_DIRECTORY    ManagedNativeHeader;
   
} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;
 
移除强名需要修改其中的这两个字段
Flags
StrongNameSignature
其中Flags是一个按位或值,我们只需要移去COMIMAGE_FLAGS_STRONGNAMESIGNED
在winnt.h中有其定义:
typedef enum ReplacesCorHdrNumericDefines
{
// COM+ Header entry point flags.
    COMIMAGE_FLAGS_ILONLY               =0x00000001,
    COMIMAGE_FLAGS_32BITREQUIRED        =0x00000002,
    COMIMAGE_FLAGS_IL_LIBRARY           =0x00000004,
    COMIMAGE_FLAGS_STRONGNAMESIGNED     =0x00000008,
    COMIMAGE_FLAGS_TRACKDEBUGDATA       =0x00010000,
    //略
 }

然后将StrongNameSignature和VirtualAddress和Size都置为0
 
第二步是修改Assembly表
将Flags字段的PublicKey位移去
 在Partition II Metadata中有描述:
 PublicKey   0x0001
 SideBySideCompatible  0x0000
 <reserved>   0x0030
 Retargetable   0x0100
 EnableJITcompileTracking  0x8000
 DisableJITcompileOptimizer 0x4000
 
另外修改PublicKey字段,将其置为0
代码为:
 pCorHeader->Flags &= ~COMIMAGE_FLAGS_STRONGNAMESIGNED;
 pCorHeader->StrongNameSignature.VirtualAddress = 0;
 pCorHeader->StrongNameSignature.Size = 0;
 PMETA_ASSEMBLY_TABLE assemblyTable = (PMETA_ASSEMBLY_TABLE)(tables[TableType::Assembly].Address);
 assemblyTable->Flags &= 0x1110;  //PublicKey = 0x0001
 assemblyTable->PublicKey = 0;
这样我们就将到了一个没有强名的程序集

在.net的非托管 API中提供了一系列和强名相关的函数:
StrongNameCompareAssemblies   仅通过二进制签名确定两个指定的程序集是否不同。
StrongNameErrorInfo    获取由强名称方法引发的上一个错误代码。
StrongNameFreeBuffer    释放上一次调用强名称方法时分配的内存。
StrongNameGetBlob    通过位于指定内存地址的可执行文件的二进制表示形式填充指定的缓冲区。
StrongNameGetBlobFromImage   获取位于指定内存地址的程序集映像的二进制表示形式。
StrongNameGetPublicKey    从私钥/公钥对中检索公钥。
StrongNameHashSize    使用指定的哈希算法获取哈希值所需要的缓冲区大小。
StrongNameKeyDelete    删除具有指定容器名称的公钥-私钥对。
StrongNameKeyGen    创建一个用于强名称的公/私钥对。
StrongNameKeyGenEx    创建具有指定密钥大小的公钥/私钥对,以供强名称使用。
StrongNameKeyInstall    向容器中导入公钥-私钥对。
StrongNameSignatureGeneration   生成指定程序集的强名称签名。
StrongNameSignatureGenerationEx   使用指定的密钥容器名称获取指定程序集的强名称签名。
StrongNameSignatureSize    返回强名称签名的大小。
StrongNameSignatureVerification   获取一个指示所提供的可移植的可执行(.dll 或 .exe)文件的程序集清单是否包含强名称签名的值。
StrongNameSignatureVerificationEx  获取一个指示所提供的可移植的可执行(.dll 或 .exe)文件的程序集清单是否包含强名称签名的值。
StrongNameSignatureVerificationFromImage 验证已映射到内存的程序集对关联的公钥是否有效。
StrongNameTokenFromAssembly   从指定的程序集文件中创建强名称标记。
StrongNameTokenFromAssemblyEx   从指定的程序集文件创建强名称标记,并返回公钥。
StrongNameTokenFromPublicKey   返回表示公钥的标记。
10/26/2007

强暴.net程序集 之六 (Assembly表的结构和修改)


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

上回有一个TableRowSizes函数没有说,它是用来得到指定元数据表的行大小
以Assembly表为例,在Partition II Metadata中描述了它的结构:

The Assembly table has the following columns:
• HashAlgId (a 4-byte constant of type AssemblyHashAlgorithm, §23.1.1)
• MajorVersion, MinorVersion, BuildNumber, RevisionNumber (each being 2-byte constants)
• Flags (a 4-byte bitmask of type AssemblyFlags, §23.1.2)
• PublicKey (an index into the Blob heap)
• Name (an index into the String heap)
• Culture (an index into the String heap)

HashAlgId 4个字节
MajorVersion, MinorVersion, BuildNumber, RevisionNumber 共8个字节
Flags 4个字节,指定了在Side By Side模式运行时的限制
PublicKey、Name、Culture、是#Blob heap或#String heap的索引,Public Key是用于assembly的完整性及发布者验证;Name是Assembly的名字;Culture是此Assembly所支持的语言,要注意这几个字段都不是固定大小,而是能过HeapSize推算出来的

对于Blob heap或String heap的大小的值是通过META_COMPOSITE_HEADER的HeapSizes计算出来的
在Partition II Metadata有描述:
The HeapSizes field is a bitvector that encodes the width of indexes into the various heaps.  If bit 0 is set, indexes into the “#String” heap are 4 bytes wide; if bit 1 is set, indexes into the “#GUID” heap are 4 bytes wide; if bit 2 is set, indexes into the “#Blob” heap are 4 bytes wide.  Conversely, if the HeapSize bit for a particular heap is not set, indexes into that heap are 2 bytes wide.
Heap size flag Description
0x01 Size of “#String” stream >= 216.
0x02 Size of “#GUID” stream >= 216.
0x04 Size of “#Blob” stream >= 216.

这些信息正好是ildasm对.assembly 伪指令的输出,比如
.assembly CountDown
{ .hash algorithm 32772
  .ver 1:0:0:0
}

于是我们就可以求得总的行大小(Assembly表只有这一行)
  bytes = 4;
  bytes += 8;
  bytes += 4;
  bytes += (heapSizes & 0x04) ? 4 : 2;
  bytes += (heapSizes & 0x01) ? 4 : 2;
  bytes += (heapSizes & 0x01) ? 4 : 2;

现在我们改一下主版本号,注意我用的是内存映射文件
 PMETA_ASSEMBLY_TABLE assemblyTable = (PMETA_ASSEMBLY_TABLE)(tables[TableType::Assembly].Address);
 WORD* pVersion = &(assemblyTable->MajorVersion);
 *pVersion = atoi(szBuffer);

用ildasm看已经修改过来了:
.assembly query
{
  .ver 8:0:0:0
}

但是... ....

 

 

shuai yang