linux動態注入(含視訊演示)
如果純粹用文字來描述什麼是動態注入,可能還是不太容易理解,所以本篇文章從如下一段程式碼開始:
// who.c #include <stdio.h> #include <unistd.h> int main() { while (1) { printf("who are you ?\n"); sleep(2); } return 0; }
這段程式碼本身沒有什麼意思,執行"gcc who.c -o who -g -Wall"完成編譯並啟動who程式,每隔2s會向終端列印一句"who are you ?",但有意思的是,可能會出現一種"詭異"的現象,比如螢幕上突然冒出一句"it's me ~"。
1. 為什麼可以出現這種“詭異”的現象?
通過反彙編who程式可以看出,程式最終會進入while(1)迴圈體,重複執行"bf24064000"、"e8c5feffff"、"bd0x000000"、"e8ebfeffff"、"ebea"這5條機器指令:
如果who程式執行到某條指令(比如"e8c5feffff")時暫停了,並且在恢復執行之前,0x400586處的指令塊被替換成列印"it's me ~"的程式碼,那麼who程式下次執行,自然就會列印"it's me ~"。
2. 怎麼獲取列印"it's me ~"的機器碼?
- 32位系統,編譯如下程式碼並在反彙編結果中提取:
// isme32.c int main() { __asm__( "jmp forward\n\t" "backward:popl %esi\n\t" "movl $4, %eax\n\t" "movl $2, %ebx\n\t" "movl %esi, %ecx\n\t" "movl $12, %edx\n\t" "int $0x80\n\t" "int3\n\t" "forward:call backward\n\t" ".string \"it's me ~\\n\"" ); return 0; }
- 64位系統,編譯如下程式碼並在反彙編結果中提取:
// isme64.c int main() { __asm__( "jmp forward\n\t" "backward:popq %rsi\n\t" "movq $1, %rax\n\t" "movq $2, %rdi\n\t" "movq $12, %rdx\n\t" "syscall\n\t" "int3\n\t" "forward:call backward\n\t" ".string \"it's me ~\\n\"" ); return 0; }
本篇文章的實驗環境是64位ubuntu系統,所以執行"gcc isme64.c -oisme64 -g -Wall"生成isme64可執行檔案,並通過反彙編isme64檔案提取機器碼:
用藍色、綠色標記出來的16進位制內容,即為列印"it's me ~"的機器碼,但有2點需要說明:
- 綠色部分其實是"it's me ~\n\0"這串字元的ascii碼值,也被objdump解釋成彙編指令了,這是反彙編工具很難避免的一個問題,用gdb/disassemble反彙編的結果也是一樣的,因為它們選擇的都是線性掃描演算法,遞迴下降演算法效果會好一些,但仍然不能保證得到精確的結果;
- C程式實現列印"it's me ~",寫一條printf("it's me ~")語句不就可以了麼,為什麼要寫成這種"奇奇怪怪"的樣子?因為如果用C語法來實現的話,"it's me ~"這個字串會被編譯器安排到isme64程式的.data段,就沒辦法隨著指令塊一起注入到目標程式中,而且這段程式碼中取"it's me ~"地址的技巧,對於彙編初學者是很有意思的,程式碼不長,建議在大腦裡執行一遍,並找到其中的奧妙。
3. 怎麼實現注入?
(理解這段程式碼,至少需要學習ptrace()系統呼叫的作用,如果有核心基礎,也可以更深入的學習一下ptrace()的內部原理,另外,CODE巨集對應的內容,即為利用文章第2節描述的方法,提取的機器碼)
// inject.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/user.h> #include <errno.h> // 注入指令塊(列印"it's me ~") #ifdef ENV_I386 #define CODE \ "\xeb\x15\x5e\xb8\x04\x00\x00\x00" \ "\xbb\x02\x00\x00\x00\x89\xf1\xba" \ "\x0c\x00\x00\x00\xcd\x80\xcc\xe8" \ "\xe6\xff\xff\xff\x69\x74\x27\x73" \ "\x20\x6d\x65\x20\x7e\x0a\x00" #define REG_IP regs.eip #else // X_64 #define CODE \ "\xeb\x19\x5e\x48\xc7\xc0\x01\x00" \ "\x00\x00\x48\xc7\xc7\x02\x00\x00" \ "\x00\x48\xc7\xc2\x0c\x00\x00\x00" \ "\x0f\x05\xcc\xe8\xe2\xff\xff\xff" \ "\x69\x74\x27\x73\x20\x6d\x65\x20" \ "\x7e\x0a\x00" #define REG_IP regs.rip #endif #define CODE_SIZE (sizeof(CODE)-1) /* 往pid程式的addr地址處寫資料 */ void putdata(pid_t pid, unsigned long addr, void *vptr, int len) { int count = 0; long word; while (count < len) { memcpy(&word, vptr+count, sizeof(word)); word = ptrace(PTRACE_POKEDATA, pid, addr+count, word); count += sizeof(word); if (errno != 0) printf("putdata failed: %p\n", (void *)(addr+count)); } } /* 讀pid程式addr地址處的資料 */ void getdata(pid_t pid, unsigned long addr, void *vptr, int len) { int i = 0, count = 0; long word; unsigned long *ptr = (unsigned long*)vptr; while (count < len) { word = ptrace(PTRACE_PEEKDATA, pid, addr+count, NULL); count += sizeof(word); ptr[i++] = word; if (errno != 0) printf("getdata failed: %p\n", (void *)(addr+count)); } } int main(int argc, char *argv[]) { pid_t pid; struct user_regs_struct regs; char backup[CODE_SIZE+1]; if (argc != 2) { printf("Usage: %s {pid}\n", argv[0]); return -1; } // 通過啟動引數,獲取被注入程式號,並attach pid = atoi(argv[1]); ptrace(PTRACE_ATTACH, pid, NULL, NULL); // SIGTRAP if (errno != 0) printf("attach failed: %s\n", strerror(errno)); wait(NULL); // 收到回覆訊號,保證後續過程在attach完成的情況下執行 // 獲取被注入程式當前暫存器值 ptrace(PTRACE_GETREGS, pid, NULL, ®s); // 備份*ip暫存器指向的指令塊 getdata(pid, REG_IP, backup, CODE_SIZE); // 替換為CODE指令塊 putdata(pid, REG_IP, CODE, CODE_SIZE); // 恢復被注入程式的執行 ptrace(PTRACE_CONT, pid, NULL, NULL); // SIGCONT // 注入程式最後一條指令為int3,此為即為等待注入指令塊執行完畢 wait(NULL); // 等待使用者輸入,方便檢視結果 printf("continue to execute the orginal process, press any key ..\n"); getchar(); // 將修改的指令塊恢復為備份內容 putdata(pid, REG_IP, backup, CODE_SIZE); // 恢復被注入程式暫存器值 ptrace(PTRACE_SETREGS, pid, NULL, ®s); // detach,被注入程式恢復正常執行 ptrace(PTRACE_DETACH, pid, NULL, NULL); return 0; }
4. 視訊演示
a. 執行gcc inject.c -oinject -g -Wall,編譯生成注入程式
b. 檢視who程式號N,並執行sudo inject N,進行動態注入操作
c. 觀察who程式執行視窗是否列印了"it's me ~"
d. 回到inject程式執行視窗,按任意鍵結束inject程式,並恢復who程式到注入前的狀態,繼續不停的列印"who are you ?"
視訊連結:https://v.youku.com/v_show/id_XMzgzMTM1NTU1Ng==.html?spm=a2h0k.11417342.soresults.dposter
5. 參考
ptrace實現動態注入:http://www.cnblogs.com/r1ng0/p/9585356.html
gdb的工作原理:https://blog.csdn.net/u012658346/article/details/51159971
原文作者:xinpoo
原文連結:https://bbs.pediy.com/thread-246948.htm
轉載請註明,轉自看雪論壇
看雪閱讀推薦:
3、[翻譯]國外2018最新區塊鏈教程英文版,大膽翻譯,助力論壇『區塊鏈安全』開設第四棒!
4、[原創]看雪安全峰會—《從WPA2四次握手看KRACK金鑰重灌攻擊》
相關文章
- 高手 Linux 程式碼炫酷秀(含演示視訊)2019-01-11Linux
- 16、DeathStar:一鍵自動化域滲透工具(含演示視訊)2018-06-14
- Linux火焰圖效能分析文章及視訊演示2020-02-22Linux
- 動態代理及java演示2022-01-16Java
- 自動化專案基類實踐--視訊演示2020-04-05
- Activity啟動模式(GIF 動態演示)2019-05-12模式
- Dynamic Wallpaper視訊動態桌布2022-03-16
- String轉化為Int 視訊演示2018-10-24
- Tomcat Filter之動態注入2020-07-04TomcatFilter
- Mac視訊動態桌布:Dynamic Wallpaper2022-03-09Mac
- mysql下載與安裝 視訊演示2018-10-24MySql
- git的基本命令使用演示視訊2018-04-13Git
- Python爬取動態載入的視訊(梨視訊,xpath)2022-03-21Python
- Mac視訊動態桌布:Dynamic Wallpaper Mac2022-03-24Mac
- Github webhooks 自動部署部落格文章,使用總結【含視訊】2021-11-16GithubWebHook
- 隨手記:Bruno動態注入Header2024-07-16Header
- 小程式骨架屏動態注入元件2018-12-05元件
- 4K動態視訊桌布「Dynamic Wallpaper」2020-10-31
- 除錯JS獲得動態視訊地址2018-11-22除錯JS
- Mac上好用的視訊動態桌布:Dynamic Wallpaper2022-03-02Mac
- 怎麼將視訊製作成GIF動態圖2019-06-21
- Linux動態庫2023-10-03Linux
- .NET開發框架(一)-框架介紹與視訊演示2019-06-30框架
- asyncio非同步程式設計【含視訊教程】2020-05-06非同步程式設計
- android短視訊開發,點選靜態圖片自動跳轉播放視訊2021-12-15Android
- Pytorch視訊記憶體動態分配規律探索2020-11-16PyTorch記憶體
- Linux程式狀態——top,ps中看到程式狀態D,S的含義2018-11-15Linux
- linux課堂視訊2020-04-04Linux
- iPhone 新漏洞,可檢視並傳送iPhone照片(含視訊)2018-10-14iPhone
- 短視訊開發,製作上下動態的箭頭動畫2022-04-29動畫
- [Linux]動靜態庫2024-11-30Linux
- 分分鐘讀懂tcp/ip通訊協議原理(含視訊)2018-05-02TCP協議
- 抖音短視訊動態封面設定教程 抖音動態封面怎麼設定?2018-07-23
- 互動視訊 :新形態,新價值(附下載)2020-07-04
- 直播app系統原始碼,動態遇到視訊時開始自動播放2022-06-28APP原始碼
- Linux 安全資訊檢視2018-10-17Linux
- 雪亮工程動態視訊監控系統建設動態人臉識別系統搭建2019-04-02
- 深度解讀 2018 JavaScript 趨勢報告(含視訊)2018-11-23JavaScript