色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何用替換函數的Trick做正常的事情

Linux閱碼場 ? 來源:未知 ? 作者:胡薇 ? 2018-11-23 11:00 ? 次閱讀

浙江溫州皮鞋濕,下雨進水不會胖。周六的雨夜,期待明天的雨會更大更冷。

已經多久沒有編程了?很久了吧…其實我本來就不怎么會寫代碼,時不時的也就是為了驗證一個系統特性,寫點玩具而已,工程化的代碼,對于我而言,實在是吃力。

最近遇到一些問題,需要特定的解法,也就有機會手寫點代碼了。其實這個話題記得上一次遇到是在8年前,時間過得好快。

替換一個已經在內存中的函數,使得執行流流入我們自己的邏輯,然后再調用原始的函數,這是一個很古老的話題了。比如有個函數叫做funcion,而你希望統計一下調用function的次數,最直接的方法就是 如果有誰調用function的時候,調到下面這個就好了 :

void new_function()

{

count++;

return function();

}

網上很多文章給出了實現這個思路的Trick,而且一直以來計算機病毒也都采用了這種偷梁換柱的伎倆來實現自己的目的。然而,當你親自去測試時,發現事情并不那么簡單。

網上給出的許多方法均不再適用了,原因是在早期,這樣做的人比較少,處理器操作系統大可不必理會一些不符合常規的做法,但是隨著這類Trick開始做壞事影響到正常的業務邏輯時,處理器廠商以及操作系統廠商或者社區便不得不在底層增加一些限制性機制,以防止這類Trick繼續起作用。

常見的措施有兩點:

可執行代碼段不可寫

這個措施便封堵住了你想通過簡單memcpy的方式替換函數指令的方案。

內存buffer不可執行

這個措施便封堵住了你想把執行流jmp到你的一個保存指令的buffer的方案。

stack不可執行

別看這些措施都比較low,一看誰都懂,它們卻避免了大量的緩沖區溢出帶來的危害。

那么如果我們想用替換函數的Trick做正常的事情,怎么辦?

我來簡單談一下我的方法。首先我不會去HOOK用戶態的進程的函數,因為這樣意義不大,改一下重啟服務會好很多。所以說,本文特指HOOK內核函數的做法。畢竟內核重新編譯,重啟設備代價非常大。

我們知道,我們目前所使用的幾乎所有計算機都是馮諾伊曼式的統一存儲式計算機,即指令和數據是存在一起的,這就意味著我們必然可以在操作系統層面隨意解釋內存空間的含義。

我們在做正當的事情,所以我假設我們已經拿到了系統的root權限并且可以編譯和插入內核模塊。那么接下來的事情似乎就是一個流程了。

是的,修改頁表項即可,即便無法簡單地通過memcpy來替換函數指令,我們還是可以用以下的步驟來進行指令替換:

重新將函數地址對應的物理內存映射成可寫;

用自己的jmp指令替換函數指令;

解除可寫映射。

非常幸運,內核已經有了現成的 text_poke/text_poke_smp 函數來完成上面的事情。

同樣的,針對一個堆上或者棧上分配的buffer不可執行,我們依然有辦法。辦法如下:

編寫一個stub函數,實現隨意,其代碼指令和buffer相當;

用上面重映射函數地址為可寫的方法用buffer重寫stub函數;

將stub函數保存為要調用的函數指針。

是不是有點意思呢?下面是一個步驟示意圖:

下面是一個代碼,我稍后會針對這個代碼,說幾個細節方面的東西:

#include

#include

#include

#include

#include

#define OPTSIZE5

// saved_op保存跳轉到原始函數的指令

char saved_op[OPTSIZE] = {0};

// jump_op保存跳轉到hook函數的指令

char jump_op[OPTSIZE] = {0};

static unsigned int (*ptr_orig_conntrack_in)(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_hook_state *state);

static unsigned int (*ptr_ipv4_conntrack_in)(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_hook_state *state);

// stub函數,最終將會被保存指令的buffer覆蓋掉

static unsigned int stub_ipv4_conntrack_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_hook_state *state)

{

printk("hook stub conntrack\n");

return 0;

}

// 這是我們的hook函數,當內核在調用ipv4_conntrack_in的時候,將會到達這個函數。

static unsigned int hook_ipv4_conntrack_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_hook_state *state)

{

printk("hook conntrack\n");

// 僅僅打印一行信息后,調用原始函數。

return ptr_orig_conntrack_in(ops, skb, in, out, state);

}

static void *(*ptr_poke_smp)(void *addr, const void *opcode, size_t len);

static __init int hook_conn_init(void)

{

s32 hook_offset, orig_offset;

// 這個poke函數完成的就是重映射,寫text段的事

ptr_poke_smp = kallsyms_lookup_name("text_poke_smp");

if (!ptr_poke_smp) {

printk("err");

return -1;

}

// 嗯,我們就是要hook住ipv4_conntrack_in,所以要先找到它!

ptr_ipv4_conntrack_in = kallsyms_lookup_name("ipv4_conntrack_in");

if (!ptr_ipv4_conntrack_in) {

printk("err");

return -1;

}

// 第一個字節當然是jump

jump_op[0] = 0xe9;

// 計算目標hook函數到當前位置的相對偏移

hook_offset = (s32)((long)hook_ipv4_conntrack_in - (long)ptr_ipv4_conntrack_in - OPTSIZE);

// 后面4個字節為一個相對偏移

(*(s32*)(&jump_op[1])) = hook_offset;

// 事實上,我們并沒有保存原始ipv4_conntrack_in函數的頭幾條指令,

// 而是直接jmp到了5條指令后的指令,對應上圖,應該是指令buffer里沒

// 有old inst,直接就是jmp y了,為什么呢?后面細說。

saved_op[0] = 0xe9;

// 計算目標原始函數將要執行的位置到當前位置的偏移

orig_offset = (s32)((long)ptr_ipv4_conntrack_in + OPTSIZE - ((long)stub_ipv4_conntrack_in + OPTSIZE));

(*(s32*)(&saved_op[1])) = orig_offset;

get_online_cpus();

// 替換操作!

ptr_poke_smp(stub_ipv4_conntrack_in, saved_op, OPTSIZE);

ptr_orig_conntrack_in = stub_ipv4_conntrack_in;

barrier();

ptr_poke_smp(ptr_ipv4_conntrack_in, jump_op, OPTSIZE);

put_online_cpus();

return 0;

}

module_init(hook_conn_init);

static __exit void hook_conn_exit(void)

{

get_online_cpus();

ptr_poke_smp(ptr_ipv4_conntrack_in, saved_op, OPTSIZE);

ptr_poke_smp(stub_ipv4_conntrack_in, stub_op, OPTSIZE);

barrier();

put_online_cpus();

}

module_exit(hook_conn_exit);

MODULE_DESCRIPTION("hook test");

MODULE_LICENSE("GPL");

MODULE_VERSION("1.1");

測試是OK的。

在上面的代碼中,saved_op中為什么沒有old inst呢?直接就是一個jmp y,這豈不是將原始函數中的頭幾個字節的指令給遺漏了嗎?

其實說到這里,還真有個不好玩的Trick,起初我真的就是老老實實保存了前5個自己的指令,然后當需要調用原始ipv4_conntrack_in時,就先執行那5個保存的指令,也是OK的。隨后我objdump這個函數發現了下面的代碼:

0000000000000380 :

380: e8 00 00 00 00 callq 385

385: 55 push %rbp

386: 49 8b 40 18 mov 0x18(%r8),%rax

38a: 48 89 f1 mov %rsi,%rcx

38d: 8b 57 2c mov 0x2c(%rdi),%edx

390: be 02 00 00 00 mov $0x2,%esi

395: 48 89 e5 mov %rsp,%rbp

398: 48 8b b8 e8 03 00 00 mov 0x3e8(%rax),%rdi

39f: e8 00 00 00 00 callq 3a4

3a4: 5d pop %rbp

3a5: c3 retq

3a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)

3ad: 00 00 00

注意前5個指令: e8 00 00 00 00 callq 385

可以看到,這個是可以忽略的。因為不管怎么說都是緊接著執行下面的指令。所以說,我就省去了inst的保存。

如果按照我的圖示中常規的方法的話,代碼稍微改一下即可:

char saved_op[OPTSIZE+OPTSIZE] = {0};

...

// 增加一個指令拷貝的操作

memcpy(saved_op, (unsigned char *)ptr_ipv4_conntrack_in, OPTSIZE);

saved_op[OPTSIZE] = 0xe9;

orig_offset = (s32)((long)ptr_ipv4_conntrack_in + OPTSIZE - ((long)stub_ipv4_conntrack_in + OPTSIZE + OPTSIZE));

(*(s32*)(&saved_op[OPTSIZE+1])) = orig_offset;

但是以上的只是玩具。

有個非常現實的問題。在我保存原始函數的頭n條指令的時候,n到底是多少呢?在本例中,顯然n是5,符合如今Linux內核函數第一條指令幾乎都是callq xxx的慣例。

然而,如果一個函數的第一條指令是下面的樣子:

op d1 d2 d3 d4 d5

即一個操作碼需要5個操作數,我要是只保存5個字節,最后在stub中的指令將會是下面的樣子:

op d1 d2 d3 d4 0xe9 off1 off2 off3 off4

這顯然是錯誤的,op操作碼會將jmp指令0xe9解釋成操作數。

解藥呢?當然有咯。

我們不能魯莽地備份固定長度的指令,而是應該這樣做:

curr = 0

if orig[0] 為單字節操作碼

saved_op[curr] = orig[curr];

curr++;

else if orig[0] 攜帶1個1字節操作數

memcpy(saved_op, orig, 2);

curr += 2;

else if orig[0] 攜帶2字節操作數

memcpy(saved_op, orig, 3);

curr += 3;

...

saved_op[curr] = 0xe9; // jmp

offset = ...

(*(s32*)(&saved_op[curr+1])) = offset;

這是正確的做法。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11292

    瀏覽量

    209331
  • 函數
    +關注

    關注

    3

    文章

    4327

    瀏覽量

    62573

原文標題:Linux內核如何替換內核函數并調用原始函數

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    何用labview啟動

    何用labview啟動
    發表于 03-13 16:07

    何用Primff()函數打印串行端口信息?

    ”);// Uart可以打印串口信息。現在,我想這樣:PrtTF(“Helon Test\r\n”);//“Helon Test\R\n”由串行工具恢復。所以,我的問題是如何用Primff()函數打印串行端口
    發表于 09-26 08:35

    GUI函數里面做了哪些事情,對顯示界面有何作用

    請問 GUI_TOUCH_Exec()函數里面做了哪些事情,對顯示界面有何作用?該函數的資料很難找到,請大家幫忙。謝謝!
    發表于 06-03 04:35

    何用Matlab去實現FFT函數和IFFT函數

    Matlab的FFT函數和IFFT函數有什么用法嗎?如何用Matlab去實現FFT函數和IFFT函數呢?
    發表于 11-18 07:05

    何用printf()函數代替串口發送數據?

    何用printf()函數代替串口發送數據?
    發表于 12-01 08:01

    何用STM32通用定時器微秒延時函數

    何用STM32通用定時器微秒延時函數
    發表于 12-01 06:37

    何用__write函數替換掉原先的fputc函數

    何用__write函數替換掉原先的fputc函數呢?
    發表于 12-01 06:55

    何用2SC2539替換2SC1971

    何用2SC2539替換2SC1971
    發表于 12-22 11:40 ?3172次閱讀

    替換數組子集函數

    Labview之替換數組子集函數,很好的Labview資料,快來下載學習吧。
    發表于 04-19 10:43 ?0次下載

    何用AD電路板邊框

    何用AD電路板邊框,感興趣的小伙伴們可以看看。
    發表于 07-26 10:43 ?0次下載

    matlab升級2021a版本后有哪些函數需要替換

    使用新的函數進行替換!先到互聯網上搜索一下該函數的使用方法吧!因為無法在matlab里面運行此函數了,所以老函數的用法只能靠搜索來給大家
    的頭像 發表于 06-10 16:44 ?9826次閱讀

    在C++中如何用函數實現多態

    01 — C++虛函數探索 C++是一門面向對象語言,在C++里運行時多態是由虛函數和純虛函數實現的,現在我們看下在C++中如何用函數實現
    的頭像 發表于 09-29 14:18 ?1693次閱讀

    C語言內聯函數

    函數B很小,又被頻繁的調用,可能函數調用的切換時間比函數內代碼的執行時間還長,這樣明顯劃不來,那么我們就可以將這個函數聲明為內聯(加上 inline ),編譯器在編譯時,會把內聯
    的頭像 發表于 02-21 16:55 ?951次閱讀
    C語言內聯<b class='flag-5'>函數</b>

    何用兩種不同的方法列寫雙容水槽傳遞函數

    何用兩種不同的方法列寫雙容水槽傳遞函數
    的頭像 發表于 03-10 16:20 ?3822次閱讀
    如<b class='flag-5'>何用</b>兩種不同的方法列寫雙容水槽傳遞<b class='flag-5'>函數</b>

    MySQL替換字符串函數REPLACE

    MySQL是目前非常流行的開源數據庫管理系統之一,它具有強大的功能和性能。其中之一的字符串函數REPLACE,可以用于替換字符串中的指定字符或字符串。在本文中,我們將詳細討論MySQL替換字符串
    的頭像 發表于 11-30 10:44 ?1545次閱讀
    主站蜘蛛池模板: 蜜臀色欲AV无人A片一区| 精品一卡2卡三卡4卡乱码精品视频| 国产免费内射又粗又爽密桃视频| 极品色αv影院| 嫩草影院在线观看网站成人| 偷偷要色偷偷| 2021国产在线视频| 国产精品久久久久久人妻精品蜜桃 | 在线视频a| 动漫美女人物被黄漫在线看| 九色PORNY真实丨首页| 日韩精品在线看| 伊人大香线蕉精品在线播放| 高清 仑乱 一级 a| 榴莲推广APP网站入口下载安装| 特污兔午夜影视院| 90后性爱视频| 国产亚洲精品久久久无码狼牙套 | 耽肉高h喷汁呻吟| 久久全国免费久久青青小草| 思思久久99热只有频精品66| 91香蕉福利一区二区三区| 国产探花在线精品一区二区| 欧美午夜精品一区二区蜜桃 | 97视频在线观看视频最新| 国产亚洲精品久久久999无毒| 欧美日韩第一区| 永久免费的污视频网站| 国产精品免费大片| 寂寞夜晚看免费视频| 妓女嫖客叫床粗话对白| 国内精品蜜汁乔依琳视频| 久久这里只有热精品18| 快乐激情站| 日韩欧美1区| 小黄文污到你湿| 这里只有精品网| a4you销魂gogo人体| 嫩草欧美曰韩国产大片| 人人干人人看| 色小说在线|