逆向XignCode3驅動程序:分析註冊通信和回調函數(part4)

2020-12-25 騰訊網

在分析DriverEntry時,我確定了兩個函數,它們負責不同類型的回調註冊(fn_InitRegistrationNotifyAndCallbackRoutines和fn_RegisterCreateProcessNotifyRoutine),但我沒有更深入地了解他們的作用。

0x01 概述

·識別互斥鎖和自旋鎖

·了解如何使用Win API註冊通知例程

·了解如何創建PCREATE_PROCESS_NOTIFY_ROUTINE

·了解驅動程序如何管理執行期間可能出現的各種錯誤狀態。

由於目標是分析驅動程序並提供所需的信息,你可以自行學習其餘信息,我不會深入了解什麼是Notify和Callback例程。這裡有一些連結資源:

·PsSetCreateProcessNotifyRoutine

·PsSetCreateProcessNotifyRoutineEx

·PCREATE_PROCESS_NOTIFY_ROUTINE

你需要了解的最重要的是,反作弊控制系統中發生的事情。因此,他們將註冊此通知和回調例程,這將允許他們在觸發事件時執行操作前和操作後的操作,例如,已創建新進程,已加載新DLL等。

有一些函數尚未初始化變量,自旋鎖和數組。所有這些變量都在驅動程序的多個函數中使用,當我逆向尚未見過的其他函數時,將發現那些含義。

由於函數越來越大,越來越複雜,因此我將避免解釋一些有關驅動程序開發的基本知識,例如互斥量初始化,分配等。

函數代碼:

signed __int64 fn_InitRegistrationNotifyAndCallbackRoutines()

{

_QWORD *v0; // rax

_QWORD *v1; // rbx

wchar_t *v2; // rax

signed __int64 result; // rax

__int64(__fastcall *PsSetCreateProcessNotifyRoutineEx)(PVOID, BOOLEAN); // rax

int ntStatus; // eax

signed int v6; // ecx

signed int ntStatus_1; // ebx

__int64 _RemoveRoutine; // rdx

UNICODE_STRING DestinationString; // [rsp+20h] [rbp-18h]

v0 = ExAllocatePoolWithTag(0, 0x28ui64, 0x78687A31u);

qword_14000CD70 = v0;

v1 = v0;

if (!v0)

return 0xC000009Ai64;

memset(v0, 0, 0x28ui64);

*v1 = 0i64;

task_status_NotifyCallbackRoutine = 0; // Task mutex

bNotifyCallbackRoutines = 0;

KeInitializeMutex(&BlackCipherMutex, 0);

v2 = ExAllocatePoolWithTag(0, 0x2000ui64, 0x78687A31u);

Str1 = v2;

if (!v2)

return 0xC000009Ai64;

*v2 = 0;

qword_14000CD08 = 0i64;

someMaxValue = 0x1000i64;

result = j_fn_ConfigWindowsVersion();

if (result

return result;

fn_InitWeirdVariables_();

fn_InitWeirdVariables2_();

status_PsSetCreateProcessNotifyRoutine = 0;

status_PsSetCreateProcessNotifyRoutineEx = 0;

RtlInitUnicodeString(&DestinationString, L"PsSetCreateProcessNotifyRoutineEx");

PsSetCreateProcessNotifyRoutineEx = MmGetSystemRoutineAddress(&DestinationString);

fn_pPsSetCreateProcessNotifyRoutineEx = PsSetCreateProcessNotifyRoutineEx;

if (PsSetCreateProcessNotifyRoutineEx)

{

ntStatus = PsSetCreateProcessNotifyRoutineEx(fn_CreateProcessNotifyRoutineExImp, 0i64);

v6 = status_PsSetCreateProcessNotifyRoutineEx;

if (ntStatus >= 0)

v6 = 1;

status_PsSetCreateProcessNotifyRoutineEx = v6;

}

else

{

v6 = status_PsSetCreateProcessNotifyRoutineEx;

}

if (!v6)

{

ntStatus_1 = PsSetCreateProcessNotifyRoutine(fn_CreateProcessNotifyRoutine, 0i64);

if (ntStatus_1

{

status_PsSetCreateProcessNotifyRoutine = 0;

goto LABEL_13;

}

status_PsSetCreateProcessNotifyRoutine = 1;

}

ntStatus_1 = 0;

LABEL_13:

if (ntStatus_1

{

label_exit:

fn_InitWeirdVariables3_();

nullsub_1();

return ntStatus_1;

}

ntStatus_1 = fn_RegisterCallbackFunction();

if (ntStatus_1

{

if (status_PsSetCreateProcessNotifyRoutineEx && fn_pPsSetCreateProcessNotifyRoutineEx)

fn_pPsSetCreateProcessNotifyRoutineEx(fn_CreateProcessNotifyRoutineExImp, 1u);// 2nd Parameter equal to 1 == remove

if (status_PsSetCreateProcessNotifyRoutine)

{

LOBYTE(_RemoveRoutine) = 1;

PsSetCreateProcessNotifyRoutine(fn_CreateProcessNotifyRoutine, _RemoveRoutine);

}

goto label_exit;

}

return 0i64;

}

在函數的開頭,進行了一些緩衝區和互斥鎖初始化,尚不知道這些變量的用途是什麼,但是由於這些變量的使用方式,可以識別它們。

例如,作為互斥相關函數(例如KeInitializeMutex)或彙編操作碼(例如lock xadd)的參數:

之後,將找到第一個有趣的函數sub_140003C38,我決定將其命名為j_fn_ConfigWindowsVersion。

該函數標識Windows的當前版本,並使用有關某些特定內核結構的信息來初始化一系列偏移變量,繼續執行:

result = j_fn_ConfigWindowsVersion();

if ( result

return result;

fn_InitWeirdVariables_();

fn_InitWeirdVariables2_();

status_PsSetCreateProcessNotifyRoutine = 0;

status_PsSetCreateProcessNotifyRoutineEx = 0;

忽略fn_InitWeirdVariables_和fn_InitWeirdVariables2_,因為這些函數只是初始化一些自旋鎖和驅動程序以後使用的變量,暫時對此不感興趣。

ProcessId >>= 2;

v3 = (ProcessId >> 5) & 0x1FF;

v4 = ~(1

之後,將初始化兩個NTSTATUS變量:status_PsSetCreateProcessNotifyRoutine和status_PsSetCreateProcessNotifyRoutineEx,如果第一次嘗試使用PsSetCreateProcessNotifyRoutineEx註冊NotifyRoutine失敗,那麼他們將使用這些變量來控制執行流程,並再次嘗試使用PsSetCreateProcessNotifyRoutineEx進行嘗試。

RtlInitUnicodeString(&DestinationString, L"PsSetCreateProcessNotifyRoutineEx");

PsSetCreateProcessNotifyRoutineEx = MmGetSystemRoutineAddress(&DestinationString);

fn_pPsSetCreateProcessNotifyRoutineEx = PsSetCreateProcessNotifyRoutineEx;

檢索PsSetCreateProcessNotifyRoutineEx的地址並將其存儲到變量中,如果此變量值不為NULL,則進行第一次嘗試:

if ( PsSetCreateProcessNotifyRoutineEx )

{

ntStatus = PsSetCreateProcessNotifyRoutineEx(fn_CreateProcessNotifyRoutineExImp, 0i64);

v6 = status_PsSetCreateProcessNotifyRoutineEx;

if ( ntStatus >= 0 )

v6 = 1;

status_PsSetCreateProcessNotifyRoutineEx = v6;

}

else

{

v6 = status_PsSetCreateProcessNotifyRoutineEx;

}

可以看到調用了PsSetCreateProcessNotifyRoutineEx ,在第一個參數中,每當創建或退出新進程時,它將發送要執行的例程(fn_CreateProcessNotifyRoutineExImp);在第二個參數中,它確定需要註冊而不是刪除「通知例程」。相同的函數用於創建和刪除NotifyRoutine。

如果第一次嘗試失敗,則對該函數再進一步一點,將看到PsSetCreateProcessNotifyRoutine 第二次嘗試:

if ( !v6 )

{

ntStatus_1 = PsSetCreateProcessNotifyRoutine(fn_CreateProcessNotifyRoutine, 0i64);

if ( ntStatus_1

{

status_PsSetCreateProcessNotifyRoutine = 0;

goto LABEL_13;

}

status_PsSetCreateProcessNotifyRoutine = 1;

}

確定了另一個回調例程:fn_CreateProcessNotifyRoutine。

0x03 fn_CreateProcessNotifyRoutine和fn_CreateProcessNotifyRoutineExImp

如果檢查fn_CreateProcessNotifyRoutineExImp和fn_CreateProcessNotifyRoutine,會注意到它們都是真實例程:

void __fastcall fn_CreateProcessNotifyRoutineExImp(PEPROCESS Process, __int64 ProcessId, PVOID CreateInfo)

{

if ( CreateInfo ) // If CreateInfo parameter is NULL, the specified process is exiting.

fn_Analyze_CreateProcessNotifyRoutine(ProcessId);

else

fn_Analyze_ExitProcessNotifyRoutine(ProcessId);

}

在文檔中解釋了PCREATE_PROCESS_NOTIFY_ROUTINE接收的參數,基於此,可以確定此函數如何確定是否由於創建或刪除進程而調用了回調。

fn_Analyze_CreateProcessNotifyRoutine和fn_Analyze_ExitProcessNotifyRoutine是我稍後將要分析的大函數。

0x04 fn_InitRegistrationNotifyAndCallbackRoutines

嘗試最後一次回調註冊:

ntStatus_1 = fn_RegisterCallbackFunction();

if ( ntStatus_1

{

if ( status_PsSetCreateProcessNotifyRoutineEx && fn_pPsSetCreateProcessNotifyRoutineEx )

fn_pPsSetCreateProcessNotifyRoutineEx(fn_CreateProcessNotifyRoutineExImp, 1u);// 2nd Parameter equal to 1 == remove

if ( status_PsSetCreateProcessNotifyRoutine )

{

LOBYTE(_RemoveRoutine) = 1;

PsSetCreateProcessNotifyRoutine(fn_CreateProcessNotifyRoutine, _RemoveRoutine);

}

goto label_exit;

}

但是,如果registerCallbackFunction失敗,則需要在離開之前刪除先前創建的NotifyRoutine,在這種情況下,他們將_RemoveRoutine設置為1,然後再次調用PsSetCreateProcessNotifyRoutine將其刪除,fn_RegisterCallbackFunction將被撤銷。

參考及來源:https://niemand.com.ar/2020/01/31/reversing-xigncode3-driver-part-4-1-registering-notify-and-callback-routines/

相關焦點

  • 熊貓燒香病毒逆向過程及其分析思路
    學習逆向也有段時間了,就想著找點東西練一下水平不高。找了個病毒分析一比較經典的病毒分析。我看網上有很多關於熊貓燒香病毒的分析,但都是側重於對病毒功能以及影響的總結,具體分析方法並未提及。本文主要側重於對熊貓燒香病毒逆向分析過程中的思路和方法的分享。
  • 《RT-Thread驅動框架分析》-Pin驅動
    還有網絡相關的WLAN驅動等。驅動分析API簡要說明RT-Thread的pin驅動為上層應用提供兩套不同的API,一套是對接設備驅動框架。一套是封裝好的API,用戶層可以直接使用。接下來我們來分析一下這兩套API的使用,以及實現。pin框架層次從上面的圖可以看出,對於不同晶片,用戶層的接口是統一的,而對於驅動層來說,只需要對接好相應的回調函數。
  • 嵌入式硬體通信接口協議-UART(四)設計起止式的應用層協議
    數據解析的前提是通信雙方都是用統一的數據幀格式,因此在這裡將設計一個簡單的起止式的數據幀格式,保證設備之間進行可靠的通信。現在的很多無線模塊,為了使用簡單和易於集成,模塊對外使用UART接口,並採用AT指令來完成配置和使用,常見的有ESP8266的WiFi模塊、HC-05藍牙串口模塊。
  • USB設備驅動程序
    既然還沒有"驅動程序",為何能知道是"android phone"答1. windows裡已經有了USB的總線驅動程序,接入USB設備後,是"總線驅動程序"知道你是"android phone" 提示你安裝的是"設備驅動程序"
  • RT-Thread傳感器設備驅動框架介紹
    這些傳感器,世界上的各大半導體廠商都有出產,雖然增加了市場的可選擇性,同時也加大了應用程式開發的難度。因為不同的傳感器廠商、不同的傳感器都需要配套自己獨有的驅動才能運轉起來,這樣在開發應用程式的時候就需要針對不同的傳感器做適配,自然加大了開發難度。為了降低應用開發的難度,增加傳感器驅動的可復用性,我們設計了 Sensor 驅動框架。
  • [系統安全] 二.如何學好逆向分析及呂布傳遊戲逆向案例
    系統安全系列作者將深入研究惡意樣本分析、逆向分析、攻防實戰等,通過在線筆記和實踐操作的形式分享與博友們學習,希望能與您一起進步。前文作者先帶領大家學習什麼是逆向分析;這篇文章將繼續普及逆向分析基礎知識,告訴大家如何學好逆向分析,並結合作者經驗給出逆向分析的路線推薦,最後給出呂布傳遊戲逆向案例。
  • 從串口驅動到Linux驅動模型,想轉Linux的必會!
    本文通過對Linux下串口驅動的分析。由最上層的C庫。到作業系統系統調用層的封裝。再到tty子系統的核心。再到一系列線路規程。再到最底層的硬體操作。對Linux中的tty子系統進行簡要的說明。從理論到實踐。以便讀者能對OS原理有更深入的了解和更具體的掌握。在具體分析之前。我們必須對串口。驅動。和Linux作業系統有一定的了解。
  • 2013年4月成人自考計算機基礎與程序設計真題
    1,y= 1 10.當a=1,b=3,c=5,d=4時,執行下面程序段後,x的值是( )if(a﹤b)if(c﹤d) x=1;else if(a﹤c) x=2;else   x=4; A.1 B.2 C.3 D.4 11.對do... while(表達式);結構的循環,下面說法正確的是
  • 利用EasyX圖像編程製作y=x^2函數圖像
    >line(-50,0,50,0);line(0,-100,0,100);moveto(x,-pow(x,2)/25);setlinecolor(RED);for(x=-50;x<=50;x++)
  • 逆向分析Cobalt Strike安裝後門
    非常適用於團隊作戰,Cobalt Strike集成了埠轉發、服務掃描,自動化溢出,多模式埠監聽,win exe木馬生成,win dll木馬生成,java木馬生成,office宏病毒生成,木馬捆綁;釣魚攻擊包括:站點克隆,目標信息獲取,java執行,瀏覽器自動攻擊等,至於Cobalt Strike詳細怎麼玩,我就不介紹了,網上很多教程,功能也很強大,我主要想從逆向的角度去分析一下Cobalt Strike
  • USB驅動開發的步驟及方法解析
    也就是說,獨立和分層是驅動實現方式上的分類,而本機和加載流式則是驅動模型上的分類。所謂本機驅動就是作業系統有保留專門的接口,而加載流式驅動是指編寫DLL文件導出各種流式接口函數的接口。 2. USB加載式流接口驅動要點分析 為了支持不同類型的外圍設備,WinCE平臺提供了具有定製接口的流接口驅動程序模型。
  • c++中槽函數 - CSDN
    它是一種函數回調機制,當一個信號關聯了多個槽時,信號發出,這些槽將會被調用,當然,也可以僅僅關聯一個槽函數。第一個模板參數Signature的含義和function相同,也是一個函數類型,表示signal調用的函數(槽,事件處理handler),例如:signal<void(int, double)>
  • Linux進程間通信的socketpair()函數
    所以,socketpair()這個函數就被master+worker型的多進程伺服器廣泛用於master和各個worker的通信。也可以用於子進程間的通信,因為第二個子進程被fork()時一樣要共享父進程的socketpair,只要子進程間規定好誰使用1,誰使用2就行。
  • 「異鬼Ⅱ」Bootkit木馬詳細分析
    5) 感染相關的代碼在dll中,木馬會在內存中展開感染模塊並加載執行,以下將其稱為infect.dll,該dll提供兩個導出函數,一個為BkInstall、BkTryWriteDisk,分別對應直接應用層寫磁碟和通過驅動寫磁碟的功能
  • 如何編寫基於ARM的裸機程序和基於Linux的驅動程序?
    ARM系列文章合集如下:《從0學arm合集》前言在嵌入式開發中,ADC應用比較頻繁,本文主要講解ADC的基本原理以及如何編寫基於ARM的裸機程序和基於Linux的驅動程序。便於加密處理信息傳輸的安全性和保密性越來越重要,數字通信的加密處理的比模擬通信容易得多,以話音信號為例,經過數字變換後的信號可用簡單的數字邏輯運算進行加密、解密處理。3.
  • 51單片機點亮一個燈和驅動蜂鳴器【程序+圖文】
    單片機編程的頭文件和這個意思一樣,它會對其中的「寄存器」等關鍵詞進行詳細的解釋。        下面就通過一些經典模塊,給大家展示一些入門的小程序^O^。真正的福利來啦!程序代碼如下所示。: delay(uint x) **返回:無 **函數功能描述:延時函數,延時大概x毫秒 **********************************************************/  voiddelay(uint x)            //延時函數,讓單片機空跑一段時間。
  • 逆向入門分析實戰(一)
    說明程序已經成功地判斷出重複運行了。1.2.逆向分析下面我們就對這個程序進行逆向分析,我們需要對主函數和子函數分別進行逆向分析,這次先分析主函數。學習彙編語言和逆向,我們完全沒有必要逐條指令去仔細閱讀所有的代碼,重要的是從整體上理解程序究竟做了哪些操作。彙編語言也是一種程式語言,平常大家也不會去一行一行地仔細閱讀別人寫的大量代碼,除了必須要理解的重要部分花時間仔細讀一讀,剩下的部分基本都是一帶而過,只要大體上理解程序在做什麼事就好了。
  • 高考加油,正弦函數的圖像和單調性
    典型例題分析1:已知直線y=m(0<m<2)與函數f(x)=2sin(ωx+φ)(ω>0)的圖象相鄰的三個交點依次為A(1,m),B(5,m),C(7,m),則ω=(  )A.π/3B.π/4C.π/2D.π/6
  • 了解常用加解密算法並簡單逆向識別
    5. 512位消息分組為16組 x 32位,進行4*16次運算逆向識別FindCrypt2和peid的KANA都是識別加法常數。消息填充和MD5一樣,512位為一組,然後16組x32位,然後擴充位80組x32位,進行4*20次運算。2.