关于反汇编工具的小小问题

初见ildasm.exe&IL反汇编程序
相关概念:
ildasm.exe(IL反汇编程序)(摘自msdn)
(Microsoft intermediate language) Disassembler is a companion tool
to the MSIL Assembler (Ilasm.exe). Ildasm.exe takes a portable
executable (PE) file that contains Microsoft intermediate language
(MSIL) code and creates a text file suitable as input to
Ilasm.exe.
is automatically installed with Visual Studio and with the Windows
SDK (Software Development Kit). To run the tool, we recommend that
you use the Visual Studio Command Prompt or the Windows SDK Command
Prompt (CMD Shell). These utilities enable you to run the tool
easily, without navigating to the installation folder. For more
information, see Visual Studio and Windows SDK Command
问题描述:
最近在学习《深入浅出WPF》,学习到4.2.2,说到用IL反编译器打开一个WPF编译结果。IL是个什么鬼?怎么打开?打开编译结果的意思是打开什么?凭着我多年猜蒙结合的学习思路,我还是请教了谷大人。IL反编译器就是上述的ildasm.exe,然后我在C盘中进行搜索,最终找到了ildasm.exe,并且打开了这个反编译器,再凭借着我的学习思路,这个反编译器应该是要打开一个编译结果,而这个编译结果应该是藏在bin文件夹下的debug文件夹下的.exe程序,打开试了试,得到了书中所说的结果,虽然我什么也看不懂吧,权当是初见,后面肯定还会涉及。
解决方法:
C盘搜索ildasm.exe
进行反汇编,文件-打开
摘自:/reading//
开始-所有程序-Visual Studio 2013-Visual Studio
Tools-双击打开VS 2013开发人员命令提示
2 输入 ildasm,回车
3 文件-打开
思路拓展&未解困惑:
搜索ildasm.exe时,出现了好多搜索结果,这都是什么跟什么?
答:找到一个极好的网页,网友极好地解释了这一切。
网页链接:
/b/lucian/archive//where-are-the-sdk-tools-where-is-ildasm.aspx
the SDK tools? Where is ildasm?
Wischik, MSFT 14 Nov
&C:\Program Files\Microsoft
SDKs\Windows\v6.0\bin\ildasm.exe
C:\Program
Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe
C:\Program
Files\Microsoft SDKs\Windows\v7.0A\bin\ildasm.exe
C:\Program
Files\Microsoft SDKs\Windows\v7.0A\bin\x64\ildasm.exe
C:\Program
Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0
Tools\ildasm.exe
C:\Program
Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0
Tools\x64\ildasm.exe
C:\Program
Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\ildasm.exe
C:\Program
Files (x86)\Microsoft
SDKs\Windows\v7.0A\bin\x64\ildasm.exe
C:\Program
Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0
Tools\ildasm.exe
C:\Program
Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0
Tools\x64\ildasm.exe
all these different versions for? Which one should I use? Sumit
Kumar is the Project Manager for “Windows SDK”, so I sat down with
him to find out. The Windows SDK blog is at .
(Note: the
last four directories exist but don't actually contain a copy of
ildasm.exe. I just put them there for dramatic effect.)
the SDK tools?
tools are a sort of grab-bag of utilities that advanced programmers
will find useful. Some are general tools put forward by the Windows
and C++ teams. Others are for .Net development from the CLR or
Visual Studio teams. There are over 100 tools but so far I’ve only
used these:
&&&&&&&&&&&&&&&
ildasm.exe & dumps out the IL bytecode from a .net
&&&&&&&&&&&&&&&
peverify.exe & verifies that Visual Basic produces valid .net
assemblies.
&&&&&&&&&&&&&&&
gacutil.exe & checks which assemblies are in the Global Assembly
&&&&&&&&&&&&&&&
guidgen.exe & generates a new GUID
&&&&&&&&&&&&&&&
consume.exe & consumes memory or CPU or page-file or disk, as a
stress test
Windows SDK blog is a great place to find out about them. Bonus: a
.net game called Terrarium!
“Windows release track of the SDK tools” means that whenever a new
version of Windows is released, they release a new version of the
SDK tools as a free download. The intention is that this release of
the tools is good for targeting this release of Windows. So when
Version 6.0 of Windows was released, i.e. Vista, they released the
tools in “C:\Program Files\Microsoft SDKs\Windows\v6.0”.
“Visual Studio release track” means that whenever a new version of
Visual Studio is released, it incorporates a version of the SDK
tools as well. The intention is that this release of the tools is
good for targeting anything that this release of Visual Studio can
target. (Because VS can target most prior versions of the .Net
framework, it means that the VS version of the SDK tools will also
include prior versions of the .Net-specific tools.) For the VS
release track, the tools directory name has the suffix “A” after
it. So Visual Studio 9 (which came out after v6.0 of Windows) went
in “C:\Program Files\Microsoft SDKs\Windows\v6.0A”.
readers will look at the list above and think “Ahah! In the list
above he wrote v7.0A! The “A” means it’s a release of Visual
Studio. And the 7.0 means that the next Windows will be released
before Visual Studio 2010!” Hold your horses! I’m sorry to
disappoint you, but we picked these numbers and directory names for
the SDK a long time in advance, there's no guarantee that they're
final, and the numbers end up bearing no relation to release
schedules.
?& 64 bit ?
Studio 2010 will ship with a new version of .Net, version 4.0. But
it supports "multi-targeting", where you can target older versions
of .Net as well. And so it has to ship .Net3.5 versions of the
tools as well as .Net4.0 versions of the tools.
Also, some
of the tools have 64 others are 32-bit only. The 64bit
versions are only installed if you have a 64bit operating system.
The 64bit versions are found in subdirectories called "...\x64". If
the x64 directories does not contain a particular tool, then it
means that we haven't shipped a 64bit version of that tool, and
you're expected fall back to the 32bit version.
the complete table.& Only the shaded rows actually
exist. The rest are all potential future versions that Microsoft
won't commit to until it ships them.
Release "A"
C:\Program Files\Microsoft
SDKs\Windows\...
...\v6.0\bin\ildasm.exe
...\v6.0A\bin\ildasm.exe
...\v6.1\bin\ildasm.exe
Server2008
...\v7.0\bin\ildasm.exe
?? next version
...\v7.0\bin\x64\ildasm.exe
?? next version
...\v7.0A\bin\ildasm.exe
...\v7.0A\bin\x64\ildasm.exe
...\v7.0A\bin\NETFX 4.0
Tools\ildasm.exe
...\v7.0A\bin\NETFX 4.0
Tools\x64\ildasm.exe
C:\Program Files (x86)\Microsoft
SDKs\Windows\...
This directory only exists on x64 systems, and it
doesn't contain any tools executables.
Ignore it.
and folder locations
launch the "Visual Studio Command Line Tools" shortcut from the
start menu, it sets the PATH environment variable to point to
appropriate versions of the tools.
But if you
have your own tooling and want to point it to the correct
locations, you should use the registry. For release 7.0A of the
tools we expect to use these registry keys. Once again, these are
not final and might change when we ship Visual Studio
&[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-NetFx35Tools-x64]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A\bin\x64"
&[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-NetFx35Tools-x86]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A\bin"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-NetFx40Tools-x64]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\x64"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-NetFx40Tools-x86]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-VSHeadersLibs]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A"
&[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-VSTools]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A"
&[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDK-VSWin32Tools]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A"
&[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft
SDKs\Windows\v7.0A\WinSDKIntellisenseRefAssys]
"InstallationFolder"="C:\Program Files\Microsoft
SDKs\Windows\v7.0A"
&The set of registry keys isn't
complete for all past versions of the tools. To compute the path to
earlier versions of the tools is awkward. That's because different
languages use different paths (e.g. Italian versions of Windows use
C:\Programmi) and because users can themselves choose different
paths (e.g. E:\Apps).
Notionally
in VB you should puter.FileSystem.SpecialDirectories.ProgramFiles. This
returns "C:\Program Files" or its equivalent, and works great most
of the time. The only exception is if you're running on a 64bit
operating system and your assembly is set to target "CPU:x86"
rather than AnyCPU or x64. In this case it returns "C:\Program
Files (x86)" or its equivalent, which isn't any use.
the Windows SDK tools use non-localized directory and filenames. So
once you get to the path to the program files directory, you can
append "\Microsoft SDKs\Windows\v6.0\bin\ildasm.exe" or similar to
point to a particular tool.
&2 反汇编界面的那些符号都是什么意思?哪些是可以双击看结果的?
&摘自:/zh-tw/library/aa309387(v=vs.71).aspx
Ildasm.exe
Framework 1.1
這個教學課程提供了
.NET Framework SDK
中所附之 MSIL
反組譯工具 (Ildasm.exe)
的簡介。Ildasm.exe
工具可以剖析任何 .NET Framework
組件 (Assembly),並且以人們可閱讀的
(Human-Readable)
格式顯示資訊。Ildasm.exe
不只會顯示 Microsoft
Intermediate Language (MSIL)
程式碼,也會顯示包括介面在內的命名空間 (Namespace)
和型別。您可以使用 Ildasm.exe
來檢查 .NET
的原生組件 (例如
Mscorlib.dll),以及其他人提供的或是您自行建立的
.NET Framework
組件。大部分 .NET
開發人員都會認為 Ildasm.exe
是不可或缺的工具。
對於這個教學課程,請使用隨附在 SDK
中之 WordCount
範例的 Visual C#
版本。您也可以使用 Visual
版本,但是這兩種語言所產生的 MSIL
將會不同,畫面影像也不盡相同。WordCount
位於 \Samples\Applications\WordCount\
目錄中。若要建置(Build)
和執行範例,請依照 Readme.htm
檔案中所列的指示進行。這個教學課程會使用 Ildasm.exe
來檢查 WordCount.exe
若要開始,請建置 WordCount
範例,並使用下列命令列將它載入 Ildasm.exe
WordCount.exe
這樣便會使 Ildasm.exe
的視窗顯示出來,如下圖所示。
Ildasm.exe 視窗中的樹狀結構會顯示WordCount.exe 內部包含的組件資訊清單資訊,以及四個全域類別型別:App、ArgParser、WordCountArgParser&和&WordCounter。
按兩下樹狀結構中的任何型別,即可看見型別的詳細資訊。在下圖中,已將&WordCounter&類別型別展開。
在上圖中,您可以看見所有的&WordCounter&成員。下列表格將說明每一個圖形符號所代表的意義。
按兩下&.class public auto ansi
beforefieldinit&項目會顯示下列資訊:
在上圖中,您可以清楚地看出來,WordCounter&型別是從&System.Object&型別衍生而來。
WordCounter&型別還包含另外一個型別,名稱為&WordOccurrence。您可以將&WordOccurrence&型別展開,即可看見它的成員,如下圖所示。
從樹狀結構中可以看出來,WordOccurrence&實作了&System.IComparable&介面,更明確地講,就是&CompareTo&方法。不過,在後續的說明中,我們將略過&WordOccurrence&型別,而將焦點集中在&WordCounter&型別上。
您可以看到&WordCounter&型別含有五個&private&欄位:totalBytes、totalChars、totalLines、totalWords&和&wordCounter。前四個欄位是&int64&型別的執行個體(Instance),而&wordCounter&欄位則是&System.Collections.SortedList&型別的參考。
在這些欄位之後,您可以看見方法。第一個方法&.ctor&是建構函式
(Constructor)。這個特殊的型別只有一個建構函式,但是其他型別則可以有多個建構函式,每一個的簽名碼
(Signature)
都不同。WordCounter&建構函式的傳回型別
(Return Type)
為&void&(和所有建構函式一樣),而且不接受參數。如果按兩下建構函式方法,會出現新的視窗,它會顯示方法中所包含的
程式碼,如下圖所示。
程式碼實際上很容易閱讀和了解 (如需所有的詳細資訊,請參閱位於
\Tool Developers
Guide\Docs
資料夾 Partition III
檔案中的&CIL Instruction Set
Specification)。在最接近頂端的地方,您可以看見這個建構函式需要
個位元組的 MSIL
程式碼。從這個數字並不能判斷出 JIT
編譯器將會發出多少機器碼,因為它的大小是根據主機 CPU
和用來產生程式碼的編譯器而定。
Language Runtime
為堆疊架構。因此,若要執行任何作業,MSIL
程式碼會先將運算元推入虛擬堆疊中,然後執行運算子。運算子會將運算元從堆疊中抓取出來、執行所需的作業,然後將結果放回堆疊上。不論任何時候,這個方法推入虛擬堆疊上的運算元都不能超過八個。您只要查看出現在
程式碼前面的&.maxstack&屬性,就可以辨識出這個數目。
現在,請檢查前面幾個 MSIL
指令,如下列四行:
ldarg.0 ; Load the object's 'this' pointer on the stack
IL_0001: ldc.i4.0 ; Load the constant 4-byte value of 0 on the
IL_0002: conv.i8 ; Convert the 4-byte 0 to an 8-byte 0
IL_0003: stfld int64 WordCounter::totalLines
位於 IL_0000
的指令會將傳遞至方法的第一個參數載入到虛擬堆疊上,而且一定會將物件記憶體的位址傳遞給每一個執行個體方法
Method)。這個引數稱為
Zero,而且不會明確顯示在方法的簽名碼中。因此,即使&.ctor&方法看起來像是接收了零個引數,實際上是接收了一個引數。接著,位於
的指令會將這個物件的指標載入到虛擬堆疊上。
位於 IL_0001
的指令會將 4
位元組的零值常數載入到虛擬堆疊上。
位於 IL_0002
的指令會取得堆疊頂端的數值 (4
位元組的零),並將它轉換成
位元組的零,如此便會將 8
位元組的零放到堆疊的頂端。
這時,堆疊含有兩個運算元:8
位元組的零和指向這個物件的指標。位於 IL_0003
的指令會使用這兩個運算元,將堆疊頂端的數值 (8
位元組的零)
儲存到堆疊上所辨識物件的&totalLines&欄位中。
totalChars、totalBytes&和&totalWords&欄位會重複相同的
指令序列 (Sequence)。
wordCounter&欄位的初始化是從位於IL_0020
的指令開始,如以下所示:
IL_0021: newobj instance void
[mscorlib]System.Collections.SortedList::.ctor()
IL_0026: stfld class [mscorlib]System.Collections.SortedList
WordCounter::wordCounter
位於 IL_0020
的指令會將&WordCounter&的&this&指標推入虛擬堆疊上。newobj&指令不會使用這個運算元,但是位於
的&stfld&指令將會使用。
位於 IL_0021
的指令會告知 Runtime
建立新的&System.Collections.SortedList&物件,並且不使用任何引數呼叫它的建構函式。當&newobj&傳回時,SortedList&物件的位址是在堆疊上。這時候,位於
的&stfld&指令會將&SortedList&物件的指標儲存到&WordCounter&物件的&wordCounter&欄位中。
在所有&WordCounter&物件的欄位都完成初始化之後,位於IL_002b
的指令會將&this&指標推到虛擬堆疊上,然後
會呼叫基底型別 (Base Type)
(System.Object)
中的建構函式。
當然,位於 IL_0031
的最後一個指令是傳回指令,會使&WordCounter&建構函式傳回到建立函式的程式碼中。建構函式必須傳回&void,在建構函式傳回之前才不會將任何物件放入堆疊上。
下面是另外一個範例。請按兩下&GetWordsByOccurranceEnumerator&方法,即可看見它的MSIL
程式碼,如下圖所示。
您可以看見,這個方法的程式碼大小為 69
個位元組,而且這個方法在虛擬堆疊上需要有四個位置。此外,這個方法還有三個區域變數:一個為&System.Collection.SortedList&型別,另外兩個為&System.Collections.IDictionaryEnumerator&型別。請注意,除非組件與/debug
選項相容,否則不會將原始程式碼中提到的變數名稱發出到 MSIL
程式碼中。如果沒有使用 /debug,會分別使用&V_0、V_1&和&V_2&等變數名稱來取代&sl、de&和&CS$00000。
當這個方法開始執行時,第一件事就是執行&newobj&指令,這個指令會建立新的&System.Collections.SortedList&物件,並呼叫這個物件的預設建構函式。當&newobj&傳回時,所建立物件的位址是在虛擬堆疊上。stloc.0&指令
會將這個值儲存在區域變數 0
或&sl(不含
的&V_0)&(屬於&System.Collections.SortedList&型別)
位於 IL_0006
和 IL_0007
的指令會將&WordCounter&物件的&this&指標
(在傳遞至方法的Argument Zero
載入到堆疊上,並呼叫&GetWordsAlphabeticallyEnumerator&方法。當&call&指令傳回時,列舉值的位址是在堆疊上。stloc.1&指令
會將這個位址儲存在區域變數 1
或&de&(不含
的&V_1),這個變數屬於&System.Collections.IDictionaryEnumerator&型別。
位於 IL_000d
的&br.s&指令會造成&while&陳述式的&IL&測試條件無條件分支。這個&IL&測試條件從位於
的指令開始。在 IL_0032
位址中,會將&de&(或&V_1)
(IDictionaryEnumerator)
的位址推到堆疊上,然後在 IL_0033
位址呼叫它的&MoveNext&方法。如果&MoveNext&傳回&True,表示有要列舉的項目,而且&brtrue.s&指令會跳到位於IL_000f
在位於 IL_000f
和 IL_0010
的指令中,會將&sl&(或&V_0)&和&de&(或&V_1)&中的物件位址推到堆疊上。然後,呼叫&IdictionaryEnumerator&物件的&get_Value&屬性方法,以取得目前項目的項目數目。這個數目是儲存在&System.Int32&中的
位元值。程式碼會將&Int32&物件轉換成int&數值型別
(Value Type)。將參考型別
(Reference Type)
轉換成數值型別需要位於 IL_0016
的&unbox&指令。當&unbox&傳回時,Unboxed
數值的位址是在堆疊上。ldind.i4&指令
位元組的數值 (這個數值會指向目前在堆疊上的位址)
載入到堆疊上。換句話說,Unboxed
位元組整數是放在堆疊上。
在位於 IL_001c
的指令中,會將&sl&(或&V_1)&的數值
(IDictionaryEnumerator&的位址)
推到堆疊上,並呼叫它的&get_Key&屬性方法。當&get_Key&傳回時,System.Object&的位址是在堆疊上。程式碼知道字典中包含字串,因此編譯器會使用位於
的&castclass&指令,將這個&Object&轉換成&String。
以下幾個新的指令 (從
到 IL_002d)
會建立新的&WordOccurrence&物件,然後將物件的位址傳遞至&SortedLists&物件的&Add&方法。
在位於 IL_0032
的指令中,會再評估一次&while&陳述式的測試條件。如果&MoveNext&傳回&True,迴圈
會執行另一個循環。但是,如果&MoveNext&傳回&False,迴圈會停止執行,並在位於
的指令結束。位於 IL_003a
到 IL_0040
的指令會呼叫&SortLists&物件的GetEnumerator&方法。傳回的值為&System.Collections.IDictionaryEnumerator,它是被留在堆疊上而成為&GetWordsByOccurrenceEnumerator&傳回值。
虽然摘自的我觉得都写得很好,但是再次申明,我只是与ildasm初见,我也看不明白!
已投稿到:初识exe程序反汇编小感[转]_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
初识exe程序反汇编小感[转]
上传于||暂无简介
阅读已结束,如果下载本文需要使用2下载券
想免费下载本文?
下载文档到电脑,查找使用更方便
还剩4页未读,继续阅读
你可能喜欢09-1109-1109-1109-11
03-1202-2809-1009-11
也许你感兴趣
1. 2. 3. 4. 5. 6. 7. 8. 9. 10.Re: windbg反汇编函数的问题 - 高端调试 :: 论坛
欢迎光临 高端调试
帖子排序:&
Oldest to newest
Newest to oldest
windbg反汇编函数的问题
最近学习windows的SEH机制,在windbg下显示如下的错误lkd& uf ntdll!KiUserExceptionDispatcherCouldn't resolve error at 'ntdll!KiUserExceptionDispatcher'lkd& x ntdll!*Dispatcher*&&&&&&&&&&& ^ Couldn't resolve 'x ntdll'系统是win8.1 x64请问该如何解决
IP 地址: 已记录&&
Re: windbg反汇编函数的问题
Symbol 没对#&.sympath+ symsrv*symsrv.dll*d:\symbols*/download/symbols# .reload /s /f ntdll.dll
IP 地址: 已记录&&
Re: windbg反汇编函数的问题
lkd&& .reload /s /f ntdll.dll"ntdll.dll" was not found in the image list.Debugger will attempt to load "ntdll.dll" at given base 00000.Please provide the full image name, including the extension (i.e. kernel32.dll)for more reliable results.Base address and size overrides can be given as.reload &image.ext&=&base&,&size&.Unable to add module at 00000
IP 地址: 已记录&&
Re: windbg反汇编函数的问题
你是km debug吗?先重启下系统看看吧
IP 地址: 已记录&&
Re: windbg反汇编函数的问题
&突然正常了
IP 地址: 已记录&&
Re: windbg反汇编函数的问题
又不行了时好时坏会不会跟杀软有关?
IP 地址: 已记录&&
|- 论坛搜索
|- 热门主题
|- 未回复的主题
|- 找回密码
|- Windows内核调试
|- C/C++本地代码调试
|- .Net程序调试
|- 脚本程序调试
|- Java程序调试
|- Linux内核调试
|- 《程序员》杂志调试专栏
|- 远程调试
|- 调试ACPI和BIOS
|- 特殊的调试任务
|- 转储分析
|- Windows内核
|- Linux内核
|- CPU架构
|- PCI/PCI Express架构
|- 软件物语
|- 社区活动
|- 名人逸事
|- 欢迎使用CnForums
|- BUG也精彩
|- 豆腐工程
|- 软件圈里十大怪
Windows Vista
|- 用调试利剑剖析VISTA内幕
|- 老专家如何破解新问题
|- 我的电脑谁说了算?
Office开发
驱动程序开发
|- Windows驱动开发
|- Linux驱动开发
|- Windows CE驱动开发
用户态开发
|- Windows本地代码(native)高级开发
|- Web应用开发
|- WinFX和.Net
|- Office开发
|- 高端调试团队
|- 版面布局
|- 活动建议
|- 网站维护
|- 64-bit Windows
|- 64-bit CPU
|- 《软件调试》的示例程序
|- 《软件调试》的工具
|- 《软件调试》书友
|- 《软件调试》答疑
|- 《软件调试》勘误和意见
|- 《格蠹汇编》
|- PaaS和SaaS
|- 游戏开发与调试
(C) ADVDBG.ORG All Rights Reserved.(转)gdb反汇编小结 - micky.zhou - 博客园
随笔 - 22, 文章 - 2, 评论 - 1, 引用 - 0
如果在Linux平台可以用gdb进行反汇编和调试。(转)
2. 最简C代码分析
&&& 为简化问题,来分析一下最简的c代码生成的汇编代码:
&&& # vi test1.c
&&& int main()
&& && & return 0;
&&& 编译该程序,产生二进制文件:
&&& # gcc test1.c -o test1
& & # file test1 &
&&& test1: ELF 32-bit LSB executable 80386 Version 1, dynamically linked, not stripped
&&& test1是一个ELF格式32位小端(Little Endian)的可执行文件,动态链接并且符号表没有去除。
&&& 这正是Unix/Linux平台典型的可执行文件格式。
&&& 用mdb反汇编可以观察生成的汇编代码:
&&& # mdb test1
&&& Loading modules: [ libc.so.1 ]
&&& & main::dis&&& &&& &&& &&& &&& && ; 反汇编main函数,mdb的命令一般格式为& &地址&::dis
&&& main:&&& &&& & pushl&& %ebp&& &&& ; ebp寄存器内容压栈,即保存main函数的上级调用函数的栈基地址
&&& main+1:&&& &&& movl&&& %esp,%ebp& ; esp值赋给ebp,设置main函数的栈基址
&&& main+3:&&&&&&&&& subl&&& $8,%esp
&&& main+6:&&& &&& & andl&&& $0xf0,%esp
&&& main+9:&&&&&&&&& movl&&& $0,%eax
&&& main+0xe:&&& &&& subl&&& %eax,%esp
&&& main+0x10:&&&& movl&&& $0,%eax&&& ; 设置函数返回值0
&&& main+0x15:&&&& leave&&&&&&&&&&&&& ; 将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址
&&& main+0x16:&&&& ret&&&&&&&&&&&&&&& ; main函数返回,回到上级调用
&&& 注:这里得到的汇编语言语法格式与Intel的手册有很大不同,Unix/Linux采用AT&T汇编格式作为汇编语言的语法格式
&&& &&&& 如果想了解AT&T汇编可以参考文章:
&&& 问题:谁调用了 main函数?
&&&& 在C语言的层面来看,main函数是一个程序的起始入口点,而实际上,ELF可执行文件的入口点并不是main而是_start。
&&&& mdb也可以反汇编_start:
&&& & _start::dis&&& &&& &&& &&& &&& && ;从_start 的地址开始反汇编
&&& _start:&&& &&& &&&&& pushl&& $0
&&& _start+2:&&& &&& &&& pushl&& $0
&&& _start+4:&&& &&& &&& movl&&& %esp,%ebp
&&& _start+6:&&& &&& &&& pushl&& %edx
&&& _start+7:&&& &&& &&& movl&&& $0x80504b0,%eax
&&& _start+0xc:&&& &&& & testl&& %eax,%eax
&&& _start+0xe:&&&&&&&&& je&&&&& +0xf&&&&&&&&&&& &_start+0x1d&
&&& _start+0x10:&&&&&&&& pushl&& $0x80504b0
&&& _start+0x15:&&&&&&&& call&&& -0x75&&&&&&&&&& &atexit&
&&& _start+0x1a:&&&&&&&& addl&&& $4,%esp
&&& _start+0x1d:&&&&&&&& movl&&& $0x8060710,%eax
&&& _start+0x22:&&&&&&&& testl&& %eax,%eax
&&& _start+0x24:&&&&&&&& je&&&&& +7&&&&&&&&&&&&& &_start+0x2b&
&&& _start+0x26:&&&&&&&& call&&& -0x86&&&&&&&&&& &atexit&
&&& _start+0x2b:&&&&&&&& pushl&& $0x80506cd
&&& _start+0x30:&&&&&&&& call&&& -0x90&&&&&&&&&& &atexit&
&&& _start+0x35:&&&&&&&& movl&&& +8(%ebp),%eax
&&& _start+0x38:&&&&&&&& leal&&& +0x10(%ebp,%eax,4),%edx
&&& _start+0x3c:&&&&&&&& movl&&& %edx,0x8060804
&&& _start+0x42:&&&&&&&& andl&&& $0xf0,%esp
&&& _start+0x45:&&&&&&&& subl&&& $4,%esp
&&& _start+0x48:&&&&&&&& pushl&& %edx
&&& _start+0x49:&&&&&&&& leal&&& +0xc(%ebp),%edx
&&& _start+0x4c:&&&&&&&& pushl&& %edx
&&& _start+0x4d:&&&&&&&& pushl&& %eax
&&& _start+0x4e:&&&&&&&& call&&& +0x152&&&&&&&&& &_init&
&&& _start+0x53:&&&&&&&& call&&& -0xa3&&&&&&&&&& &__fpstart&
&&& _start+0x58:&&&&&&& call&&& +0xfb&&&&&&& &main&&&&&&&&&&&&&& ;在这里调用了main函数
&&& _start+0x5d:&&&&&&&& addl&&& $0xc,%esp
&&& _start+0x60:&&&&&&&& pushl&& %eax
&&& _start+0x61:&&&&&&&& call&&& -0xa1&&&&&&&&&& &exit&
&&& _start+0x66:&&&&&&&& pushl&& $0
&&& _start+0x68:&&&&&&&& movl&&& $1,%eax
&&& _start+0x6d:&&&&&&&& lcall&& $7,$0
&&& _start+0x74:&&&&&&&& hlt
&&& 问题:为什么用EAX寄存器保存函数返回值?
&&& 实际上IA32并没有规定用哪个寄存器来保存返回值。但如果反汇编Solaris/Linux的二进制文件,就会发现,都用EAX保存函数返回值。
&&& 这不是偶然现象,是操作系统的ABI(Application Binary Interface)来决定的。
&&& Solaris/Linux操作系统的ABI就是Sytem V ABI。
&&& 概念:SFP (Stack Frame Pointer) 栈框架指针&
&&& 正确理解SFP必须了解:
&&& &&& IA32 的栈的概念
&&& &&& CPU 中32位寄存器ESP/EBP的作用
&&& &&& PUSH/POP 指令是如何影响栈的
&&& &&& CALL/RET/LEAVE 等指令是如何影响栈的
&&& 如我们所知:
&&& 1)IA32的栈是用来存放临时数据,而且是LIFO,即后进先出的。栈的增长方向是从高地址向低地址增长,按字节为单位编址。
&&& 2) EBP是栈基址的指针,永远指向栈底(高地址),ESP是栈指针,永远指向栈顶(低地址)。
&&& 3) PUSH一个long型数据时,以字节为单位将数据压入栈,从高到低按字节依次将数据存入ESP-1、ESP-2、ESP-3、ESP-4的地址单元。
&&& 4) POP一个long型数据,过程与PUSH相反,依次将ESP-4、ESP-3、ESP-2、ESP-1从栈内弹出,放入一个32位寄存器。
&&& 5) CALL指令用来调用一个函数或过程,此时,下一条指令地址会被压入堆栈,以备返回时能恢复执行下条指令。
&&& 6) RET指令用来从一个函数或过程返回,之前CALL保存的下条指令地址会从栈内弹出到EIP寄存器中,程序转到CALL之前下条指令处执行
&&& 7) ENTER是建立当前函数的栈框架,即相当于以下两条指令:
&&& &&& pushl&& %ebp
&&& &&& movl&&& %esp,%ebp
&&& 8) LEAVE是释放当前函数或者过程的栈框架,即相当于以下两条指令:
&&& &&& movl ebp esp
&&& &&& popl& ebp
&&& 如果反汇编一个函数,很多时候会在函数进入和返回处,发现有类似如下形式的汇编语句:
&&& &&& pushl&& %ebp&&& &&& &&& ; ebp寄存器内容压栈,即保存main函数的上级调用函数的栈基地址
&&& &&& movl&&& %esp,%ebp&&& && ; esp值赋给ebp,设置 main函数的栈基址
&&& &&& ...........&&& &&& &&&& ; 以上两条指令相当于 enter 0,0
&&& &&& ...........
&&& &&& leave&&& &&& &&& &&& && ; 将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址
&&& &&& ret&&&&&&&&&&&&&&&&&&&& ; main函数返回,回到上级调用
&&& 这些语句就是用来创建和释放一个函数或者过程的栈框架的。
&&& 原来编译器会自动在函数入口和出口处插入创建和释放栈框架的语句。
&&& 函数被调用时:
&&& 1) EIP/EBP成为新函数栈的边界
&&& 函数被调用时,返回时的EIP首先被压入堆栈;创建栈框架时,上级函数栈的EBP被压入堆栈,与EIP一道行成新函数栈框架的边界
&&& 2) EBP成为栈框架指针SFP,用来指示新函数栈的边界
&&& 栈框架建立后,EBP指向的栈的内容就是上一级函数栈的EBP,可以想象,通过EBP就可以把层层调用函数的栈都回朔遍历一遍,调试器就是利用这个特性实现 backtrace功能的
&&& 3) ESP总是作为栈指针指向栈顶,用来分配栈空间
&&& 栈分配空间给函数局部变量时的语句通常就是给ESP减去一个常数值,例如,分配一个整型数据就是 ESP-4
&&& 4) 函数的参数传递和局部变量访问可以通过SFP即EBP来实现
&&& 由于栈框架指针永远指向当前函数的栈基地址,参数和局部变量访问通常为如下形式:
&&& &&& +8+xx(%ebp)&& && && ; 函数入口参数的的访问
&&& &&& -xx(%ebp)&&& &&& && ; 函数局部变量访问
&&&&&&&&&&&
&&& 假如函数A调用函数B,函数B调用函数C ,则函数栈框架及调用关系如下图所示:
+-------------------------+----& 高地址
| EIP (上级函数返回地址)
+-------------------------+
| EBP (上级函数的EBP)
| --+ &------当前函数A的EBP (即SFP框架指针)
+-------------------------+
+--&偏移量A
| Local Variables
| ..........
&------ESP指向函数A新分配的局部变量,局部变量可以通过A的ebp-偏移量A访问
+-------------------------+
| Arg n(函数B的第n个参数)
+-------------------------+
| Arg .(函数B的第.个参数)
+-------------------------+
| Arg 1(函数B的第1个参数)
+-------------------------+
| Arg 0(函数B的第0个参数)
| --+ &------ B函数的参数可以由B的ebp+偏移量B访问
+-------------------------+
+--& 偏移量B
| EIP (A函数的返回地址)
+-------------------------+ --+
| EBP (A函数的EBP)
|&--+ &------ 当前函数B的EBP (即SFP框架指针)
+-------------------------+
| Local Variables
| ..........
| &------ ESP指向函数B新分配的局部变量
+-------------------------+
| Arg n(函数C的第n个参数)
+-------------------------+
| Arg .(函数C的第.个参数)
+-------------------------+
+--& frame of B
| Arg 1(函数C的第1个参数)
+-------------------------+
| Arg 0(函数C的第0个参数)
+-------------------------+
| EIP (B函数的返回地址)
+-------------------------+
| EBP (B函数的EBP)
| --+ &------ 当前函数C的EBP (即SFP框架指针)
+-------------------------+
| Local Variables
| ..........
| &------ ESP指向函数C新分配的局部变量
+-------------------------+----& 低地址
frame of C
&&& 再分析test1反汇编结果中剩余部分语句的含义:
&&& # mdb test1
&&& Loading modules: [ libc.so.1 ]
&&& & main::dis&&&&&&&&&&&&&&&&&&&&&&& ; 反汇编main函数
&&& main:&&& &&&&& pushl&& %ebp&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&& main+1:&&& &&& movl&&& %esp,%ebp&&&&&&& ; 创建Stack Frame(栈框架)
&&& main+3:&&& && subl&&& $8,%esp&&&&&& ; 通过ESP-8来分配8字节堆栈空间
&&& main+6:&&& && andl&&& $0xf0,%esp&&& ; 使栈地址16字节对齐
&&& main+9:&&& && movl&&& $0,%eax&&&&&& ; 无意义
&&& main+0xe:&&&& subl&&& %eax,%esp&&&& ; 无意义
&&& main+0x10:&&&& movl&&& $0,%eax&&&&&&&&& ; 设置main函数返回值
&&& main+0x15:&&&& leave&&&&&&&&&&&&&&&&& & ; 撤销Stack Frame(栈框架)
&&& main+0x16:&&&& ret&&&&&&&&&&&&&&&&&&&&& ; main 函数返回
&&& 以下两句似乎是没有意义的,果真是这样吗?
&&& &&& movl&&& $0,%eax
&&& &&& subl&&&& %eax,%esp
&&& 用gcc的O2级优化来重新编译test1.c:
&&& # gcc -O2 test1.c -o test1
&&& # mdb test1
&&& & main::dis
&&& main:&&& &&&& pushl&& %ebp
&&& main+1:&&& && movl&&& %esp,%ebp
&&& main+3:&&& && subl&&& $8,%esp
&&& main+6:&&& && andl&&& $0xf0,%esp
&&& main+9:&&& && xorl&&& %eax,%eax&&&&& ; 设置main返回值,使用xorl异或指令来使eax为0
&&& main+0xb:&&&& leave
&&& main+0xc:&&&& ret
&&& 新的反汇编结果比最初的结果要简洁一些,果然之前被认为无用的语句被优化掉了,进一步验证了之前的猜测。
&&& 提示:编译器产生的某些语句可能在程序实际语义上没有用处,可以用优化选项去掉这些语句。
&&& 问题:为什么用xorl来设置eax的值?
&&& 注意到优化后的代码中,eax返回值的设置由 movl $0,%eax 变为 xorl %eax,%eax ,这是因为IA32指令中,xorl比movl有更高的运行速度。
&&& 概念:Stack aligned 栈对齐
&&& 那么,以下语句到底是和作用呢?
&&& &&& subl&&& $8,%esp
&&&&&& andl&&& $0xf0,%esp&&&& ; 通过andl使低4位为0,保证栈地址16字节对齐
&&& 表面来看,这条语句最直接的后果是使ESP的地址后4位为0,即16字节对齐,那么为什么这么做呢?
&&& 原来,IA32 系列CPU的一些指令分别在4、8、16字节对齐时会有更快的运行速度,因此gcc编译器为提高生成代码在IA32上的运行速度,默认对产生的代码进行16字节对齐
&&& &&& andl $0xf0,%esp 的意义很明显,那么 subl $8,%esp 呢,是必须的吗?
&&& 这里假设在进入main函数之前,栈是16字节对齐的话,那么,进入main函数后,EIP和EBP被压入堆栈后,栈地址最末4位二进制位必定是1000,esp -8则恰好使后4位地址二进制位为0000。看来,这也是为保证栈16字节对齐的。
&&& 如果查一下gcc的手册,就会发现关于栈对齐的参数设置:
&&& -mpreferred-stack-boundary=n&&& ; 希望栈按照2的n次的字节边界对齐, n的取值范围是2-12
&&& 默认情况下,n是等于4的,也就是说,默认情况下,gcc是16字节对齐,以适应IA32大多数指令的要求。
&&& 让我们利用-mpreferred-stack-boundary=2来去除栈对齐指令:
&&& # gcc -mpreferred-stack-boundary=2 test1.c -o test1
&&& & main::dis
&&& main:&&&&&& pushl&& %ebp
&&& main+1:&&&& movl&&& %esp,%ebp
&&& main+3:&&&& movl&&& $0,%eax
&&& main+8:&&&& leave
&&& main+9:&&&& ret
&&& 可以看到,栈对齐指令没有了,因为,IA32的栈本身就是4字节对齐的,不需要用额外指令进行对齐。
&&& 那么,栈框架指针SFP是不是必须的呢?
&&& # gcc -mpreferred-stack-boundary=2 -fomit-frame-pointer test1.c -o test
&&& & main::dis
&&& main:&&&&&& movl&&& $0,%eax
&&& main+5:&&&& ret
&&& 由此可知,-fomit-frame-pointer 可以去除SFP。
&&& 问题:去除SFP后有什么缺点呢?
&&& 1)增加调式难度
&&& &&& 由于SFP在调试器backtrace的指令中被使用到,因此没有SFP该调试指令就无法使用。
&&& 2)降低汇编代码可读性
&&& &&& 函数参数和局部变量的访问,在没有ebp的情况下,都只能通过+xx(esp)的方式访问,而很难区分两种方式,降低了程序的可读性。
&&& 问题:去除SFP有什么优点呢?
&&& 1)节省栈空间
&&& 2)减少建立和撤销栈框架的指令后,简化了代码
&&& 3)使ebp空闲出来,使之作为通用寄存器使用,增加通用寄存器的数量
&&& 4)以上3点使得程序运行速度更快
&&& 概念:Calling Convention& 调用约定和 ABI (Application Binary Interface) 应用程序二进制接口
&&& &&& 函数如何找到它的参数?
&&& &&& 函数如何返回结果?
&&& &&& 函数在哪里存放局部变量?
&&& &&& 那一个硬件寄存器是起始空间?
&&& &&& 那一个硬件寄存器必须预先保留?
&&& Calling Convention& 调用约定对以上问题作出了规定。Calling Convention也是ABI的一部分。
&&& 因此,遵守相同ABI规范的操作系统,使其相互间实现二进制代码的互操作成为了可能。
&&& 例如:由于Solaris、Linux都遵守System V的ABI,Solaris 10就提供了直接运行Linux二进制程序的功能。
&&& 详见文章:
&&& && && &&
&&& 本文通过最简的C程序,引入以下概念:
&&& &&& SFP 栈框架指针
&&& &&& Stack aligned 栈对齐
&&& &&& Calling Convention& 调用约定 和 ABI (Application Binary Interface) 应用程序二进制接口
&&& 今后,将通过进一步的实验,来深入了解这些概念。通过掌握这些概念,使在汇编级调试程序产生的core dump、掌握C语言高级调试技巧成为了可能。

我要回帖

更多关于 小小军团合战三国问题 的文章

 

随机推荐