如何快速讀取Mac進程的內存映射
讀取Mac的內存映射過程
首先需要2個變數的地址:ruby_version(用來布局結構)和ruby_current_thread(用來進行堆棧跟蹤)。
由於它們就在我正在查看的Ruby二進位文件的符號表中,所以通常獲取這兩個變數的地址並不難,但是由於ASLR的存在,二進位文件會隨機載入到內存中。所以我需要做到以下幾點:
1. 在符號表中找到ruby_version的地址;
2. 找出Ruby二進位文件在內存中載入的位置(來自進程的內存映射);
3. 減去__mh_execute_header符號的值。
在Linux上,你可以通過查看/ proc / PID / maps文件來獲得內存映射,它們的組成方式是這樣的:地址範圍,許可權(例如r-xp),大小, inode編號以及一個映射到該文件的文件名。
00400000-00401000 r-xp 00000000 00:14 13644 /usr/bin/ruby1.9.1
00600000-00601000 r--p 00000000 00:14 13644 /usr/bin/ruby1.9.1
00601000-00602000 rw-p 00001000 00:14 13644 /usr/bin/ruby1.9.1
0060b000-00887000 rw-p 00000000 00:00 0 [heap]
7f1d44648000-7f1d4464a000 r-xp 00000000 00:14 14411 /usr/lib/ruby/1.9.1/x86_64-linux/enc/trans/transdb.so
7f1d4464a000-7f1d4484a000 ---p 00002000 00:14 14411 /usr/lib/ruby/1.9.1/x86_64-linux/enc/trans/transdb.so
7f1d4484a000-7f1d4484b000 r--p 00002000 00:14 14411 /usr/lib/ruby/1.9.1/x86_64-linux/enc/trans/transdb.so
7f1d4484b000-7f1d4484c000 rw-p 00003000 00:14 14411 /usr/lib/ruby/1.9.1/x86_64-linux/enc/trans/transdb.so
而在所有版本的Mac上,雖然內存映射的結構基本上是相同的,但是獲取它們會相當困難!起初我試圖在Mac上使用適用於Linux的方法(就是上文提到的/ proc文件),但花了3天時間都沒有得到。為此,我進行了以下2種嘗試:
嘗試1——使用vmmap
VMMap是一個免費的工具,可以用來分析應用程序使用虛擬和物理內存的情況。除了內存使用圖形來表示,VMMap也顯示摘要信息和詳細進程的內存映射。強大的過濾和刷新功能可以確定進程的內存使用情況和應用功能內存成本的來源。
不過,由於某種原因vmmap速度很慢,獲取一個進程的內存映射需要2秒,這可能是因為vmmap在獲取內存映射之前暫停了這個過程。
我不想直接使用vmma的原因,除了不滿意vmmap的速度以外,另外一個原因就是如果vmmap真的是在獲取內存映射之前暫停了這個過程,那這就意味著內存映射的分析過程被干擾了。
嘗試2——在Rust中重新使用vmmap
Rust是針對多核體系提出的語言,並且吸收一些其他動態語言的重要特性,比如不需要管理內存,比如不會出現Null指針等等。由於不想直接使用vmmap,所以我考慮在Rust中重新實現它的功能(至少是我需要的部分)。我在Rust中寫了一個簡單的vmmap複製功能,代碼就在main.rs。
為了做到這一點,我使用了mach crate,它具有Rust綁定功能,可以調用一些Mac內核函數。我還了解到,在Mac / BSD上有一個「埠(port)」的概念:
『埠』是一個受保護的消息隊列,用於在任務之間進行通信,任務擁有發送許可權並接收每個埠的許可權
以下是如何從一個程序獲得一個內存映射的方法,不過這個函數的介面有一點奇怪,就是你給它一個埠ID和一個地址,它會給你該地址之後的第一個內存映射。基本上這個函數只是將mach_vm_region函數從Mach微內核中封裝起來(所有Mach函數的頭文件都在/usr/include/mach/*.h中)。
我已經注釋了一些代碼,它使用https://github.com/andrewdavidmackenzie/libproc-rs crate作為regionfilename函數(它給出了與內存映射關聯的庫的文件名)。我就不得不在github master上使用該版本的crate,因為其發布的版本有一個Use-After-Free(UAF)漏洞。
fn mach_vm_region(target_task: mach_port_name_t, mut address: mach_vm_address_t) -> Option {
let mut count = mem::size_of::() as mach_msg_type_number_t;
let mut object_name: mach_port_t = 0;
// we need to create new `size` and `info` structs for the function we call to read the data
// into
let mut size = unsafe { mem::zeroed::() };
let mut info = unsafe { mem::zeroed::() };
let result = unsafe {
// Call the underlying Mach function
mach::vm::mach_vm_region(
target_task as vm_task_entry_t,
&mut address,
&mut size,
VM_REGION_BASIC_INFO,
&mut info as *mut vm_region_basic_info_data_t as vm_region_info_t,
&mut count,
&mut object_name,
)
};
if result != KERN_SUCCESS {
return None;
}
// this uses
let filename = match regionfilename(41000, address) {
Ok(x) => Some(x),
_ => None,
};
Some(Region {
size: size,
info: info,
address: address,
count: count,
filename: filename,
})
}
以上是我編寫的Rust程序,比vmmap要快很多!整個內存映射的時間要在80毫秒左右完成,比vmmap快大約15倍。
不過到現在為止,我的Rust vmmap複製還存在一個主要問題,就是它實際上只是給我一些現在我的進程中的內存映射。而對於任何動態鏈接庫(包括一個Ruby庫以及我需要的地址和文件名),它們被存儲在一個叫做「dyld_shared_cache」的地方。
在下面的鏈接中有一堆關於這個「dyld」的代碼,我打算試著利用一下。
用於從Mac進程讀取內存映射的有用資源
以下是我在Mac上閱讀內存映射時發現的4個最有用的資源:
1.來自OS X內部的vmmap.c源代碼;
2.「使用Mach-O二進位文件和dyld」,用於在Mac進程中查找共享庫的地址;
3.來自Chromium的dynamic_images.cc,用它讀取Mac進程中的信息共享庫;
4.psutil的OS X C代碼,ppsutil是一個跨平台庫,能夠輕鬆實現獲取系統運行的進程和系統利用率(CPU,內存,磁碟,網路等)信息,主要應用於系統監控,分析和限制系統資源及進程的管理,它實現了同等命令行工具提供的功能,所以它的源代碼有助於了解Mac內部的結構
※WhatsApp爆嚴重漏洞,黑客可監控群聊信息
※雲高防之另類玩法:遊戲盾
TAG:嘶吼RoarTalk |