C程式設計語言的彙編解釋 第七章 人肉編譯器和反編譯器

挑戰極限吧少年!

7。1 人肉編譯器

先來個簡單的例子,大家可以嘗試翻譯一下大概的彙編:

#include

main() {

int a, b;

a = 1;

b = a + 1;

}

其彙編大概是這樣的:

* 給a,b在棧上預留2個int大小的空間

* 把1放入a的棧空間

* 把a的棧空間的資料放到暫存器裡面

* 對暫存器做加1

* 把結果放入b的棧空間

再來個複雜點的例子, 大家再試試:

#include

main() {

int c;

while ((c = getchar()) != EOF) {

putchar(tolower(c));

}

}

其彙編大概是這樣的:

* 給c分配一個棧上的空間

* 立一個label在接下來的程式碼的頭部,方便跳回來

* bl getchar方法,返回值x0(w0)放入c的棧空間

* 把c的棧空間的內容撈出來跟EOF做比較

* 如果不等於則往下走,如果等於則跳到迴圈尾部

* 往下走就是bl tolower引數是c(會放到x0(w0),返回值也在x0(w0)),再bl putchar(引數是x0(w0)),跳回最開始立的label

* 迴圈尾部,結束

7。2 人肉反編譯

其實主要考猜和工具輔助,工具輔助會幫助給出更多的符號或者字面量意思,讓彙編程式碼更容易理解。隨便拉個庫看看彙編(這裡選了libobjc,有原始碼更好看):

_NXCreateHashTable:

00000001800ac010 sub sp, sp, #0x30

00000001800ac014 stp x29, x30, [sp, #0x20]

00000001800ac018 add x29, sp, #0x20

00000001800ac01c ldr q0, [x0, #0x10]

00000001800ac020 str q0, [sp, #0x10]

00000001800ac024 ldr q0, [x0]

00000001800ac028 str q0, [sp]

00000001800ac02c mov x0, sp

00000001800ac030 bl _NXCreateHashTableFromZone

00000001800ac034 ldp x29, x30, [sp, #0x20]

00000001800ac038 add sp, sp, #0x30

00000001800ac03c ret

; endp

方法名‘_NXCreateHashTable’,前面三句prologue不多說,然後從第一個引數x0的偏移0x10處取出一個q暫存器大小的值,然後存在sp的偏移0x10的位置。再從第一個引數x0中取出一個q暫存器大小的值,放到sp位置。這裡相當於把x0裡存的地址裡0x20長度的內容,複製到sp就是棧底。然後sp移到x0,作為‘_NXCreateHashTableFromZone’的引數。後面三句是epilogue和返回,不多介紹。這裡我們沒有看到對‘_NXCreateHashTableFromZone’方法可能存在的返回值x0做處理,那要不是沒有返回值,要不是返回值直接作為‘_NXCreateHashTable’的返回值了。而方法的引數去了2個q暫存器值,這裡懷疑引數是一個結構體(如果是整數型別引數,不會用到q暫存器,而從方法名判斷,引數不應該是浮點數,那麼很有可能就是結構體了)。

程式碼比較簡單,這裡就不寫人肉反彙編的結果了。

再來點複雜的:

__ZL13addNamedClassP10objc_classPKcS0_:// addNamedClass(objc_class*, char const*, objc_class*)

00000001800b44dc stp x22, x21, [sp, #-0x30]!

00000001800b44e0 stp x20, x19, [sp, #0x10]

00000001800b44e4 stp x29, x30, [sp, #0x20]

00000001800b44e8 add x29, sp, #0x20

00000001800b44ec mov x21, x2

00000001800b44f0 mov x20, x1

00000001800b44f4 mov x19, x0

00000001800b44f8 mov x0, x20

00000001800b44fc bl __ZL8getClassPKc

00000001800b4500 mov x8, x0

00000001800b4504 cbz x8, loc_1800b4530

00000001800b4508 cmp x8, x21

00000001800b450c b。eq loc_1800b4530

00000001800b4510 mov x0, x20

00000001800b4514 mov x1, x8

00000001800b4518 mov x2, x19

00000001800b451c bl _inform_duplicate

00000001800b4520 bl __ZL14nonMetaClassesv

00000001800b4524 ldr x8, [x19]

00000001800b4528 and x1, x8, #0xffffffff8

00000001800b452c b loc_1800b453c

loc_1800b4530:

00000001800b4530 adrp x8, #0x1b1f3d000

00000001800b4534 ldr x0, [x8, #0x8]

00000001800b4538 mov x1, x20

loc_1800b453c:

00000001800b453c mov x2, x19

00000001800b4540 ldp x29, x30, [sp, #0x20]

00000001800b4544 ldp x20, x19, [sp, #0x10]

00000001800b4548 ldp x22, x21, [sp], #0x30

00000001800b454c b _NXMapInsert

方法名‘__ZL13addNamedClassP10objc_classPKcS0_’,一個C++方法,方法名和引數在後面的註釋中已經明確的說明了。

前面4句prologue不多做分析。

00000001800b44ec mov x21, x2

00000001800b44f0 mov x20, x1

00000001800b44f4 mov x19, x0

分別把三個引數x0/x1/x2存入x21/x20/x19。

00000001800b44f8 mov x0, x20

00000001800b44fc bl __ZL8getClassPKc

把x20也就是第二個引數放入x0,作為‘__ZL8getClassPKc’的引數,由於第二個引數是字串,那這裡猜測它是類名。

00000001800b4500 mov x8, x0

00000001800b4504 cbz x8, loc_1800b4530

00000001800b4508 cmp x8, x21

00000001800b450c b。eq loc_1800b4530

把返回值(根據類名查到的類)放入x8,判斷x8是否為0,如果是則跳到loc_1800b4530。把x8和x21(第三個引數)對比,判斷要加的類和根據類名取出來類是不是同一個,如果是也跳到loc_1800b4530。loc_1800b4530的功能後面分析。

00000001800b4510 mov x0, x20

00000001800b4514 mov x1, x8

00000001800b4518 mov x2, x19

00000001800b451c bl _inform_duplicate

00000001800b4520 bl __ZL14nonMetaClassesv

前面兩個判斷都不成立的話,也就意味著,根據類名可以取到一個類,而我們又要新增一個同名的類,那麼要乾的事情,就是‘_inform_duplicate’(告知類重複),然後呼叫nonMetaClasses(不知道幹嘛的)。

00000001800b4524 ldr x8, [x19]

00000001800b4528 and x1, x8, #0xffffffff8

00000001800b452c b loc_1800b453c

從x19(第一個引數)中取出x8,然後and一下,跳到loc_1800b453c處。這裡objc_class取出來的x8是一個isa,‘0xffffffff8’是isa的mask,這個可以透過閱讀objc的原始碼來理解。

loc_1800b4530:

00000001800b4530 adrp x8, #0x1b1f3d000

00000001800b4534 ldr x0, [x8, #0x8]

00000001800b4538 mov x1, x20

取一個常量匯入x0(這裡是‘_gdb_objc_realized_classes’),x20放入x1(也就是__ZL13addNamedClassP10objc_classPKcS0_的第二個引數,類名)

loc_1800b453c:

00000001800b453c mov x2, x19

00000001800b4540 ldp x29, x30, [sp, #0x20]

00000001800b4544 ldp x20, x19, [sp, #0x10]

00000001800b4548 ldp x22, x21, [sp], #0x30

00000001800b454c b _NXMapInsert

下面是epilogue以及呼叫‘_NXMapInsert’的引數,把程式碼從前往後順一次可以看出‘_NXMapInsert’的引數分別是,一個常量,類名,類指標。

這裡看出了一個很懵逼的點,方法的第一個引數和第三個引數都是objc_class指標型別,而x19最終會用來註冊,而x21被用來比較類是不是存在。翻了一下老版的objc程式碼,發現方法只有兩個引數,那也就意味著新版的objc又加了一些詭異的特性,而一般情況下,第一個引數和第三個引數的值應該是相等的。

老版程式碼如下:

/***********************************************************************

* addNamedClass

* Adds name => cls to the named non-meta class map。

* Warns about duplicate class names and keeps the old mapping。

* Locking: runtimeLock must be held by the caller

**********************************************************************/

static void addNamedClass(Class cls, const char *name)

{

rwlock_assert_writing(&runtimeLock);

Class old;

if ((old = getClass(name))) {

inform_duplicate(name, old, cls);

// getNonMetaClass uses name lookups。 Classes not found by name

// lookup must be in the secondary meta->nonmeta table。

addNonMetaClass(cls);

} else {

NXMapInsert(gdb_objc_realized_classes, name, cls);

}

assert(!(cls->data()->flags & RO_META));

// wrong: constructed classes are already realized when they get here

// assert(!cls->isRealized());

}

和前面的分析基本一致。區別在於,引數少了一個,鎖沒了,‘addNonMetaClass’方法名變成了‘nonMetaClass’。