[C in ASM(ARM64)]第七章 人肉編譯器和反編譯器
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’。