萌新逆向學習筆記——CreateRemoteThread注入Shellcode

2021-02-16 看雪學院

本文為看雪論壇優秀文章

看雪論壇作者ID:psycongroo


筆者已經有一段時間沒發文了,說實話最近學習逆向沒勁兒,不知道是不是因為天氣總是變化無常,人感覺有點疲憊。之前一直在看韓國人寫的《逆向工程核心原理》,但總感覺缺了點什麼,於是乎買了本《加密與解密》。總體上來說看到現在給我的感覺就是,很難,似乎較為注重理論知識。與《逆向工程核心原理》一章好幾個實踐不同,《加密與解密》前面用了大部分章節去介紹諸如動靜分析技術,加密算法,window內核等基礎理論知識。較為後面才有HOOK,注入等偏實踐的內容。即便有實踐其過程也沒有《逆向工程核心原理》那麼詳細和細緻。所以個人認為,剛入門還是先看《逆向工程核心原理》,根據上面的內容去實踐,熟悉Win32的開發和一些常用的API,有了基礎再去看《加密與解密》。不然就可能會和筆者一樣,被《加密與解密》其前面龐大的基礎理論內容搞得暈頭轉向, 喪失一定的興趣和耐心。回到這次的主題上,這次的內容是使用CreateRemoteThread的方法在目標進程中運行自己寫的Shellcode,大部分內容是效仿《加密與解密》中的一些代碼。雖然《逆向工程核心原理》中也有提到,但是當時筆者沒怎麼留意,一些寫Shellcode的便捷方法書裡也沒提到,所以當時也就放棄了。在看到《加密與解密》中用了一些較為人性的方法寫Shellcode時,於是便想試試,才有了這篇文章。
在上一篇文章中,筆者介紹了使用CreateRemoteThread去迫使其他進程執行LoadLibrary並加載我們自定義的DLL以達到目的。而事實上只要我們定義的函數的模板符合CreateRemoteThread參數中定義的函數模板,就能通過遠程線程的方式去執行它,而不僅限於LoadLibrary。
DWORD WINAPI ThreadProc(  _In_ LPVOID lpParameter);

所以我們只要定義只有一個參數的函數,把它轉換成LPTHREAD_START_ROUTINE(這是一個指向上面模板的指針)就可以了。可問題來了,參數只有一個,萬一我的函數用了若干個參數呢?而且這個參數必須位於目標進程的虛擬內存當中,否則是無法使用的。對於第二個問題,我們可以使用VirtualAllocEx函數向目標申請內存虛擬空間。對於第一個問題,我們可以構建一個結構體,存放所有的參數,然後在調用的時候通過內存偏移來訪問參數:
typedef struct _INJECT_DATA{    char lpText[8];      char lpCaption[8];  }INJECT_DATA;

那有沒有更加方便快捷的方法呢?結合上面兩點就產生了本篇文章的主旨內容Shellcode。有人說Shellcode僅限於Linux和unix,而經百度似乎也有很多種說法。個人理解是一段被注入後可獨立運行,依賴性少或完全沒依賴的代碼。為了撇開爭論,暫且保持書上所說的那樣,稱之為Shellcode。說的直白一點,其實就是把參數,要運行的函數地址全部放到結構體裡,最後以一段用彙編編寫的Shellcode運行起來。以調用MessageBoxA彈出窗口的Shellcode為示例。首先是存放Shellcode和參數,函數地址的結構體:
typedef struct _INJECT_DATA{    BYTE shellCode[0x1D];      char lpText[8];      char lpCaption[8];      LPVOID lpThreadStart;  }INJECT_DATA;

__declspec (naked)void shellCodeFun(void) {    __asm {        call L001;         L001:        pop ebx;        //sub ebx, 5;        and bx, 0;        push 0;        lea esi, dword ptr ds : [ebx]INJECT_DATA.lpCaption ;  //0x25注意偏移量        push esi;        lea esi, dword ptr ds : [ebx]INJECT_DATA.lpText ;  //0x1D        push esi;        push 0;        call dword ptr ds : [ebx]INJECT_DATA.lpThreadStart ;  //0x2D        ret;    }}

最後把整個結構體通過CreateRemoteThread注入即可。因為結構體的起始地址就是Shellcode的起始地址,因此注入後Shellcode就會運行起來。


步驟一定義和初始化:定義結構體和編寫Shellcode,當然這些都是根據自己的需求來具體定義的,在這裡同樣以彈框來做示例(Shellcode代碼在上面,這裡不贅述):
   INJECT_DATA data;    ZeroMemory(&data, sizeof(INJECT_DATA));    PBYTE pShellCode = (PBYTE)shellCodeFun;#ifdef DEBUG    if (pShellCode[0] == 0xE9)    {                        pShellCode = pShellCode + *(ULONG*)(pShellCode + 1) + 5;    }#endif     memcpy(data.shellCode, pShellCode, sizeof(data.shellCode));    char text[] = "message";    char title[] = "title";    memcpy(data.lpText, text, 8);    memcpy(data.lpCaption, title, 8);          HMODULE hmod = GetModuleHandleA("user32.dll");    FARPROC dialog = GetProcAddress(hmod, "MessageBoxA");    data.lpThreadStart = dialog;

1. 結構體需要初始化,用諸如ZeroMemory的函數來進行初始化,不然會報錯。
call L001;  //自己是為了自定位,為了拿到Shellcode開始的地址,也即是結構體的地址。 L001:pop ebx;  //拿出call時保存的返回地址and bx, 0;  //因為使用VirtualAllocEx函數申請的虛擬內存都是64kb對齊的,只要低地址。push 0;lea esi, dword ptr ds : [ebx]INJECT_DATA.lpCaption ;....

3. 注入進程是debug版本,需要對Shellcode地址做額外處理:
#ifdef DEBUG if (pShellCode[0] == 0xE9) {               pShellCode = pShellCode + *(ULONG*)(pShellCode + 1) + 5; }#endif 


步驟二申請內存並寫入:通過VirtualAllocEx申請虛擬內存,通過WriteProcessMemory寫入虛擬內存:
int injectSize = sizeof(INJECT_DATA);PBYTE mBuffer = (PBYTE)VirtualAllocEx(mProcess, NULL, injectSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);  WriteProcessMemory(mProcess, mBuffer, &data, injectSize, NULL)

步驟三建立遠程線程執行:
mRemoteThread = CreateRemoteThread(mProcess, NULL, 0, (LPTHREAD_START_ROUTINE)mBuffer, NULL, 0, NULL);


第一次寫Shellcode,雖然看上去簡單,但卻遇到了很多問題。例如什麼是Shellcode中的自定位,Shellcode中如何使用參數,如何使用調試器調試等等。說實話這次將MessageBox作為示例,但個人感覺似乎不太妥當。因為只有當目標程序加載了User32.dll,這段Shellcode才能生效。也就是說這裡的Shellcode對User32.dll這個庫產生了依賴。或許可以在ntdll.dll中找到與之對應的函數,調用這個才更符合Shellcode無依賴的定義吧。Shellcode的應用很廣泛,粗略看了《加密與解密》,書中介紹到很多注入都是編寫Shellcode並利用形同如CreateRemoteThread函數能執行一段函數的特性來實現的。所以以後凡是看到一個函數能調用其另一個函數,就有這種注入的可能。


第一條:使用調試器調試這條才是最重要的,因為所有的疑問都能在調試器裡找到答案。為了調試遠程線程,首先我們在visual studio中對CreateRemoteThread打上斷點:然後用ollydbg或者x32dbg附加目標程序,隨後設置線程開始斷點:再然後運行我們的注入程序,程序會在創建遠程線程的地方斷點:同時記錄我們調用VirtualAllocEx時返回的內存地址,這是我們Shellcode所在的目標進程的地址。在調試器中跳到這個地址,然後我們就能看到自己寫的Shellcode:當然我們來這裡不是為了旅遊的,打下斷點,並且讓visual studio中的斷點繼續運行,調試器會在線程開始的地方斷下(因為我們設置過):其實只要找到Shellcode地址下斷就行了,並不需要在olldbg/32xdbg中設置線程開始的斷點。只不過這樣做是為了更直觀的看到線程的工作流程。
第二條Shellcode自定位為什麼要用到Shellcode自定位,因為我們要確定Shellcode在目標程序中的虛擬內存地址。為什麼要用到這個地址,因為我們Shellcode中用到的參數,如文中彈窗標題,內容等,都需要通過這個地址來尋找。為什麼能通過Shellcode地址定位到我們參數的地址?因為我們的Shellcode和參數都是放到同一個結構體中的,而結構體裡的成員地址都是連續的。結構體的首地址便是結構體中定義的第一個成員的地址(文中的是Shellcode)。使用Call會將Call下一條的地址入棧,隨後使用pop拿出來這條地址,通過低16位and運算置0(書上說的是因為使用VirtualAllocEx申請的內存大小是64kb對齊,說實話我也沒搞懂),或者sub減去5(Call 地址指令的大小),就能拿到基址:這樣,我們就可以通過基址+Shellcode大小+偏移量拿到參數,也可以通過dword ptr ds : [ebx]INJECT_DATA.lpCaption這樣的方法拿到參數。
第三條debug版本中Shellcode地址處理當我們的注入程序是debug版本,在把Shellcode寫入結構體前,需要對Shellcode的地址進行處理:
#ifdef DEBUG    if (pShellCode[0] == 0xE9)    {                        pShellCode = pShellCode + *(ULONG*)(pShellCode + 1) + 5;    }#endif 

這是因為處於debug版本的注入程序下,會在跳到真正的函數地址前多出一個jmp指令的中間層:如果此時沒有對地址進行處理,本來應該複製Shellcode的代碼到結構體成員當中,現在卻複製了 jmp xxxx代碼,從而造成錯誤。而處理的方法就是將當前指向jmp代碼的地址加上jmp後面的地址,最後在加上整個jmp指令的長度。真正的Shellcode地址=006E180C(當前指向jmp的地址)+9EFF(jmp指令後面的地址數據)+5(jmp指令長度)=6EB710。造成這種現象是因為debug版本下編譯器並沒有進行優化,使得函數的調用都有一個jmp表,如上圖。而十六進位為E9的jmp為近距離跳轉,也就是說jmp後面跟的地址是相對於這條jmp指令的相對地址,也叫偏移。因此,兩者相加,最後加上指令本身的長度,就能獲得原地址了。

附件中的源程序包含之前文章DLL注入代碼,可無視,輸入進程PID後,按1即可進行Shellcode注入。https://share.weiyun.com/AhueFz5l

看雪ID:psycongroo

https://bbs.pediy.com/user-home-899080.htm

  *本文由看雪論壇 psycongroo 原創,轉載請註明來自看雪社區。

  掃碼購票立享2.5折優惠!

  十大乾貨議題收入囊中

相關焦點

  • 簡單shellcode學習
    引言之前遇到沒開啟NX保護的時候,都是直接用pwtools庫裡的shellcode一把梭,也不太懂shellcode代碼具體做了些什麼,遇到了幾道不能一把梭的題目
  • 編寫Windows Kernel Shellcode
    網上流傳的永恆之藍漏洞利用代碼中,關於內核shellcode部分,大部分都是利用APC注入的。
  • 惡意代碼分析之APC進程注入學習
    將 shellcode 的16進位數據 copy 到 ollydbg 工具的反彙編窗口中,即可完成。例如在 ollydbg 中打開任意一個 exe 文件,在反彙編窗口中選中足夠的行數,滑鼠右擊-二進位-二進位粘貼。經測試這段 shellcode 的作用是會彈出計算器,如下。
  • 萌新逆向學習筆記——遠程線程注入DLL
    本文為看雪論壇精華文章看雪論壇作者ID:psycongroo今天的主題——遠程線程注入
  • CTF必備技能丨Linux Pwn入門教程——ShellCode
    我們通過兩個例子分別學習一下如何使用工具和手工對ShellCode進行變形。/usr/bin/python#coding:utf-8from pwn import *context.update(arch = 'amd64', os = 'linux', timeout = 1)   io = remote('172.17.0.3', 10001)   shellcode = "\x48\x31\xd2
  • 利用miasm解析shellcode(一)
    本文轉載自【微信公眾號:MicroPest,ID:gh_696c36c5382b】前面介紹了一些不同類型的逆向方法過程,這裡再介紹個shellcode的解析;在freebuf上看了利用miasm解析shellcode的文,覺得非常有意思,官網的推薦是這樣的:「miasm 是一個可以做動態及靜態分析的框架,支持很多處理器之外,也可以 load Windows
  • 一步步學寫Windows下的Shellcode
    為什麼會問這個問題,前段時間在做win下的Exploit,但是都是使用大佬寫的shellcode,無法實現個人的一些需求。而網絡上編寫shellcode的教程大多是關於Linux的,加之順帶學習PE文件結構,所以打算寫一篇關於Windows 下shellcode的編寫,為要編寫Shellcdoe的讀者提供一些參考。
  • VS Code Remote 發布!開啟遠程開發新時代
    北京時間 2019 年 5 月 3 日,在 PyCon 2019 大會上,微軟發布了 VS Code Remote,開啟了遠程開發的新時代!▲ Remote SSH 擴展運行截圖遠程開發例如,假設你正在開展深度學習項目。您通常需要一個高GPU性能的虛擬機(例如 Azure Data Science Virtual Machine),配置了訓練大數據模型所需的所有工具和框架。
  • 工具Pe2shc (Pe to Shellcode) 的源碼級分析
    導讀:在本篇中,我將會對到由C++和彙編組合而成的源碼級工具進行分析講解,學習到如何將EXE轉化成Shellcode
  • CVE-2020-7699:NodeJS模塊代碼注入
    使用這種設計漏洞的原型攻擊通過注入不相適應的對象類型到現有的對象中來引發錯誤,導致DoS攻擊。 利用現有原型的攻擊 由於express-fileupload 提供的"parseNested"特徵使得現實的攻擊成為可能。
  • 逆向分析Cobalt Strike安裝後門
    逆向跟蹤分析 1.拼接字符串,如下所示: 3.通過VirtualAlloc分配內存空間,解密shellcode代碼,如下所示:
  • 編寫Shellcode:尋找EIP/RIP
    (OSCE)(連結:https://www.offensive-security.com/ctp-osce/)認證時,我花費了大量的時間用於研究如何編寫自定義shellcode。本文是我計劃要發布的一系列博文裡的第一篇,這個系列詳細介紹我在準備認證過程中所學習到的技術。本文的重點是描述如何找到EIP/RIP以及找到後做什麼。OSCE專注於32位系統,作為繼續學習的一部分,我會研究並記錄下在64位系統上的方法。這超出了OSCE的需求,但這是我繼續學習過程的一部分。 在本文中,我不會介紹到所有的可能找到EIP和RIP的方法。
  • shellcode編寫指南
    編寫shellcode3.1 c++庫文件配合內聯彙編先來針對指定系統的shellcode的編寫,指定系統的,我們首先通過LoadLibraryA函數導入相應的dll文件,獲得一個dll句柄,在把這個dll句柄當作參數傳入GetProcAddress 搜索查找指定函數,返回該函數的地址,然後通過函數的地址來調用函數,用c++代碼內聯彙編實現
  • 滲透測試學習筆記之案例一
    因而決定從今天起開始寫一些跟滲透測試相關的文章,也可以認為是學習筆記吧,留作日後的技術積累和參考吧。All rights reserved.C:\Windows\system32>whoamiwhoamint authority\system後滲透利用:在上一步中我們成功地從IP:10.11.1.220上反彈了一個shell回來,但很顯然這不是一個完美的交互式的shell且不穩定可靠,那麼接下來我們該怎麼辦呢?
  • RT-Thread AT 組件應用筆記 - 客戶端篇
    AT Socket 配置與使用2 問題闡述本應用筆記將圍繞下面幾個問題來介紹 RT-Thread AT 組件。3.2.1AT Client 配置1.下載RT-Thread 源碼2.下載env 工具3.開啟 env 工具,進入 rt-thread\bsp\stm32f4xx-HAL 目錄,在 env 命令行輸入 menuconfig 進入配置界面配置工程。
  • 滲透中利用postgresql getshell及注入技巧
    1.為了方便大家日後更好的學習安全,小編為此專門建立了一個氛圍良好的學習圈子。
  • CVE-2016-4622 Webkit slice學習筆記
    PwnRabb1t原創的文章,關於CVE-2016-4622 Webkit slice的一篇學習筆記,文章篇幅較長,閱讀約15分鐘,文章未經許可禁止轉載!有了任意地址讀寫,接下來就是如何getshell的問題了,我們可以寫wasm和JIT來執行shellcode。
  • Battleye 系列翻譯之shellcode更新
    —最近在學習遊戲安全相關內容,然後我發現,在遊戲破解,數據分析,邏輯逆向方面的文章,前輩們寫的很多,有許多可以學習的地方了
  • 利用WinRM實現內網無文件攻擊反彈shell
    四、在(win7)中放置木馬,並且用(kali)接收反彈的shell1、生成shellcode3、kali啟動監聽shell模式五、利用(server2016)的WinRM 實現內網無文件攻擊反彈shell1、在(server2016)中執行以下命令winrm
  • 如何編寫shellcode查找EIP & RIP
    我在學習OSCE認證時,花費了大量時間研究如何編寫自定義shellcode。這是我打算發布的一系列博客文章中的第一篇,其中將詳細介紹我在此過程中學到的一些技術。這篇博客文章的重點將是描述如何找到EIP / RIP,以及找到它可以做什麼。OSCE將精力集中在32位系統上,作為我繼續學習的一部分,我將研究和記錄適用於64位系統的方法。