繼續蹂躪linux

2021-08-25 01:31:42 字數 3273 閱讀 1025

話說linux的核心空間不能訪問使用者空間,這是真的嗎?前面說過很多次,沒有什麼是絕對不能的,一定要區分清楚什麼是不允許什麼是不可能,使用者空間不能訪問核心空間是硬體規定和實現的,但是反過來,核心空間不能訪問使用者空間卻只是linux的一種約定,一種規定或者多多少少有點設計上的約定,核心空間擁有至高無上的權力為何要限制它呢?其實不是限制而是這樣規定使得實現起來更加方便和有條理,仔細想想,核心空間被所有的程序共享,你讓核心訪問使用者空間, 那麼訪問誰的使用者空間呢?所以要想實現核心訪問使用者空間,必須在訪問時指明訪問哪個程序的使用者空間,而且還會涉及缺頁處理的問題,畢竟核心訪問的使用者空間對應的程序可能不是當前程序,之後的複雜性就不用多說了吧。另外的原因就是設計上的規定了,設計上有個「流」的概念,要想使設計靈活,模組化,或者說低耦 合,那就得使對應的流單向化,就是所謂的單向依賴,tcp/ip模型和osi模型就是這麼設計的,上層使用下層的服務,下層絕對不依賴上層,就是不會「調 用」上層,在作業系統的設計中當然也是這樣,核心為使用者空間服務並且不依賴使用者空間,就是不呼叫使用者空間。

事實上就真的不能呼叫使用者空間嗎?肯定不是的,就像核心不允許在中斷中睡眠,但是你真的在中斷處理函式裡面呼叫了個schedule並不會使得核心馬上崩 潰,只是核心後面的行為變得不能保證了。我們還是用核心模組來實現乙個核心訪問使用者空間的例子,訪問使用者空間的資料在《char *和char陣列的區別(深拷貝和淺拷貝的觀點)以及核心訪問使用者空間》裡面已經說過了,現在要談的的是核心呼叫使用者空間的函式,這個理解起來要明白兩個 東西,乙個是程式的text段,乙個程式的棧,乙個函

數的執行關聯到了text段和棧,所謂text段其實也是一種資料,核心呼叫使用者空間的函式實際上就是訪問了使用者空間的資料,核心從使用者空間的text段取到被調函式的指令,這就訪問了使用者空間資料,然後執行的場所呢?當然是棧了,有函式呼叫就需要棧,可是這個棧不是那個棧,核心呼叫使用者空間的函式時所用的棧是核心棧,其實也是當前程序的當前棧。這就是說,乙個函式執行要弄明白兩點,一是到** 取指令(一般為text段,當然在緩衝區溢位攻擊中可能是棧),二是在**執行(一般是

棧)。因此核心呼叫使用者空間的函式就是到使用者空間的text段取指令,在核心棧執行,如此而已,還有乙個問題,就是如何得到使用者空間的函式位址,當然可以通過特徵掃,也可以硬賦值,我這裡為了簡單採用了後者,先看看使用者空間程式:

void userfunc( int i )

pringf("value:%d/n",i);

int main()

void *mod;

unsigned int sz;

int ret;

mod = readfile("test.ko", &sz);//將模組讀到mod中,此函式略

ret = init_module(module, size, "");//用系統呼叫載入模組

free(mod);

return 0;

static __init int test_init(void)

int (*p)(int i);

p = 0x80486b4;

(*p)(12);

return 0;

編 譯此模組為test.ko(若有不明請參閱《linux核心相關的兩個問題》)。然後將test.ko複製到前面應用程式testcall相同的目錄,執 行testcall,猜猜得到了什麼?如果說核心不能呼叫使用者函式的話,系統應該panic,如果真的能呼叫的話,應該列印出value:12,可惜,內 核既沒有panic也沒有列印value:12,而是列印出了value:-795975574,看來還是呼叫了使用者空間的函式,只是引數傳遞有問題,還是那句話:彙編**永遠都不會欺騙你。這其實有點哲學意義,越高層越不能相信,最低層的往往最可信,這就是「微服私訪」的意義,寧信老百姓,不信當官的。 我們還是用objdump看看userfunc吧:

80486b4 :

push %ebp

mov %esp,%ebp

...

pushl $0x8(%ebp)

call printf

然後再用objdump看看test.ko裡面是怎麼設定引數的:

00000000 :

...

mov $0x80486b4,%edx

mov $0xc,%eax

call *%edx

...

看 到了吧,直接用暫存器eax傳遞引數,如果你不了解編譯器還可以設定引數傳遞的方式,那麼為了得到正確結果有兩種方式,乙個直接用彙編寫userfunc 函式,而是保留c語言的userfunc,但是在內部想辦法得到eax的值,如果你根本不會彙編(或者內嵌彙編),那麼得到eax的唯一方式就是利用函式的返回值,就是再加乙個函式:

int help()

然後更改userfunc:

void userfunc( int i )

help 函式什麼也沒有做,只是返回,用objdump確保它內部沒有觸及eax的話,這次列印的一定是value:12,因為help函式直接返回了乙個值,該 值在eax中,但是help什麼也沒有做,因此它就直接將eax返回了,試試看,果然結果正確(估計再高版本的編譯器會認為help的寫法不對,但最起碼 在我的gcc上它是正確的,想搞攻擊的朋友一定不能放過任何蛛絲馬跡)。但是這畢竟不是權宜之計,如果傳遞第二個,第三個...引數的話這種方法就不奏效 了,當然直接用彙編寫函式什麼時候都奏效。那該怎麼辦呢?想想gcc的特性,它專門提供了設定引數傳遞方式的介面,比如用暫存器傳,用堆疊傳等等,在利用 這些特性之前先想明白為何核心用暫存器傳參,實際上核心棧是很寶貴的,linux中只有4k不到,不像使用者空間從3g的邊界向下擴充套件,如果核心棧中塞滿了 引數資訊,很容易就會棧溢位,另外乙個原因就是速度,核心絕對不想讓程序在核心態時間過久,要知道push和pop是操作記憶體的,是相當耗時的操作,為了 速度也要盡量用暫存器傳參。

明白了一切後修改的方式有兩種,一種是該核心模組,另一種是該使用者函式,先看看改核心模組吧,其實很簡單,就是將

int (*p)(int i);

改為:

int __attribute__((regparm(0))) (*p)(int i);

__attribute__((regparm(0))) 告訴編譯器不要用暫存器傳遞引數而是用堆疊,其實核心專門提供了乙個巨集asmlinkage來標誌這個特性,如此改變後,結果就正確了;另外改使用者函式的 方式與此類似,就是告訴編譯器用暫存器傳遞引數而不是用堆疊,具體改法就是核心模組不變在使用者函式前加上 __attribute__((regparm(n))),n為使用暫存器的個數,不同平台規定不同,比如i386上n為3表示可以用eax,edx 和ecx傳遞引數,這樣結果也是正確的。

經過上述對linux的蹂躪可以明白核心真的就是權力很大,完全可以呼叫使用者的函式,但是呼叫之前必須自己心裡清楚當前程序是誰以及那個函式確實存在

繼續蹂躪linux

話說linux的核心空間不能訪問使用者空間,這是真的嗎?前面說過很多次,沒有什麼是絕對不能的,一定要區分清楚什麼是不允許什麼是不可能,使用者空間不能訪問核心空間是硬體規定和實現的,但是反過來,核心空間不能訪問使用者空間卻只是linux的一種約定,一種規定或者多多少少有點設計上的約定,核心空間擁有至高...

關於個人意志遭到蹂躪

省公司運動會滁州選拔賽 本不打算參加,但最近讀了 跑步聖經 有點小衝動,就報了1.5km,好歹中學的冬季越野跑上我也是一員健將。長久上班的人,體力真是不敢恭維。跑到最後只剩三個人在爭奪。一直領跑的我在 200m處被另外兩人趕超,甩開,隨後位居第二的人在 100m處兩個趔趄,趴倒。tc人包攬了兩個出線...

來自窮逼對HttpCanary的蹂躪

部落格 httpcanary一直強制公升級,國內又禁止fq,我只能如此了 主要是,我經常用到httpcanary,目前最新版的是3.3.6,而我用的是3.1.5,一直提示讓我更新,不更新不給用。我嘗試反編譯了,但是人家有360加固,沒得辦法。最後,實在受不了,就想到android能不能像pc一樣,設...