1. 程式人生 > >《逆向分析實戰》數據的存儲及表示形式

《逆向分析實戰》數據的存儲及表示形式

函數 最簡 先來 職位 ascii碼表 封裝 process 社區 音頻

本文摘自人民郵電出版社異步社區《逆向分析實戰》一書,點擊查看http://www.epubit.com.cn/book/details/4918
關註微信公眾號【異步社區】每周送書

學習過計算機的讀者都知道,計算機中的各種數據都是以二進制形式進行存儲的,無論是文本文件、圖片文件,還是音頻文件、視頻文件、可執行文件等,統統都是由二進制文件存儲的。學習過計算機的讀者在學習計算機基礎的時候一定學習過進制轉換,也一定學習過數據的表示方式等,大部分人在學習這部分知識時會覺得枯燥、無用,但是對於學習逆向知識和使用逆向工具,數據的存儲及表示形式是必須要掌握的。
本文借助OllyDbg這款調試工具來一起討論數據的存儲及表示形式,讓讀者對於學習計算機的數據存儲及表示可以更加的感性,從而脫離純粹理論性的學習。

本文內容較為枯燥,但是著實是學習逆向的基礎知識,對於從來沒有接觸過逆向或者是剛開始接觸逆向的讀者,本文內容還是有一定幫助的。

本文關鍵字:進制 數據表示 數據轉換 數據存儲

1.1 進制及進制的轉換

了解進制的概念及進制的轉換是學習逆向的基礎,因為計算機使用的進制是二進制,它又不同於我們現實生活中使用的十進制,因此我們必須學習不同的進制及進制之間的轉換。
1.1.1 現實生活中的進制與計算機的二進制

我們在現實生活中會接觸到多種多樣的進制,通常見到的有十進制、十二進制和二十四進制等。下面分別對這幾種進制進行舉例說明。

十進制是每個人從上學就開始接觸和學習的進制表示方法。所謂的十進制,就是逢十進一,最簡單的例子就是9+1=10。這個無需過多解釋。

十二進制也是我們日常生活中常見的表示方法。所謂的十二進制,就是逢十二進一,例如12個月為1年,13個月就是1年1個月。

二十四進制也是我們日常生活中常見的表示方法。所謂的二十四進制,就是逢二十四進一,例如24小時為1天,25小時就是1天1小時。

介紹了以上現實生活中的例子後,我們再來說說計算機中的二進制。根據前面各種進制的解釋,我們可以想到,二進制就是逢二進一。這裏舉個不太恰當的例子,例如2斤就是1公斤。

在計算機中為什麽使用二進制呢?簡單說就是計算機用高電平和低電平來表示1和0最為方便和穩定,高電平被認為是1,低電平被認為是0,這就是所謂的二進制的來源。

由於二進制在閱讀上不方便,計算機又引入了十六進制來直觀地表示二進制。所謂的十六進制,就是逢十六進一。

因此在計算機中,我們常見的數據表示方法有二進制、十進制和十六進制。

1.1.2 進制的定義

在學習小學數學的時候我們就學習了十進制,十進制一共有十個數字,從0一直到9,9再往後數一個的時候要產生進位,也就是逢十進一。總結十進制的定義則是,由0到9十個數字組成,並且逢十進一。

舉一反一地來說,二進制的定義是,由0到1兩個數字組成,逢二進一。十六進制的定義是由0到9十個數字和A到F六個字母組成,逢十六進一。

由此,我們衍生出N進制的定義是,由N個符號組成,逢N進一。

表1-1所列為這三種進制的數字表。
表1-1  二進制、十進制和十六進制數字表

技術分享圖片

1.1.3 進制的轉換
在逆向當中,我們直接面對的通常是十六進制,而由於很多原因,我們需要將其當作十進制或二進制來查看,當然也有可能需要根據二進制轉換成十六進制或十進制。所以,我們就需要掌握進制之間的轉換。

1.二進制轉十進制
二進制整數的每個位都是2的冪次方,最低位是2的0次方,最高為是2的(N-1)次方,我們通過一個例子進行說明。我們把二進制數10010011轉換成十進制數,計算方式如下:

10010011 = 1 × 27 + 0 × 26 + 0 × 25 + 1 × 24 + 0 × 23 + 0 × 22 + 1 × 21 + 1 × 20 = 128 + 0 + 0 +16 + 0 + 0 + 2 + 1 = 147

我們得出的結果是,把二進制10010011轉換成十進制後是147。我們用計算機進行驗算,如圖1-1和圖1-2所示。

從圖1-1和圖1-2中可以看出,我們的計算結果是正確的,由此讀者在計算二進制時按照上面轉換的例子進行轉換即可。
技術分享圖片
圖1-1 驗算二進制(一)
技術分享圖片
圖1-2 驗算二進制(二)
2.十六進制與二進制的轉換
由於一個簡單的數值用二進制表示需要很長的位數,這樣對於閱讀很不方便,因此匯編和調試器常用十六進制表示二進制。十六進制的每個位可以代表4個二進制位,因為2的4次方剛好是16。這樣,在二進制與十六進制之間就產生了一個很好的對應關系,如表1-2所列。

表1-2  二進制對應的十六進制與十進制數
技術分享圖片
根據此表,我們可以很快地把二進制和十六進制進行轉換,把上例的二進制10010011轉換成十六進制,轉換過程如下:

第一步,把10010011從最低開始按每四位分為一組,不足四位前面補0,劃分結果為1001 0011;

第二步,把劃分好的組進行查表,1001對應十六進制是9,0011對應的十六進制是3。

那麽,二進制10010011轉換成十六進制後的值是93。讀者可以通過計算器自行進行驗算。

在逆向中常用的就是二進制與十進制的轉換,或者是二進制與十六進制的轉換,其他的轉換方式讀者可以自行查找資料進行學習。關於十六進制和二進制需要記住的重要一點就是,一位十六進制數可以表示四位二進制數。

1.2 數據寬度、字節序和ASCII碼

前面介紹了計算機中常用的進制表示方法和轉換,現在讀者知道了計算機存儲的都是二進制的數據,那麽接下來要討論的是在計算機中數據存儲的單位以及數據是如何存儲在存儲空間的。
1.2.1 數據的寬度
數據的寬度是指數據在存儲器中存儲的尺寸。在計算機中,所有數據的基本存儲單位都是字節(byte),每個字節占8個位(位是計算機存儲的最小單位,而不是基本單位,因為在存儲數據時幾乎沒有按位進行存儲的)。其他的存儲單位還有字(word)、雙字(dword)和八字節(qword)。
技術分享圖片
圖1-3 給出各個存儲單位所包含的位數。
在計算機編程中,常用的幾個重要數據存儲單位分別就是byte、word和dword,這幾個存儲單位稍後我們會使用到。

1.2.2 數值的表示範圍
在計算機中存儲數值時,也是要依據前面介紹過的數據寬度進行存儲的,那麽在存儲數據時由於存儲數據的寬度限制,數值的表示也是有範圍限制的。那麽byte、word和dword能存儲多少數據呢?我們先來計算一下,如果按位存儲的話,能存儲多少個數據,再分別來計算以上三種單位能夠存儲的數值的範圍。

計算機使用二進制進行數據存儲時,一位二進制最多能表示幾個數呢?因為是二進制數,只存在0和1兩個數,所以一位二進制數最多能表示兩個數,分別是0和1。那麽,兩位二進制最多能表示幾個數呢?因為一位二進制數能表示兩個數,所以兩位二進制數則能表示2的2次方個數,即4個數,分別是0、1、10、11。進一步地,三位二進制數能表示的就是2的3次方個數,即8個數,分別是0、1、10、11、100、101、110、111。

上面的過程可以整理成表1-3。
技術分享圖片
2的8次方是256,為什麽數值只有0~255個呢?因為計算機計數是從0開始,從0到255同樣是256個數,這裏的2的8次方表示能夠表示數值的個數,而不是能夠表示數值的最大的數。

1.2.3 字節序
字節序也稱為字節順序,在計算機中對數值的存儲有一定的標準,而該標準隨著系統架構的不同而不同。了解字節存儲順序對於逆向工程是一項基礎知識,在動態分析程序的時候,往往需要觀察內存數據的變化情況,這就需要我們在掌握數據的存儲寬度、範圍之後,進一步了解字節順序。

通常情況下,數值在內存中存儲的方式有兩種,一種是大尾方式,另一種是小尾方式。關於字節序的知識,通過一個簡單的例子就可以掌握。

比如有0x01020304(C語言中對十六進制數的表示方式)這樣一個數值,如果用大尾方式存儲,其存儲方式為01 02 03 04,而用小尾方式進行存儲則是04 03 02 01,用更直觀的方式展示其區別,如表1-5所列。
表1-5  字節順序對比表
技術分享圖片
從兩個地址列可以看出,地址的值都是一定的,沒有變化,而數據的存儲順序卻是不相同的。從表中可以得到如下結論。

大尾存儲方式:內存高位地址存放數據低位字節數據,內存低位地址存放數據高位字節數據;

小尾存儲方式:內存高位地址存放數據高位字節數據,內存低位地址存放數據低位字節數據。

通常情況下,Windows操作系統兼容的CPU為小尾存儲方式,而Unix操作系統兼容的CPU多為大尾存儲方式。在網絡中傳輸的數據的字節順序使用的是大尾存儲方式。

1.2.4 ASCII碼
計算機智能存儲二進制數據,那麽計算機是如何存儲字符的呢?為了存儲字符,計算機必須支持特定的字符集,字符集的作用是將字符映射為整數。早期字符集僅僅使用8個二進制數據位進行存儲,即ASCII碼。後來,由於全世界語言的種類繁多,又產生了新的字符集Unicode字符編碼。

ASCII碼是美國標準信息交換碼的字母縮寫,在ASCII字符集中,每個字符由唯一的7位整數表示。ASCII碼僅使用了每個字節的低7位,最高位被不同計算機用來創建私有字符集。由於標準ASCII碼僅使用7位,因此十進制表示範圍是0~127共128個字符。

在編程與逆向中都會用到ASCII碼,因此有必要記住常用的ASCII字符對應的十六進制和十進制數。常用的ASCII字符如表1-6所列。

表1-6  常用ASCII碼表

技術分享圖片技術分享圖片
表1-6是經常使用到的ASCII字符,這些字符是經常會見到和用到的,希望讀者能將其保存,以便使用之時可以快速查閱。

Unicode編碼是為了使字符編碼更進一步符合國際化而進行的擴展,Unicode使用一個字(也就是兩個字節,即16位)來表示一個字符。這裏不做過多的介紹。

1.3 在OD中查看數據

在逆向分析中,調試工具可以說是非常重要的。調試器能夠跟蹤一個進程的運行時狀態,在逆向分析中稱為動態分析工具。動態調試會用在很多方面,比如漏洞的挖掘、遊戲外掛的分析、軟件加密解密等方面。本節介紹應用層下最流行的調試工具OllyDbg。
OllyDbg簡稱OD,是一款具有可視化界面的運行在應用層的32位的反匯編逆向調試分析工具。OD是所有進行逆向分析人員都離不開的工具。它的流行,主要原因是操作簡單、參考文檔豐富、支持插件功能等。

熟悉OD

OD的操作非常簡單,但是由於逆向是一門實戰性和綜合性非常強的技術,因此要真正熟練掌握OD的使用卻並不是容易的事,單憑操作而言看似沒有太多的技術含量,但是其真正的精髓在於配合逆向的思路來達到逆向者的目的。

1.OD的選型
為什麽先介紹OD的選型,而不直接開始介紹OD的使用呢?OD的主流版本是1.10和待崛起的2.0。雖然它的主流版本是1.10,但是它仍然存在很多修改版。所謂修改版,就是由用戶自己對OD進行修改而產生的,類似於病毒的免殺。OD雖然是動態調試工具,但是由於其強大的功能經常被很多人用在軟件破解等方面,導致很多作者的心血付諸東流。軟件的作者為了防止軟件被OD調試,加入了很多專門針對OD進行調試的反調試功能來保護自
己的軟件不被調試,從而不被破解;而破解者為了能夠繼續使用OD來破解軟件,則不得不對OD進行修改,從而達到反反調試的效果。

調試、反調試、反反調試,對於新接觸調試的愛好者來說容易混淆。簡單來說,反調試是阻止使用OD進行調試,而反反調試是突破反調試繼續進行調試。OD的修改版本之所以很多,目的就是為了能夠更好地突破軟件的反調試功能。

因此,如果從學習的角度來講,建議選擇原版的OD進行使用。在使用的過程中,除了會掌握很多調試技巧外,還會學到很多反調試的技巧,從而掌握反反調試的技巧。如果在實際的應用中,則可以直接使用修改版的OD,避免OD被軟件反調試,從而提高逆向調試分析的速度。

2.熟悉OD主界面

OD的發行是一個壓縮包,解壓即可運行使用,運行OD解壓目錄總的ollydbg.exe程序,就會出現一個分布恰當、有菜單有面板和能輸入命令的看著很強大的軟件窗口,如圖1-4所示。

在圖1-4的OD調試主窗口中的工作區大致可以分為6個部分,按照從左往右、從上往下,這6部分分別是反匯編窗口、信息提示窗口、數據窗口、寄存器窗口、棧窗口和命令窗口。下面分別介紹各個窗口的用法。

反匯編窗口:該窗口用於顯示反匯編代碼,調試分析程序主要在這個窗口中進行,這也是進行調試分析的主要工作窗口。

信息提示窗口:該窗口用於顯示與反匯編窗口中上下文環境相關的內存、寄存器或跳轉來源、調用來源等信息。

數據窗口:該窗口用於以多種格式顯示內存中的內容,可使用的格式有Hex、文本、短型、長型、浮點、地址和反匯編等。

寄存器窗口:該窗口用於顯示各個寄存器的內容,包括前面介紹的通用寄存器、段寄存器、標誌寄存器、浮點寄存器。另外,還可以在寄存器窗口中的右鍵菜單選擇顯示MMX寄存器、3DNow!寄存器和調試寄存器等。

棧窗口:該窗口用於顯示棧內容、棧幀,即ESP或EBP寄存器指向的地址部分。

命令窗口:該窗口用於輸入命令來簡化調試分析的工作,該窗口並非基本窗口,而是由OD的插件提供的功能,由於幾乎所有的OD使用者都會使用該插件,因此有必要把它也列入主窗口中。
技術分享圖片
圖1-4 OD調試主窗口

3.在數據窗口中查看數據
前面已經介紹,OD是一款應用層下的調試工具,它除了可以進行軟件的調試以外,還可以幫助我們學習前面介紹的數據寬度、進制轉換等知識,而且能夠幫助我們學習匯編語言。本節主要介紹通過OD的數據窗口來觀察數據寬度。

為了能夠直觀地觀察內存中的數據,我們通過RadAsm創建一個沒有資源的匯編工程,然後編寫一段自己的匯編代碼,代碼如下:
技術分享圖片
在上面的代碼中,定義了10個全局變量。首先,var1、var2和var3分別定義了dword類型的3個變量,其中var1的值是十六進制的12h,var2的值是十進制的12,var3的值是2進制的11b。b1到b4四個變量是字節類型的,w1和w2兩個變量是字類型的,d變量是dword類型的。

這10個全局變量就是我們要考察的關鍵。在RadAsm中進行編譯連接後,直接按下Ctrl + D這個快捷鍵,即可在RadAsm安裝時自帶的OD中打開。在OD調試器中打開該程序後,觀察它的數據窗口(如圖1-5所示)。

技術分享圖片
圖1-5 數據窗口中查看變量

在圖1-5中,數據窗口一共有3列,分別是地址列、HEX數據列和ASCII列。這3個列,可以通過單擊鼠標右鍵來改變現實方式和顯示的列數。在地址00403000處開始的4個字節12 00 00 00是十六進制的12,也就是在匯編代碼中定義的var1;在地址00403004處的4個字節0C 00 00 00是十六進制0C,也就是在匯編代碼中定義的var2,var2變量定義的值是十進制的12,也就是十六進制的0C;在地址00403008處的4個字節03 00 00 00是十六進制的03,也就是在匯編代碼中定義的var3,var3變量定義的值是2進制的11,也就是十六進制的03。

這3個變量在我們定義的時候都是以dd進行的,都是dword類型的變量,分別各占用4字節,因此在內存中,前3個變量分別是12 00 00 00、0C 00 00 00和03 00 00 00。

在地址0040300C處的值是11 22 33 44,這4個值分別是我們定義b1、b2、b3和b4 4個字節型的變量,這4變量按照內存由低到高的順序顯示分別是11、22、33、44。

在地址00403010處顯示的值是66 55 88 77,這4個值分別對應我們定義的w1和w2兩個字型變量,但是我們定義的變量w1的值是5566h,w2的值是7788h,在內存中為何顯示的是6655和8877呢?這就是我們提到過的字節順序的問題。我們的主機采用的是小尾方式存儲的數據,也就是數據的低位存放在內存的低地址中,數據的高位存放在內存的高地址中,因此在地址00403020中存放的是5566H的低位數據66,在地址00403021中存放的是5566H的高位數據55,在內存看時,順序是相反的。

在地址00403014處存放的是78 56 34 12,這是我們定義的最後一個變量d,它也是按照小尾方式存儲在內存中的。因此,在查看內存時順序也是反的。

OD提供了多種查看內存數據的方式,通過在數據窗口中單擊鼠標右鍵,會彈出如圖1-6所示菜單。

當在數據窗口中選擇數據時,右鍵的菜單提供編輯、賦值、查找、斷點功能,如圖1-7所示。
技術分享圖片
圖1-6 查看數據方式的菜單選項
技術分享圖片
圖1-7 OD中對數據操作的菜單

4.通過命令窗口改變數據窗口顯示方式
在圖1-4中的最下方可以看到有一個輸入命令的編輯框,在此處可以輸入OD的相關命令以提高調試的速度。本小節就介紹如果通過命令窗口來改變數據窗口的顯示方式。

在上面代碼中定義變量時,使用了db、dw和dd三種類型,在OD的命令窗口中也同樣可以使用者3個命令,其格式分別如表1-7所列。

表1-7  命令窗口改變數據顯示命令格式
技術分享圖片
將表1-7中的命令在命令窗口中進行輸入,數據窗口的變化和數值顯示的變化分別如圖1-8、圖1-9和圖1-10所示。技術分享圖片
圖1-8 dd命令顯示的數據窗口
技術分享圖片
圖1-9 dw命令顯示的數據窗口
技術分享圖片
圖1-10 db命令顯示的數據窗口

從圖中可以看出不同方式下數據窗口顯示的樣式,但是無論使用哪種方式顯示數據,地址列總是會顯示在最前面的,只要我們知道數據的地址,就可以直接在命令窗口中輸入顯示數據的格式來查看指定內存中的數據。

1.4 編程判斷主機字符序

編程判斷主機字節序是更進一步掌握字節序的方式,本小節給出兩種對主機的字節序進行判斷的方式。
1.4.1 字節序相關函數
在TCP/IP網絡編程中會涉及關於字節序的函數,TCP/IP協議中傳遞數據是以網絡字節序進行傳輸的,網絡字節序是指網絡傳輸相關協議所規定的字節傳輸的順序,TCP/IP協議所使用的網絡字節序與大尾方式相同。而主機字節序包含大尾方式與小尾方式,因此在進行網絡傳輸時會進行相應的判斷,如果主機字節序是大尾方式則無需進行轉換即可傳輸,如果主機字節序是小尾方式則需要轉換成網絡字節序(也就是轉換成大尾方式)然後進行傳輸。

常用的字節序涉及的函數有如下幾個:
技術分享圖片
在這4個函數中,前兩個是將主機字節序轉換成網絡字節序,後兩個是將網絡字節序轉換為主機字節序。關於更多的字節序的函數可參考MSDN。

1.4.2 編程判斷主機字節序
“編程判斷主機字節序”是很多殺毒軟件公司或者安全開發職位的一道面試題,因為這個題目比較基礎。通過前面的知識,相信讀者能夠很容易地實現該程序。這裏給出筆者自己對於該題目的實現方法。筆者認為,完成該題目有兩種方法,第一種方法是“取值比較法”,第二種方法是“直接轉換比較法”。

1.取值比較法
所謂取值比較法,是首先定義一個4字節的十六進制數。因為使用調試器查看內存最直觀的就是十六進制,所以定義十六進制數是一個操作起來比較直觀的方法。而後通過指針方式取出這個十六進制數在“內存”中的某一個字節,最後與實際數值中相對應的數進行比較。
由於字節序的原因,內存中的某字節與實際數值中對應的字節可能不相同,這樣就可以確定字節序了。

代碼如下:
技術分享圖片
以上代碼中,定義了0x01020304這個十六進制數,其在小尾方式內存中的存儲順序為04 03 02 01。取(BYTE )&dwSmallNum內存中的低地址位的值,如果是小尾方式的話,那麽低地址存儲的值為0x04;如果是大尾方式的話,則低地址存儲的值為0x01。
2.直接轉換比較法
所謂直接轉換比較法,是利用字節序轉換函數將所定義的值進行轉換,然後用轉換後的值與原值進行比較。如果原值與轉換後的值相同,說明是大尾方式,否則為小尾方式。

代碼如下:
技術分享圖片
這種方式比較直接,其前提是網絡字節序是固定的,就是大尾方式。因為是比較,所以就要有一個參照物。如果原值轉換後的結果與原值相同,就說明該主機是大尾方式存儲,反之則是小尾方式。

1.5 總結

本文對內存中存儲基礎數據的方式進行了闡述,並且在最後部分介紹了如何使用OD調試器來查看內存中的數據。在學習編程時,都會從數據類型開始介紹,不同的數據類型都是以二進制的方式存儲在內存中的,只是它們存儲的方式不同,或者是存儲的寬度不同。在我們學習逆向時,也首先講解了數據的基礎及數據的存儲方式。
本文摘自《逆向分析實戰》
技術分享圖片
也許你想知道什麽是軟件逆向,也許你已經聽說過軟件逆向,從而想要學習軟件逆向。不管你抱著什麽目的翻開本書,筆者還是在你閱讀本書之前,先來說一些軟件逆向的知識!

什麽是“軟件逆向工程”
術語“逆向工程”源自硬件領域,在軟件領域目前還沒有明確的定義。就筆者個人的理解簡單來說,軟件逆向是通過觀察分析軟件或程序的行為、數據和代碼等,來還原其設計實現,或者推導出更高抽象層次的表示。

軟件工程與軟件逆向工程的區別
對於軟件工程而言,軟件的設計講究封裝,將各個模塊進行封裝,將具體的實現進行隱藏,只暴露一個接口給使用者。對於模塊的使用者而言,封裝好的模塊相當於一個“黑盒子”,使用者使用“盒子”時,無需關心“盒子”的內部實現,只需要按照模塊預留的接口進行使用即可。

軟件逆向工程對於軟件工程而言,卻是正好相反的。對軟件進行逆向工程時要查看軟件的行為,即軟件的輸入與輸出的情況;要查看軟件的文件列表,即軟件使用了哪些動態鏈接庫(哪些動態連接庫是作者編寫的,哪些動態連接庫是系統提供的),有哪些配置文件,甚至還要通過一系列的工具查看軟件的文件結構、反匯編代碼等。

對比軟件工程與軟件逆向工程可以發現,軟件工程是在封裝、實現一個具備某種功能的“黑盒子”,而軟件逆向工程則是在分析“黑盒子”並嘗試還原封裝的實現與設計。後者對於前者而言是一個相反的過程,因此稱為“軟件逆向工程”。

學習軟件逆向工程與軟件工程的區別
對於軟件逆向工程而言,學習逆向知識,除了要學習逆向知識本身外,還需要掌握各種不同的逆向工具,或者說逆向知識中重要的一個環節就是逆向工具的使用。對於軟件開發而言,軟件開發工具在軟件開發中所占據的位置遠遠達不到逆向工具在逆向領域中的位置。因此,讀者在學習編程時可能更註重的是編程語言本身而不是工具,但是在學習逆向時,逆向知識是不可能拋開逆向工具而獨立進行學習的。

本書的主要內容
本書全面講解了軟件逆向工程的知識,即包括主流的技術,如加殼與脫殼、匯編、數據的存儲等;也有實用的工具,如主流的調試工具OllyDbg、PE工具、加殼與脫殼工具、十六進制編輯與反編譯工具、IDA與逆向、逆向工具實現等。

延伸推薦
技術分享圖片
點擊關鍵詞閱讀更多新書:
Python|機器學習|Kotlin|Java|移動開發|機器人|有獎活動|Web前端|書單
技術分享圖片
在“異步圖書”後臺回復“關註”,即可免費獲得2000門在線視頻課程;推薦朋友關註根據提示獲取贈書鏈接,免費得異步圖書一本。趕緊來參加哦!
點擊閱讀原文,查看本書更多信息
掃一掃上方二維碼,回復“關註”參與活動!
技術分享圖片

《逆向分析實戰》數據的存儲及表示形式