日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET平台PE结构分析之Metadata(一)

發布時間:2024/9/5 asp.net 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET平台PE结构分析之Metadata(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

.NET平臺PE結構分析之Metadata(一)

強命名及其去除

?

?????? 首先,這不是一篇完整的參考,所以并沒有涉及Metadata的各個方面,而只是討論了與強命名有關的部分。所以,在開始前,先列出一些參考文獻,在閱讀過程中若遇到問題,可以直接從中查閱。

?

兩本書:The Common Language Infrastructure Annotated StandardAddison Wesley

????????????? Inside Microsoft .NET IL AssemblerMicrosoft Corporation

文章:? MS.Net CLR擴展PE結構分析? (作者:Flier Lu

????????????? The .NET File Format??????????   (來自:codeproject.com

當然,還有最權威的Framework SDK的文檔。

?

本文的例子文件:

?

1、什么是強命名(StrongName

?????? 我的理解,強命名類似win32平臺下PE文件的checksum,用來對原始文件完整性進行驗證的。一般有兩個作用:一是不同版本的相同文件,Strongname是不一樣的,因此可以將其區分開;二是防止文件被修改,被修改的文件是無法運行的。第一點我們不關心,但是第二點就應該引起cracker們的注意了。一個加密非常簡單的文件,只要改動一個字節的數據(比如jejne)就可以破解,結果修改了過后卻無法運行。郁悶啊!因此,在對有強命名的PE文件修改前,必須去掉其強命名。(太累了,下面一律簡稱SN。)

?

2、怎么給程序加上SN

?????? 這和cracker關系不大,但是了解一下有好處,至少有個感性的印象。

?????? 第一步,是生成一個key文件,命令是:sdk安裝目錄\bin\sn.exe –k strong.snk

?????? 第二步,把strong.snk文件的信息加入到你需要加密的Module當中,通常在AssemblyInfo.cs的文件中添加:

?????? [assembly: AssemblyKeyFile(@"完整路徑\strong.snk")]

?????? 然后編譯生成就OK了。這樣,一個含有SN的文件便生成。可以用我寫的工具snView看一下(文件見末尾的pskill.exe,這是我N年前寫的一個查殺進程的程序,已被加上SN):

?????? 看一下它的保護效果,用UD打開文件,把末尾的一個字節從00改為01,再運行。報錯,如下:

?

3、去除強命名的兩種方法

?????? 下面介紹去除SN的兩種方法,第一種手動,第二種自動。

?

3.1、反匯編成il代碼,修改后再編譯成exe文件

?????? 這個方法不多講了,codeproject上有幾篇文章詳細說過:Building Security Awareness in .NET Assemblies : Part 3 - Learn to break strong name .NET Assemblies?,只大概說一下過程。

?

1、? ildasm反匯編

2、? .assembly 這個assemblyname塊中尋找.publickey,如圖:

?????? 注意,會搜索到很多.publickeytoken,而且長度較短。這些都不是該文件(assembly,又叫裝配件)的SN,而不過是其中的方法/類等等的唯一性標志。

3、? 刪除選定的部分

包含兩個,一個是key的值,一個是.hash algorithm,這是計算該key的算法。 

4、? 再用ilasm進行編譯

ilasm /resource=psill.res pskill.il

?

這時就可以對這個文件進行修改了。

BUT,這種方法有兩個缺點:一是麻煩,二是某些文件沒法反匯編,或反匯編不完全,或反匯編后就無法再次匯編成功。(特別是混淆過的程序)

?

3.2、直接在文件上修改

這樣最方便,但是,方便的前提是你知道.NET判斷SN的數據及修改方法,這就要牽涉到Metadata了。

原先網上有一個工具,叫snRemove,不過不好用,修改完了運行不了。這里先紹一個偶寫的工具:snRemover,可以自動去除程序中的SN。下載請到http://vxer.cn/hmx

?

?

下面介紹snRemover的原理。什么是Metadata?我們都知道,.NET下運行的PE文件類似JAVA,不是將指令編譯成機器代碼,而是編譯成il中間代碼,再在運行時進行既時編譯(JIT)。這樣,用一些軟件可以直接打開PE文件,看到類名、方法名、指令等等。所有的這些東東,都是Metadata。我們的任務,就是在Metadata中,找到標識SN的地方并修改之。

下面假定你已經對win32平臺下PE結構有些了解了,講述從簡。

PE文件中緊跟PE Header的是16Data Directory Table,最常見的是第1個輸出表和第2個輸入表。而.NET擴展的PE結構則由倒數第二個表指向,也就是Common Language Runtime header address and size(簡稱CLI),根據他,我們找到了CLI Header。以pskill.exe為例,CLI HeaderRVA2008,大小是48,算出物理偏移是1008。你現在就可以用UD打開pskill.exe跟著我走了。

00001008h: 48 00 00 00 02 00 05 00 10 42 00 00 60 11 00 00 ; H........B..`...

00001018h: 09 00 00 00 04 00 00 06 A8 26 00 00 65 1B 00 00 ; ........?..e...

00001028h: 50 20 00 00 80 00 00 00 00 00 00 00 00 00 00 00 ; P ..€...........

00001038h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................

00001048h: 00 00 00 00 00 00 00 00???????????????????????? ; ........

?

CLI Header的結構如下:

RVA

Field

Contents

0x2008

Cb(結構的大小)

0x48

0x200C

MajorRuntimeVersion

2

0x200E

MinorRuntimeVersion

0

0x2010

MetaData

0x2060

0x2014

Size of the Metadata

0x148 =(RVA of Import Table) – (RVA of MetaData)

0x2018

Flags

1

0x201C

EntryPointToken

0x06000001 (Method #1 in TypeDef table)

0x2020

Resources

0

0x2028

StrongNameSignature

0

0x2030

CodeManagerTable

0

0x2038

VTableFixups

0

0x2040

ExportAddressTableJumps

0

0x2048

ManagedNativeHeader

0

?

這里,出現了兩處和SN有關的標識。一處是FLAGS,另一處是StrongNameSignature。對于FLAGS,有這個標志:

COMIMAGE_FLAGS_STRONGNAMESIGNED (0x00000008)?

如果這處標志被置位,則認為有SN。第二處則指出了SN數據的RVA和大小,也就是最開始用snView看到的。

修改時,FLAGS標志位減去0x00000008,然后把StrongNameSignatureRVASIZE 均填0。運行一下試試,還是出錯。當然,還有一處最重要的地方要修改,我們繼續。

注意第四項Metadata,他指出了Metadata表的RVA和大小。看一下,pskillMetadataRVA=4210處,也就是物理地址3210處。

00003210h: 42 53 4A 42 01 00 01 00 00 00 00 00 0C 00 00 00 ; BSJB............

00003220h: 76 32 2E 30 2E 35 30 37 32 37 00 00 00 00 05 00 ; v2.0.50727......

00003230h: 6C 00 00 00 7C 05 00 00 23 7E 00 00 E8 05 00 00 ; l...|...#~..?..

00003240h: 80 07 00 00 23 53 74 72 69 6E 67 73 00 00 00 00 ; €...#Strings....

00003250h: 68 0D 00 00 80 01 00 00 23 55 53 00 E8 0E 00 00 ; h...€...#US.?..

00003260h: 10 00 00 00 23 47 55 49 44 00 00 00 F8 0E 00 00 ; ....#GUID...?..

00003270h: 68 02 00 00 23 42 6C6F 62 00 00 00 00 00 00 00 ; h...#Blob.......

00003280h: 02 00 00 01 57 15 02 00 09 01 00 00 00 FA 01 33 ; ....W........?3

00003290h: 00 16 00 00 01 00 00 00 33 00 00 00 02 00 00 00 ; ........3.......

000032a0h: 09 00 00 00 0A 00 00 00 0C 00 00 00 53 00 00 00 ; ............S...

000032b0h: 0D 00 00 00 04 00 00 00 01 00 00 00 05 00 00 00 ; ................

000032c0h: 01 00 00 00 00 00 0A 00 01 00 00 00 00 00 06 00 ; ................

?

看一下文檔中對Metadata的定義:

Type

Field

Description

DWORD

lSignature

“Magic” signature for physical metadata, currently 0x424A5342

WORD

iMajorVersion

Major version (1 for the first release of the common language runtime)

WORD

iMinorVersion

Minor version (1 for the first release of the common language runtime)

DWORD

iExtraData

Reserved; set to 0

DWORD

iLength

Length of the version string

BYTE[?]

iVersionString

Version string

BYTE

fFlags

Reserved; set to 0

BYTE

?

[padding]

WORD

iStreams

Number of streams

?

第一項,Metadata根部的標識,ASC碼“BSJB”。這樣,以后我們在尋找它時就可以直接搜索“BSJB”既可。這里有一點注意,就是ASC碼串VersionString是可變長度的,結束后再加一個fFlags,然后要和4字節對齊,也就是padding。這里,我們的版本號是v2.0.50727,前面iLength指出了長度是0C(十進制的12,已經是和4對齊的了,能整除),因此fFlags的地址就是00003220+0C=0000322C,后一個字節為空,又是padding。最后,05 00指出了Number of streams,共有幾個數據流。

Metadata中的數據都是存放在各種數據流stream里,比較重要的是“#~”和“#Strings”,后者保存了各種名稱(比較混淆或者反混淆,就要從這個流著手,如果有機會,下次再講),而與SN相關的則是#~流。它也是所有當中最復雜的。

緊接著上面的數據,就是各個流的Header了:

00003230h: 6C 00 00 00 7C 05 00 00 23 7E 00 00 E8 05 00 00 ; l...|...#~..?..

00003240h: 80 07 00 00 23 53 74 72 69 6E 67 73 00 00 00 00 ; €...#Strings....

00003250h: 68 0D 00 00 80 01 00 00 23 55 53 00 E8 0E 00 00 ; h...€...#US.?..

00003260h: 10 00 00 00 23 47 55 49 44 00 00 00 F8 0E 00 00 ; ....#GUID...?..

00003270h: 68 02 00 00 23 42 6C6F 62 00 00 00 00 00 00 00 ; h...#Blob.......

?

這個結構不難,如下:

Type

Field

Description

DWORD

iOffset

Offset in the file for this stream

DWORD

iSize

Size of the stream in bytes

char[]

rcName

Name of the stream; a zero-terminated ANSI string no longer than seven characters

?

?

我們以#~為例

00003230h: 6C 00 00 00 7C 05 00 00 23 7E 00 00 E8 05 00 00 ; l...|...#~..?..

紅色部分是RVA,相對于Metadata Root的,藍色部分是大小,而黑色斜體就是“#~”的ASC碼了。那為什么237E后要加兩個字節的0呢?又忘了?因為字符串要與4字節對齊。我們來計算#~流的實際物理地址:offset=root + RVA=00003210+6C=0000327C

0000327ch: 00 00 00 00 02 00 00 01 57 15 02 00 09 01 00 00 ; ........W.......

0000328ch: 00 FA 01 33 00 16 00?????????????

?

對應的結構如下:

Size

Field

Description

4 bytes

Reserved

Reserved; set to 0.

1 byte

Major

Major version of the table schema (1 for the first release of the common language runtime).

1 byte

Minor

Minor version of the table schema (0 for the first release of the common language runtime).

1 byte

Heaps

Binary flags indicate the offset sizes to be used within the heaps.

A 4-byte unsigned integer offset is indicated by 0x01 for a string heap, 0x02 for a GUID heap, and 0x04 for a blob heap.

If a flag is not set, the respective heap offset is presumed to be a 2-byte unsigned integer.

?

?

A # stream can also have special flags set: flag 0x20, indicating that the stream contains only changes made during an edit-and-continue session, and flag 0x80, indicating that the metadata might contain items marked as deleted.

1 byte

Rid

Bit count of the maximal record index to all tables of the metadata; calculated at run time (during the metadata stream initialization).

8 bytes

MaskValid

Bit vector of present tables, each bit representing one table (1 if present).

8 bytes

Sorted

Bit vector of sorted tables, each bit representing a respective table (1 if sorted).

?

這里要講一下#~流中各種數據的保存形式了。該流中保存的主要是各種表,這些表又定義了Metadata中其它的各種數據,所以才說它重要啊。現在微軟已經定義的表有

注意結構中的MaskValid數據,它是8字節的,對應2進制數有64位。從最低位開始,如果這個位為1,代表#~流中該表被定義了,如果為0,代表沒有該表。我們看一下pskill的數據,為57 15 02 00 09 01 00 00,翻譯為2進制為

2進制:0000 0000 0000 0000 0000 0001 0000 1001 0000 0000 0000 0010 0001 0101 0101 0111

16進制: 0?? 0??? 0?? 0??? 0?? 1??? 0?? 9??? 0?? 0??? 0?? 2??? 1?? 5?? 5??? 7

這樣我們就知道了一共有C個表被定義了,pskill中存在的表可以用Spices .Net看一下,再與上表對應一下,看看是不是相等:

?

?

同時,我們點擊了第20個表,AssemblyDef,看到了右邊的數據顯示出了PublicKey,那不正是我們要找的SN嗎。

????????????? 接下來的工作就是計算AssemblyDef前面表的大小,然后直到找到AssemblyDef為止。剩下的不多講了,可以看codeproject的那篇THE .NET File Format。但是這個過程是非常煩索的,我寫的強命名去除工具snRemover也沒有說細的計算,而是選擇一個比較偷懶的方法。下面再說。我們先來到AssemblyDef處:

0000376eh: 04 80 00 00 01 00 00 00 05 09 64 5F 01 00 00 00 ; .€........d_....

0000377eh: 46 00 1B 00 00?????? ???????????????????????????; F....

?

來看一下AssemblyDef的定義:

?

? HashAlgId (a 4-byte constant of type AssemblyHashAlgorithm).

? MajorVersion, MinorVersion, BuildNumber, RevisionNumber

(2-byte constants).

? Flags (a 4-byte bit mask of type AssemblyFlags).

? PublicKey (index into Blob heap).

? Name (index into String heap).

? Culture (index into String heap).

一共有6項,其中Flags項有一個常數為

afPublicKey = 0x0001,

// The assembly ref holds the full (unhashed) public key.

也就是說,如果Flags(數據中藍色部分)的第一位被置1,則認為它有SN。因此,我們將Flags1,然后將.PublicKey項(黑色斜體部分,指向BLOG中的指針)置0。現在才徹底修改完成。運行一下,OK??????

????????????? 偶是怎么定義AssemblyDef的地方的呢?因為該表的第一項為HashAlgId,目前只有三種可能:00008004,000080030。如果是0,代表沒有SN。因此直接從#~開始,搜索00008004或者00008003,定義既可。但是有失敗的可能,因為不能保證AssemblyDef之前的表中沒有0000800400008003,那樣的話就玩完了。不過我試了那么多程序,暫時沒有發現不能用。等回頭有空再把snRemover改成精確定位吧!

?

?

????????????? 要是你能堅持看到這,真得感謝你了,頭暈了吧!我打字都不行了。那就休息一下,下次再講講簡單的,因為最難的部分已經講完了。

?

?

By:tankaiha [NE365]

2006.04.28

Any bug, report to http://vxer.cn/

?

轉載于:https://www.cnblogs.com/cxd4321/archive/2008/06/04/1213313.html

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的.NET平台PE结构分析之Metadata(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。