x64下手工HOOK的方法
關於64位程序.網上HOOK方法一大堆.這里也記錄一下. 了解跨平台HOOK的真相與本質.
一丶HOOK的幾種方法之遠跳
1. 遠跳 不影響寄存器 + 15字節方法
在64位下 HOOK有幾種方法. 一種是影響寄存器的值.另一種是不影響寄存器的值.各有優劣.
第一種: 不影響寄存器的值 硬編碼占用大小為15個字節.
原理: 利用push + ret的原理. 讓HOOK的位置跳轉為我們的地址.
push 函數低地址(8個字節)
mov qword ptr ss:[rsp + 4],函數高地址(8個字節,不過高4個字節一般都是0所以可以不用給)
ret
硬編碼:
68 XX XX XX XX push LowAddress
48 C7 44 24 04 XX XX XX XX mov qword ptr ss:[rsp + 4],HighAddress
C3 ret
其中XX的地方可以換成我們的地址.
2.遠跳 影響寄存器 + 12字節方法
這一種方法則會影響寄存器的值.
原理: 利用 rax + jmp的方式進行跳轉.
mov rax,Address
Jmp rax
硬編碼
48 B8 XX XX XX XX XX XX XX XX FF E0
截圖:
這里需要注意的是地址的大小尾模式. 填寫一定不要寫錯.
3.影響寄存器,恢復寄存器 進行跳轉.
這種方法其實是第二種方法的演變. 第二種我們會直接修改rax為我們的地址.其實我們可以利用堆棧進行保存.
原理: rax + jmp + push 進行恢復還原
push reg
mov reg,address
jmp reg
pop reg
reg代表的就是任意寄存器. 如果使用這個方法可以有效地保存寄存器進行HOOK.跳轉回來的時候進行還原即可.
這里的硬編碼不確定.因為 push reg 與 pop reg 跟你使用的寄存器有關.
4. 常用 jmp + rip方式跳轉 大小6個字節
在64位程序中. 可以使用rip寄存器了. 而32位不可以.32位下想要改變 eip的值. 無非就是 jmp + call才可以改變.64位可以使用
原理: jmp + rip 進行尋址. 進行跳轉
jmp qword ptr ds:[rip]
數據地址
這種方法就是 對rip + (指令長度)這個寄存器取內容 把里面的數據當做地址進行跳轉. 所以當使用這個方式的時候.我們的下方跟着八個字節數據即可. 這個數據就是你要跳轉的地址.
如下:
它會把下面八個字節數據當做地址進行跳轉.
這時候有人說了.你這不是6字節跳轉呀.下面要跟着8個字節. 所以這里說一下.
使用這種方法. 在rip的下面不一定要跟着就是數據. 我們可以修改偏移進行跳轉.
什么意思. 現在我們是 jmp qword ptr ds:[rip] ,代表了我們要在當前rip的下方取8個字節當做地址跳轉. 那么我們也可以寫成 jmp qword ptr ds:[rip + x];
x就是任意數. 把這個偏移下的8個字節去內容當做地址去跳轉.
關於偏移 計算公式其實還是一樣. 目的地址 - 源地址 - 指令長度.
如下:
比如我們從第一條指令 進行偏移跳轉. 把圖中紅框內容當做8個字節數據進行跳轉.
那么可以寫成如下:
目的地址(紅框地址,0x7FFF12A51228) - 源地址(7FFF12A511Dd) - 指令長度(6)
指令長度為什么是6.因為我們第一行指令是 jmp qword ptr ds:[rip] 只不過我們現在還沒寫. 我們得出的偏移放到第一行中的偏移..
現在得出的偏移為 0x45 這個45我們就可以填寫到偏移中.
現在 0x7FFF12A51228 紅框地址是我們的數據.只需要在這里寫入8個字節地址即可.
如下圖:
如上圖可以看到,第一個紅框 FF 25 45 00 00 00 這個45就是我們算的偏移.
意思就是在 rip + 45位置,讀取8個字節數據當做地址進行跳轉. 然后 228地址我們寫入了8個字節數據. 數據的地址 后綴為1EC的地址. 所以看上圖,我們的RIP已經跳轉過去了.
注意,因為 rip的尋址偏移只能是上下2GB 也就是你的偏移不要超過2G位置即可.
二丶Call的幾種方式.
1. CALL PUSH + RET 方式
這種方式跟上方的push + ret原理類似.
call Next
Next:
push lowAddress;
mov [rsp + 4],highAddress
ret
這種方式就是取得下一行指令, 下一行指令入棧. 然后繼續push + ret 進行跳轉.
2.正常call
這種call 沒有試過. 原理跟上面一樣. 硬編碼 E8 偏移 的方式調用
call youAddress
硬編碼為: E8 XX XX XX XX xx代表偏移. 偏移計算公式還是 目的 - 源 - 5(指令長度)
但是是上下2GB
其他待整理