【Python微信機器人】第六七篇: 封裝32位和64位Python hook框架實戰列印微信日誌

来源:https://www.cnblogs.com/kanadeblisst/archive/2023/12/26/17928132.html
-Advertisement-
Play Games

目錄修整 目前的系列目錄(後面會根據實際情況變動): 在windows11上編譯python 將python註入到其他進程並運行 註入Python並使用ctypes主動調用進程內的函數和讀取記憶體結構體 調用彙編引擎實戰發送文本和圖片消息(支持32位和64位微信) 允許Python載入運行py腳本且支 ...


目錄修整

目前的系列目錄(後面會根據實際情況變動):

  1. 在windows11上編譯python
  2. 將python註入到其他進程並運行
  3. 註入Python並使用ctypes主動調用進程內的函數和讀取記憶體結構體
  4. 調用彙編引擎實戰發送文本和圖片消息(支持32位和64位微信)
  5. 允許Python載入運行py腳本且支持熱載入
  6. 利用彙編和反彙編引擎寫一個x86任意地址hook,實戰Hook微信日誌
  7. 封裝Detours為dll,用於Python中x64函數 hook,實戰Hook微信日誌
  8. 實戰32位和64位接收消息和消息防撤回
  9. 實戰讀取記憶體鏈表結構體(好友列表)
  10. 做一個僵屍粉檢測工具
  11. 根據bug反饋和建議進行細節上的優化
  12. 其他功能看心情加

上上篇文章說的以後只更新32位版本這句話收回,以後會同時更新32位和64位的最新版本,已經可以在Python中使用Detours來hook 64位版本。

為了加快進度,第六篇和第七篇同一天發佈,這篇文章為使用總結,想知道hook原理的可以看同時間發佈的其他幾篇文章。

溫馨提示:本次發佈的這幾篇文章都是偏技術,想獲取成品直接使用的可以等下一篇文章(實戰32位和64位接收消息和消息防撤回)

另外,這篇文章開始建群,請關註github或者公眾號菜單欄

封裝好的Hook庫

32位程式的Hook

hook的參數有兩個:記憶體地址和回調函數。回調函數的參數是一個包含x86所有寄存器的結構體指針,沒有返回值。結構體的定義如下:

class RegisterContext(Structure):
    _fields_ = [
        ('EFLAGS', DWORD),
        ('EDI', DWORD),
        ('ESI', DWORD),
        ('EBP', DWORD),
        ('ESP', DWORD),
        ('EBX', DWORD),
        ('EDX', DWORD),
        ('ECX', DWORD),
        ('EAX', DWORD),
    ]

一個簡單的Hook 示例:

def default_hook_log_callback(pcontext):
    # 獲取指針內容,獲取的context就是RegisterContext類型了
    context:RegisterContext = pcontext.contents
    # 取eax寄存器的值
    eax = context.EAX
    print("當前eax寄存器的值: ", eax)

addr = 0x100000
hooker = Hook()
hooker.hook(addr, hook_log_callback_enter)

context這個結構體獲取的就是當執行到這個地址時的寄存器的值,這個和你用x32dbg看到的寄存器的值是一樣的。值的類型都定義成DWORD,如果寄存器是類型是其他類型,比如字元串或結構體,你需要在Python里做相應的轉換,可以參考下麵Hook日誌的代碼

你同樣可以在回調函數里修改這個指針中寄存器的值,它會反映到實際的寄存器,案例的話會在消息防撤回那一篇文章演示。

64位的Hook

因為64位hook是封裝的Detour,比32位需要多定義一個函數指針,而且只能hook函數。所以hook之前需要知道被Hook的函數參數有幾個,類型如果不知道的話,可以像上面一樣都定義成c_uint64

回調函數的參數跟被Hook函數的參數必須一樣,如果參數很多,你也可以用*arg來表示,示例代碼如下:

def hook_log_callback(*args):
    print(args)
    print(kwargs)
        
hooker = Hook()
log_addr = 0x100000
c_log_addr = c_uint64(log_addr)
lp_log_func = CFUNCTYPE(c_uint64, c_uint64, c_uint64, c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64)
hooker.hook(c_log_addr, lp_log_func, hook_log_callback)

另外,回調函數的返回值類型也需要和被Hook函數一樣,一般都是先調用原函數獲取返回值然後返回。如果返回錯誤類型的返回值,進程會崩潰。

案例

為什麼要選擇Hook日誌做案例?日誌是多線程列印的,如果Hook日誌沒有問題的話,其他任何位置的Hook基本都不會有問題。

效果

hook後的效果如下:

32位代碼

from py_process_hooker import Hook
from py_process_hooker.winapi import *

base = GetModuleHandleW("WeChatWin.dll")

先定義回調函數,因為我需要同時獲取參數和返回值,所以要hook兩個地方(函數頭和函數尾)。

用x32dbg在日誌函數頭位置下個斷點,看起來有兩個有用的信息:EDX的代碼路徑和esp的函數返回地址。

定義回調函數:

def hook_log_callback_enter(pcontext):
    context = pcontext.contents
    esp = context.ESP
    # 計算調用日誌函數的地址偏移
    esp_call_offset = c_ulong.from_address(esp).value - base
    # 獲取日誌中的代碼文件路徑
    edx = context.EDX
    # 類型是char數組,ctypes定義是(c_char * n), 這個*是Python中的乘號,
    # 如果是char*指針 ctypes則定義為c_char_p
    c_code_file = (c_char * MAX_PATH).from_address(edx)
    code_file = c_code_file.value.decode()
    print(f"調用地址: WeChatWin.dll+{hex(esp_call_offset)}, 代碼路徑: {code_file}, ", end=" ")

然後看返回值,返回值獲取的是EAX的值

def hook_log_callback_leave(pcontext):
    context = pcontext.contents  
    eax = context.EAX
    c_log_info = (c_char * 1000).from_address(eax)
    log_info = c_log_info.value.decode()
    print("日誌信息: ", log_info)

在new一個Hook類hook這兩個位置:

hooker = Hook()
enter_addr = base + 0x102C250
hook.hook(enter_addr, hook_log_callback_enter)

enter_addr = base + 0x102C584
hook.hook(enter_addr, hook_log_callback_leave)

因為需要支持熱載入,所以在hook之前先調用一下unhook,這樣你修改代碼就會生效新的hook。

使用

你想hook日誌的話,先將github的代碼拉下來,然後安裝依賴,再運行main.py註入Python之後,修改robot.py, 添加如下代碼控制台就會列印日誌了:

from module import HookLog

h = HookLog()
h.hook() 

github的代碼更新了3.9.8.153.9.8.12兩個版本,如果有更新的版本,請提issue。

64位代碼

from py_process_hooker import Hook
from py_process_hooker.winapi import *

x64dbg打上斷點,可以看到RDX是代碼路徑,而RDX是函數的第二個參數。因為獲取不到寄存器,所以返回地址就拿不到了。

返回值如下, 也是char數組:

定義回調函數,日誌函數有12個參數,我就用args來代替了:

def hook_log_callback(*args):
    # 讀取第二個參數的代碼路徑
    c_code_file = (c_char * MAX_PATH).from_address(args[1])
    code_file = c_code_file.value.decode()
    # 調用被hook函數,至於為什麼要這麼調請看編譯和講解Detour那一篇
    ret = lp_log_func(c_log_addr.value)(*args)
    # 讀取返回值中的日誌信息
    c_log_info = (c_char * 1000).from_address(ret)
    log_info = c_log_info.value.decode()
    print(f"文件路徑: {code_file}, 日誌信息: {log_info}")
    return ret

開始hook

log_addr = GetModuleHandleW("WeChatWin.dll") + 0x13D6380
# 定義一個保存日誌函數地址的指針
c_log_addr = c_uint64(log_addr)
# 定義函數類型
lp_log_func = CFUNCTYPE(c_uint64, c_uint64, c_uint64, c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64,c_uint64)

hooker = Hook()
# 註意c_log_addr的生命周期,不能被垃圾回收機制回收
hook.hook(c_log_addr, lp_log_func, hook_log_callback)

代碼更新

以後微信相關的代碼統一到下麵的倉庫更新:

  • github:https://github.com/kanadeblisst00/WeChat-PyRobot
  • 國內倉庫: http://www.pygrower.cn:21180/kanadeblisst/WeChat-PyRobot

32位和64位hook的代碼封裝成庫併發布到pypi,可以通過pip install py_process_hooker安裝或者pip install --upgrade py_process_hooker更新,具體操作請看倉庫說明。

  • github: https://github.com/kanadeblisst00/py_hooker
  • 國內倉庫: http://www.pygrower.cn:21180/kanadeblisst/py_hooker

其實微信相關的代碼也可以發佈到pypi,後面代碼穩定下來再看要不要發佈。因為目前需要頻繁更新,比較麻煩。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡介 Rust 編程語言裡面有兩種巨集系統,一種是聲明巨集(Declarative Macros),另一種為過程巨集(Procedural Macros)。聲明巨集和過程巨集是兩種基本上完全不一樣的巨集系統,編寫的方式也完全不一致,使用方式除了函數式外也不一致。關於聲明巨集學習,Rust 巨集小冊 裡面有比較詳細的 ...
  • 在打算自己實現二維碼的定位的時候,看到了相關博文的關於cv2.findContours返回的層級信息來定位三個“回”字從而達到定位二維碼的目的,但是返回的hierarchy中的層級信息分別對應的是哪個輪廓卻困擾了許久,查閱了很多資料最後還是自己手動找出了清晰的規律。 關於hierarchy返回的每一 ...
  • 前言: 2023-12-26 19:38:05 最近學習分散式技術:Dubbo+zookeeper,準備寫一個demo用springboot整合dubbo和zookeeper。但是看了網上一些教程都是幾年前的,試著跟著寫了幾個demo沒一個跑起來,基本是maven依賴方面的問題。 版本信息: spr ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹`TableView`組件與資料庫聯動的常用方法及靈活運用。在Qt中,通常我們不會在`TableView`等組件中... ...
  • 1 public class code1 { 2 public static void main(String[]args) { 3 int[][] x = new int[6][6]; 4 for (int i = 0; i < x.length; i++) { 5 x[i][0] = 1; 6 ...
  • 這一章講述了RESTful API的基本概念和設計原則。通過比較傳統方式和RESTful方式操作資源的URL定義,能明顯看出RESTful的簡潔和意圖明確。RESTful的API設計使用不同的HTTP方法來操作資源,比如GET用於查詢、POST用於新增、PUT用於更新全部欄位、PATCH用於更新部分... ...
  • 隨著科技的快速發展和人們對個人命運的關註,越來越多的人開始尋找各類方法來預測自己的未來走向。而其中,八字預測是一種古老而又傳統的方法,通過計算生辰八字,從五行八字中揭示出個人的命運走勢。在這個過程中,挖數據平臺提供了一款免費算命的API介面,為用戶提供了便捷的命運預測服務。 首先,我們來瞭解一下什麼 ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹`QSqlDatabase`資料庫模塊的常用方法及靈活運用。Qt SQL模塊是Qt框架的一部分,它提供了一組類和函... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...