Android ptrace函數(shù)的實現(xiàn)

首先看sys/ptrace.h

/bionic/libc/include/sys/ptrace.h

我們在調(diào)用的時候使用的是PTRACE_的導(dǎo)出符號令漂,glibc也導(dǎo)出了PT_開頭的符號。PTRACE_開頭的符號定義在/bionic/libc/kernel/uapi/linux/ptrace.h中


都是int型的數(shù)字帮寻,回到ptrace函數(shù)的定義悍赢,頭文件中寫的是一個不定參數(shù)的形式
long ptrace(int, ...);


繼續(xù)跟進__ptrace函數(shù)



__ptrace函數(shù)使用匯編實現(xiàn)援制,其中____NR_ptrace是系統(tǒng)調(diào)用號戏挡,根據(jù)調(diào)用號進入kernel層即linux內(nèi)核的邏輯。此處__NR_ptrace的調(diào)用號為26晨仑,swi #0 為產(chǎn)生中斷褐墅,切換到內(nèi)核模式


內(nèi)核的實現(xiàn)是在/kernel/ptrace.c中
PTRACE_TRACEME調(diào)用

   static int ptrace_traceme(void)
382{
383 int ret = -EPERM;
384
385 write_lock_irq(&tasklist_lock);
386 /* Are we already being traced? */
387 if (!current->ptrace) {
388     ret = security_ptrace_traceme(current->parent);
389     /*
390      * Check PF_EXITING to ensure ->real_parent has not passed
391      * exit_ptrace(). Otherwise we don't report the error but
392      * pretend ->real_parent untraces us right after return.
393      */
394     if (!ret && !(current->real_parent->flags & PF_EXITING)) {
395         current->ptrace = PT_PTRACED;
396         __ptrace_link(current, current->real_parent);
397     }
398 }
399 write_unlock_irq(&tasklist_lock);
400
401 return ret;
402}

__ptrace_link函數(shù)

void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
38{
39  BUG_ON(!list_empty(&child->ptrace_entry));
40  list_add(&child->ptrace_entry, &new_parent->ptraced);
41  child->parent = new_parent;
42}

函數(shù)首先判斷當(dāng)前進程有沒有正在被traced拆檬,如果為否進行一系列安全檢查將當(dāng)前進程設(shè)置為PT_PTRACED,并調(diào)用__ptrace_link函數(shù)將子進程鏈接到父進程的ptrace鏈表中妥凳。之后再進行系統(tǒng)調(diào)用的時候竟贯,內(nèi)核就會判斷當(dāng)前進程是否被設(shè)置為traced,如果是內(nèi)核將向該進程發(fā)送SIGTRAP信號逝钥。該信號將導(dǎo)致當(dāng)前進程停止澄耍。并將控制權(quán)交給父進程。

PTRACE_ATTACH調(diào)用ptrace_attach

static int ptrace_attach(struct task_struct *task, long request,
274          unsigned long addr,
275          unsigned long flags)
276{
277 bool seize = (request == PTRACE_SEIZE);
278 int retval;
279
280 retval = -EIO;
281 if (seize) {
282     if (addr != 0)
283         goto out;
284     if (flags & ~(unsigned long)PTRACE_O_MASK)
285         goto out;
286     flags = PT_PTRACED | PT_SEIZED | (flags << PT_OPT_FLAG_SHIFT);
287 } else {
288     flags = PT_PTRACED;
289 }
290
291 audit_ptrace(task);
292
293 retval = -EPERM;
294 if (unlikely(task->flags & PF_KTHREAD))
295     goto out;
296 if (same_thread_group(task, current))
297     goto out;
298
299 /*
300  * Protect exec's credential calculations against our interference;
301  * SUID, SGID and LSM creds get determined differently
302  * under ptrace.
303  */
304 retval = -ERESTARTNOINTR;
305 if (mutex_lock_interruptible(&task->signal->cred_guard_mutex))
306     goto out;
307
308 task_lock(task);
309 retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
310 task_unlock(task);
311 if (retval)
312     goto unlock_creds;
313
314 write_lock_irq(&tasklist_lock);
315 retval = -EPERM;
316 if (unlikely(task->exit_state))
317     goto unlock_tasklist;
318 if (task->ptrace)
319     goto unlock_tasklist;
320
321 if (seize)
322     flags |= PT_SEIZED;
323 rcu_read_lock();
324 if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
325     flags |= PT_PTRACE_CAP;
326 rcu_read_unlock();
327 task->ptrace = flags;
328
329 __ptrace_link(task, current);
330
331 /* SEIZE doesn't trap tracee on attach */
332 if (!seize)
333     send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
334
335 spin_lock(&task->sighand->siglock);
336
337 /*
338  * If the task is already STOPPED, set JOBCTL_TRAP_STOP and
339  * TRAPPING, and kick it so that it transits to TRACED.  TRAPPING
340  * will be cleared if the child completes the transition or any
341  * event which clears the group stop states happens.  We'll wait
342  * for the transition to complete before returning from this
343  * function.
344  *
345  * This hides STOPPED -> RUNNING -> TRACED transition from the
346  * attaching thread but a different thread in the same group can
347  * still observe the transient RUNNING state.  IOW, if another
348  * thread's WNOHANG wait(2) on the stopped tracee races against
349  * ATTACH, the wait(2) may fail due to the transient RUNNING.
350  *
351  * The following task_is_stopped() test is safe as both transitions
352  * in and out of STOPPED are protected by siglock.
353  */
354 if (task_is_stopped(task) &&
355     task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
356     signal_wake_up_state(task, __TASK_STOPPED);
357
358 spin_unlock(&task->sighand->siglock);
359
360 retval = 0;
361 unlock_tasklist:
362 write_unlock_irq(&tasklist_lock);
363 unlock_creds:
364 mutex_unlock(&task->signal->cred_guard_mutex);
365 out:
366 if (!retval) {
367     wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT,
368             TASK_UNINTERRUPTIBLE);
369     proc_ptrace_connector(task, PTRACE_ATTACH);
370 }
371
372 return retval;
373}

整個函數(shù)的流程總結(jié)如下(分析來自linux3.5.4 ptrace源碼分析二 新增部分修改)
1. 判斷請求是PTRACE_SEIZE還是PTRACE_ATTACH晌缘,如果ptrace請求為PTRACE_SEIZE,則檢查其參數(shù)是否正確痢站,參數(shù)有誤則退出
2. 判斷task進程是否為kernel thread(PF_KTHREAD)磷箕,調(diào)用same_thread_group(task,current),判斷task是否和current進程在同一個線程組阵难,查看current進程是否有權(quán)限追蹤task進程岳枷,不符合要求則退出
3. 設(shè)置子進程task->ptrace = PT_TRACED,被跟蹤狀態(tài)呜叫,如果當(dāng)前進程擁有CAP_SYS_PTRACED空繁,設(shè)置task->ptrace |= PT_TRACE_CAP
4. 調(diào)用__ptrace_link(task, current),將task->ptrace_entry鏈接到current->ptraced鏈表中,并設(shè)置當(dāng)前進程為被跟蹤進程的新的父進程朱庆。
5. 如果是PTRACE_ATTACH請求(PTRACE_SEIZE請求不會停止被追蹤進程)盛泡,則調(diào)用send_sig_info(SIGSTOP,SEND_SIG_FORCED, task);發(fā)送SIGSTOP信號,中止task運行娱颊,設(shè)置task->state為TASK_STOPPED
6. 等待task->jobctl的JOBCTL_TRAPPING_BIT位被清零傲诵,阻塞時進程狀態(tài)被設(shè)置為TASK_UNINTERRUPTIBLE并引發(fā)進程調(diào)度
PTRACE_ATTACH處理的方式與PTRACE_TRACEME處理的方式不同,PTRACE_ATTACH會使父進程向子進程發(fā)送SIGTRAP信號箱硕,如果子進程停止拴竹,父進程的wait操作則會被喚醒,從而成功attach剧罩。

而PTRACE_TRACEME只是表明該進程(child)想被trace的意愿栓拜。如果一個進程調(diào)用了PTRACE_TRACEME,那么該進程處理信號的方式將會變得不同惠昔。比如:如果一個進程正在運行幕与,此時輸入ctrl+c(SIGINT),則該進程直接退出。但是舰罚,如果該進程中有ptrace(PTRACE_TRACEME,0纽门,NULL,NULL)。即該進程主動要求被跟蹤营罢,那么赏陵,當(dāng)輸入CTRL+C時饼齿,該進程將會處于stopped的狀態(tài)。

PTRACE_PEEKDATA/PEEKTEXT
PTRACE_POKEDATA/POKETEXT

int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
1076                unsigned long data)
1077 {
1078    unsigned long tmp;
1079    int copied;
1080
1081    copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
1082    if (copied != sizeof(tmp))
1083        return -EIO;
1084    return put_user(tmp, (unsigned long __user *)data);
1085 }

7int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
1088                unsigned long data)
1089{
1090    int copied;
1091
1092    copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
1093    return (copied == sizeof(data)) ? 0 : -EIO;
1094}
1095

int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
2058 {
2059    struct mm_struct *mm;
2060
2061    if (addr + len < addr)
2062        return 0;
2063
2064    mm = get_task_mm(tsk);
2065    if (!mm)
2066        return 0;
2067
2068    len = __access_remote_vm(tsk, mm, addr, buf, len, write);
2069
2070    mmput(mm);
2071    return len;
2072 }

static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm,
2006        unsigned long addr, void *buf, int len, int write)
2007 {
2008    struct vm_area_struct *vma;
2009
2010    down_read(&mm->mmap_sem);
2011
2012    /* the access must start within one of the target process's mappings */
2013    vma = find_vma(mm, addr);
2014    if (vma) {
2015        /* don't overrun this mapping */
2016        if (addr + len >= vma->vm_end)
2017            len = vma->vm_end - addr;
2018
2019        /* only read or write mappings where it is permitted */
2020        if (write && vma->vm_flags & VM_MAYWRITE)
2021            copy_to_user_page(vma, NULL, addr,
2022                     (void *) addr, buf, len);
2023        else if (!write && vma->vm_flags & VM_MAYREAD)
2024            copy_from_user_page(vma, NULL, addr,
2025                        buf, (void *) addr, len);
2026        else
2027            len = 0;
2028    } else {
2029        len = 0;
2030    }
2031
2032    up_read(&mm->mmap_sem);
2033
2034    return len;
2035 }

2#undef copy_to_user_page
83static inline void copy_to_user_page(struct vm_area_struct *vma,
84                       struct page *page,
85                       unsigned long vaddr,
86                       void *dst, void *src, int len)
87{
88  memcpy(dst, src, len);
89  if (vma->vm_flags & VM_EXEC) {
90      flush_icache_range((unsigned long) dst,
91      (unsigned long) dst + len);
92  }
93}

獲取內(nèi)存的值和寫入內(nèi)存值邏輯上差不多蝙搔,內(nèi)核是通過操作區(qū)域的屬性來判斷寫入還是讀取缕溉。最終由memcpy函數(shù)實現(xiàn)內(nèi)存的拷貝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吃型,一起剝皮案震驚了整個濱河市证鸥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌勤晚,老刑警劉巖枉层,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赐写,居然都是意外死亡鸟蜡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門挺邀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揉忘,“玉大人,你說我怎么就攤上這事端铛∑” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵禾蚕,是天一觀的道長您朽。 經(jīng)常有香客問我,道長夕膀,這世上最難降的妖魔是什么虚倒? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮产舞,結(jié)果婚禮上魂奥,老公的妹妹穿的比我還像新娘。我一直安慰自己易猫,他們只是感情好耻煤,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著准颓,像睡著了一般哈蝇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上攘已,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天炮赦,我揣著相機與錄音,去河邊找鬼样勃。 笑死吠勘,一個胖子當(dāng)著我的面吹牛性芬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剧防,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼植锉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了峭拘?” 一聲冷哼從身側(cè)響起俊庇,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸡挠,沒想到半個月后辉饱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡拣展,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年鞋囊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞎惫。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖译株,靈堂內(nèi)的尸體忽然破棺而出瓜喇,到底是詐尸還是另有隱情,我是刑警寧澤歉糜,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布乘寒,位于F島的核電站,受9級特大地震影響匪补,放射性物質(zhì)發(fā)生泄漏伞辛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一夯缺、第九天 我趴在偏房一處隱蔽的房頂上張望蚤氏。 院中可真熱鬧,春花似錦踊兜、人聲如沸竿滨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽于游。三九已至,卻和暖如春垫言,著一層夾襖步出監(jiān)牢的瞬間贰剥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工筷频, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚌成,地道東北人前痘。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像笑陈,于是被迫代替她去往敵國和親际度。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容