mirror of https://github.com/infosecn1nja/C3.git
Add backend support for shellcode generation using Donut
parent
08c3087aa7
commit
69fa74bae6
|
@ -0,0 +1,209 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// https://github.com/TheWover/donut/blob/master/lib/donut.h
|
||||||
|
|
||||||
|
namespace MWR.C3.WebController.Comms
|
||||||
|
{
|
||||||
|
public class DonutLibrary
|
||||||
|
{
|
||||||
|
public enum ErrorCode
|
||||||
|
{
|
||||||
|
SUCCESS = 0,
|
||||||
|
FILE_NOT_FOUND = 1,
|
||||||
|
FILE_EMPTY = 2,
|
||||||
|
FILE_ACCESS = 3,
|
||||||
|
FILE_INVALID = 4,
|
||||||
|
NET_PARAMS = 5,
|
||||||
|
NO_MEMORY = 6,
|
||||||
|
INVALID_ARCH = 7,
|
||||||
|
INVALID_URL = 8,
|
||||||
|
URL_LENGTH = 9,
|
||||||
|
INVALID_PARAMETER = 10,
|
||||||
|
RANDOM = 11,
|
||||||
|
DLL_FUNCTION = 12,
|
||||||
|
ARCH_MISMATCH = 13,
|
||||||
|
DLL_PARAM = 14,
|
||||||
|
BYPASS_INVALID = 15,
|
||||||
|
NORELOC = 16,
|
||||||
|
INVALID_ENCODING = 17,
|
||||||
|
INVALID_ENGINE = 18,
|
||||||
|
COMPRESSION = 19,
|
||||||
|
INVALID_ENTROPY = 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
// target architecture
|
||||||
|
public enum Architecture
|
||||||
|
{
|
||||||
|
ANY = -1, // just for vbs,js and xsl files
|
||||||
|
X86 = 1, // x86
|
||||||
|
X64 = 2, // AMD64
|
||||||
|
X84 = 3, // AMD64 + x86
|
||||||
|
}
|
||||||
|
|
||||||
|
// module type
|
||||||
|
public enum ModuleType
|
||||||
|
{
|
||||||
|
NET_DLL = 1, // .NET DLL. Requires class and method
|
||||||
|
NET_EXE = 2, // .NET EXE. Executes Main if no class and method provided
|
||||||
|
DLL = 3, // Unmanaged DLL, function is optional
|
||||||
|
EXE = 4, // Unmanaged EXE
|
||||||
|
VBS = 5, // VBScript
|
||||||
|
JS = 6, // JavaScript or JScript
|
||||||
|
}
|
||||||
|
|
||||||
|
// format type
|
||||||
|
public enum OutputFormat
|
||||||
|
{
|
||||||
|
BINARY = 1,
|
||||||
|
BASE64 = 2,
|
||||||
|
RUBY = 3,
|
||||||
|
C = 4,
|
||||||
|
PYTHON = 5,
|
||||||
|
POWERSHELL = 6,
|
||||||
|
CSHARP = 7,
|
||||||
|
HEX = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// compression engine
|
||||||
|
public enum ComperssionEngine
|
||||||
|
{
|
||||||
|
NONE = 1,
|
||||||
|
LZNT1 = 2, // COMPRESSION_FORMAT_LZNT1
|
||||||
|
XPRESS = 3, // COMPRESSION_FORMAT_XPRESS
|
||||||
|
XPRESS_HUFF = 4, // COMPRESSION_FORMAT_XPRESS_HUFF
|
||||||
|
}
|
||||||
|
|
||||||
|
// entropy level
|
||||||
|
public enum EntropyLevel
|
||||||
|
{
|
||||||
|
NONE = 1, // don't use any entropy
|
||||||
|
RANDOM = 2, // use random names
|
||||||
|
DEFAULT = 3, // use random names + symmetric encryption
|
||||||
|
}
|
||||||
|
|
||||||
|
// misc options
|
||||||
|
public enum ExitOpt
|
||||||
|
{
|
||||||
|
EXIT_THREAD = 1, // return to the caller which calls RtlExitUserThread
|
||||||
|
EXIT_PROCESS = 2, // call RtlExitUserProcess to terminate host process
|
||||||
|
}
|
||||||
|
// instance type
|
||||||
|
public enum InstanceType
|
||||||
|
{
|
||||||
|
PIC = 1, // Self-contained
|
||||||
|
URL = 2, // Download from remote server
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMSI/WLDP options
|
||||||
|
public enum AmsiWldpBypass
|
||||||
|
{
|
||||||
|
NONE = 1, // Disables bypassing AMSI/WDLP
|
||||||
|
ABORT = 2, // If bypassing AMSI/WLDP fails, the loader stops running
|
||||||
|
CONTINUE = 3, // If bypassing AMSI/WLDP fails, the loader continues running
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Constants
|
||||||
|
{
|
||||||
|
public const int DONUT_MAX_NAME = 256; // maximum length of string for domain, class, method and parameter names
|
||||||
|
public const int DONUT_MAX_DLL = 8; // maximum number of DLL supported by instance
|
||||||
|
public const int DONUT_MAX_URL = 256;
|
||||||
|
public const int DONUT_MAX_MODNAME = 8;
|
||||||
|
public const int DONUT_SIG_LEN = 8; // 64-bit string to verify decryption ok
|
||||||
|
public const int DONUT_VER_LEN = 32;
|
||||||
|
public const int DONUT_DOMAIN_LEN = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 0)]
|
||||||
|
public struct DonutConfig
|
||||||
|
{
|
||||||
|
public int arch;
|
||||||
|
public int bypass;
|
||||||
|
public int compress; // engine to use when compressing file via RtlCompressBuffer
|
||||||
|
public int entropy; // entropy/encryption level
|
||||||
|
public int fork; // fork/create a new thread for the loader
|
||||||
|
public int format; // output format for loader
|
||||||
|
public int exit_opt; // return to caller or invoke RtlExitUserProcess to terminate the host process
|
||||||
|
public int thread; // run entrypoint of unmanaged EXE as a thread. attempts to intercept calls to exit-related API
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string input;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string output;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string runtime;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string domain;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string cls;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string method;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string param;
|
||||||
|
public int ansi;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string url;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
|
||||||
|
public string modname;
|
||||||
|
public int mod_type;
|
||||||
|
public int mod_len;
|
||||||
|
public IntPtr mod; // points to DONUT_MODULE
|
||||||
|
// DONUT_INSTANCE
|
||||||
|
public int inst_type; // DONUT_INSTANCE_PIC or DONUT_INSTANCE_URL
|
||||||
|
public int inst_len; // size of DONUT_INSTANCE
|
||||||
|
public IntPtr inst; // points to DONUT_INSTANCE
|
||||||
|
public int pic_len; // size of loader/shellcode
|
||||||
|
public IntPtr pic; // points to loader/shellcode
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GenerateShellcode(DonutConfig config)
|
||||||
|
{
|
||||||
|
var buffer = DonutCreate(config);
|
||||||
|
DonutDelete(config);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use DllImport to import the Win32 MessageBox function.
|
||||||
|
[DllImport("donut")]
|
||||||
|
private static extern int DonutCreate(ref DonutConfig donutConfig);
|
||||||
|
|
||||||
|
public class DonutException : Exception
|
||||||
|
{
|
||||||
|
public DonutException(string message) : base(message) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("donut")]
|
||||||
|
private static extern int DonutDelete(ref DonutConfig donutConfig);
|
||||||
|
|
||||||
|
[DllImport("donut")]
|
||||||
|
private static extern IntPtr DonutError(int errorcode);
|
||||||
|
|
||||||
|
static string GetDonutError(ErrorCode errorcode)
|
||||||
|
{
|
||||||
|
IntPtr ptr = DonutError((int)errorcode);
|
||||||
|
return Marshal.PtrToStringAnsi(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] DonutCreate(DonutConfig config)
|
||||||
|
{
|
||||||
|
var createErrorCode = (ErrorCode)DonutCreate(ref config);
|
||||||
|
if (createErrorCode != ErrorCode.SUCCESS)
|
||||||
|
{
|
||||||
|
var errorMessage = GetDonutError(createErrorCode);
|
||||||
|
throw new DonutException($"Donut error: {errorMessage}");
|
||||||
|
}
|
||||||
|
var buffer = new byte[config.pic_len];
|
||||||
|
Marshal.Copy(config.pic, buffer, 0, config.pic_len);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DonutDelete(DonutConfig config)
|
||||||
|
{
|
||||||
|
var delErrorCode = (ErrorCode)DonutDelete(ref config);
|
||||||
|
if (delErrorCode != ErrorCode.SUCCESS)
|
||||||
|
{
|
||||||
|
var errorMessage = GetDonutError(delErrorCode);
|
||||||
|
throw new DonutException($"Donut error: {errorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MWR.C3.WebController.Models;
|
||||||
|
using MWR.C3.WebController.RandomExtentions;
|
||||||
|
|
||||||
|
namespace MWR.C3.WebController.Comms
|
||||||
|
{
|
||||||
|
public class DonutService : IDonutService
|
||||||
|
{
|
||||||
|
public DonutService(IOptions<DonutServiceOptions> options)
|
||||||
|
{
|
||||||
|
m_Options = options.Value;
|
||||||
|
m_TempPath = options.Value.Tempdir ??
|
||||||
|
Environment.GetEnvironmentVariable("TEMPDIR") ??
|
||||||
|
Environment.GetEnvironmentVariable("TEMP") ??
|
||||||
|
".";
|
||||||
|
}
|
||||||
|
|
||||||
|
private DonutServiceOptions m_Options;
|
||||||
|
private string m_TempPath;
|
||||||
|
|
||||||
|
public byte[] GenerateShellcode(byte[] payload, DonutRequest request, Build.Architecture arch, Build.BinaryType binaryType)
|
||||||
|
{
|
||||||
|
if (!m_Options.Enable)
|
||||||
|
throw new Exception("Donut service disabled");
|
||||||
|
|
||||||
|
// donut api requires files
|
||||||
|
var rand = new Random();
|
||||||
|
var tmpFilename = rand.NextString(16);
|
||||||
|
var tmpPayloadFile = Path.Combine(m_TempPath, tmpFilename + ".exe");
|
||||||
|
var tmpDonutFile = Path.Combine(m_TempPath, tmpFilename + ".donut");
|
||||||
|
|
||||||
|
WriteToFile(payload, tmpPayloadFile);
|
||||||
|
|
||||||
|
var config = new DonutLibrary.DonutConfig
|
||||||
|
{
|
||||||
|
arch = (int)(arch == Build.Architecture.X64 ? DonutLibrary.Architecture.X64 : DonutLibrary.Architecture.X86),
|
||||||
|
mod_type = (int)(binaryType == Build.BinaryType.Exe ? DonutLibrary.ModuleType.EXE : DonutLibrary.ModuleType.DLL),
|
||||||
|
format = (int)request.format,
|
||||||
|
compress = (int)request.compress,
|
||||||
|
entropy = (int)request.entropy,
|
||||||
|
inst_type = (int)DonutLibrary.InstanceType.PIC,
|
||||||
|
input = tmpPayloadFile,
|
||||||
|
output = tmpDonutFile,
|
||||||
|
bypass = (int)request.bypass,
|
||||||
|
inst_len = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DonutLibrary.GenerateShellcode(config);
|
||||||
|
return File.ReadAllBytes(tmpDonutFile);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CleanupFile(tmpPayloadFile);
|
||||||
|
CleanupFile(tmpDonutFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteToFile(byte[] payload, string filename)
|
||||||
|
{
|
||||||
|
using (var binWriter = new BinaryWriter(File.Open(filename, FileMode.Create)))
|
||||||
|
{
|
||||||
|
binWriter.Write(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CleanupFile(string filename)
|
||||||
|
{
|
||||||
|
if (File.Exists(filename))
|
||||||
|
File.Delete(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
using MWR.C3.WebController.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MWR.C3.WebController.Comms
|
||||||
|
{
|
||||||
|
public interface IDonutService
|
||||||
|
{
|
||||||
|
byte[] GenerateShellcode(byte[] payload, DonutRequest request, Build.Architecture arch, Build.BinaryType binaryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DonutServiceOptions
|
||||||
|
{
|
||||||
|
public bool Enable { get; set; }
|
||||||
|
public string Tempdir { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,8 +47,11 @@ namespace MWR.C3.WebController.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("customize")]
|
[HttpPost("customize")]
|
||||||
public async Task<IActionResult> Customize([FromBody]RelayBuildRequest request, [FromServices] ICustomizer customizer)
|
public async Task<IActionResult> Customize([FromBody]RelayBuildRequest request, [FromServices] ICustomizer customizer, [FromServices] IDonutService donut)
|
||||||
{
|
{
|
||||||
|
if (request is null)
|
||||||
|
return BadRequest("Failed to read RelayBuildRequest");
|
||||||
|
|
||||||
RelayBuild newBuild = null;
|
RelayBuild newBuild = null;
|
||||||
System.Action cleanupBuild = () =>
|
System.Action cleanupBuild = () =>
|
||||||
{
|
{
|
||||||
|
@ -73,8 +76,10 @@ namespace MWR.C3.WebController.Controllers
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
}
|
}
|
||||||
output = await customizer.CustomizeNodeRelay(newBuild);
|
output = await customizer.CustomizeNodeRelay(newBuild);
|
||||||
|
if (request.Donut != null)
|
||||||
|
output = donut.GenerateShellcode(output, request.Donut, request.Architecture, request.Type);
|
||||||
var buildName = String.IsNullOrEmpty(newBuild.Name) ? "" : $"_{newBuild.Name}";
|
var buildName = String.IsNullOrEmpty(newBuild.Name) ? "" : $"_{newBuild.Name}";
|
||||||
return File(output, "application/octet-stream", $"Relay_{newBuild.Arch.ToString().ToLower()}_{newBuild.BuildId:x}{buildName}.{newBuild.Type.ToString().ToLower()}");
|
return File(output, "application/octet-stream", $"Relay_{newBuild.Arch.ToString().ToLower()}_{newBuild.BuildId:x}{buildName}.{GetBuildExtention(request)}");
|
||||||
}
|
}
|
||||||
catch (TimeoutException e)
|
catch (TimeoutException e)
|
||||||
{
|
{
|
||||||
|
@ -95,7 +100,7 @@ namespace MWR.C3.WebController.Controllers
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
cleanupBuild();
|
cleanupBuild();
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError , $"Unknown error. {e.Message}");
|
return StatusCode(StatusCodes.Status500InternalServerError, $"Unknown error. {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,5 +122,22 @@ namespace MWR.C3.WebController.Controllers
|
||||||
ParentGatwayAgentId = gatewayAgentId,
|
ParentGatwayAgentId = gatewayAgentId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetBuildExtention(RelayBuildRequest request)
|
||||||
|
{
|
||||||
|
switch (request.Donut?.format)
|
||||||
|
{
|
||||||
|
case DonutLibrary.OutputFormat.BINARY: return "bin";
|
||||||
|
case DonutLibrary.OutputFormat.BASE64: return "b64";
|
||||||
|
case DonutLibrary.OutputFormat.RUBY: return "rb";
|
||||||
|
case DonutLibrary.OutputFormat.C: return "c";
|
||||||
|
case DonutLibrary.OutputFormat.PYTHON: return "py";
|
||||||
|
case DonutLibrary.OutputFormat.POWERSHELL: return "ps1";
|
||||||
|
case DonutLibrary.OutputFormat.CSHARP: return "cs";
|
||||||
|
case DonutLibrary.OutputFormat.HEX: return "hex";
|
||||||
|
case null: return request.Type.ToString().ToLower();
|
||||||
|
default: throw new ArgumentException("Unrecognized output format");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MWR.C3.WebController.RandomExtentions
|
namespace MWR.C3.WebController.RandomExtentions
|
||||||
{
|
{
|
||||||
|
@ -24,5 +25,12 @@ namespace MWR.C3.WebController.RandomExtentions
|
||||||
self.NextBytes(buffer);
|
self.NextBytes(buffer);
|
||||||
return BitConverter.ToUInt64(buffer);
|
return BitConverter.ToUInt64(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string NextString(this Random self, int length)
|
||||||
|
{
|
||||||
|
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
return new string(Enumerable.Repeat(chars, length)
|
||||||
|
.Select(s => s[self.Next(s.Length)]).ToArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,34 @@ namespace MWR.C3.WebController.Models
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public JArray StartupCommands { get; set; }
|
public JArray StartupCommands { get; set; }
|
||||||
|
|
||||||
|
public DonutRequest Donut { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DonutRequest
|
||||||
|
{
|
||||||
|
public DonutRequest()
|
||||||
|
{
|
||||||
|
format = DonutLibrary.OutputFormat.BINARY;
|
||||||
|
compress = DonutLibrary.ComperssionEngine.NONE;
|
||||||
|
entropy = DonutLibrary.EntropyLevel.DEFAULT;
|
||||||
|
exitOpt = DonutLibrary.ExitOpt.EXIT_THREAD;
|
||||||
|
bypass = DonutLibrary.AmsiWldpBypass.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public DonutLibrary.OutputFormat format { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public DonutLibrary.ComperssionEngine compress { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public DonutLibrary.EntropyLevel entropy { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public DonutLibrary.ExitOpt exitOpt { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public DonutLibrary.AmsiWldpBypass bypass { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"IIS Express": {
|
"IIS Express": {
|
||||||
"commandName": "IISExpress",
|
"commandName": "IISExpress",
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ namespace MWR.C3.WebController
|
||||||
{
|
{
|
||||||
c.SwaggerDoc("v1", new Info { Title = "C3 API", Version = "v1" });
|
c.SwaggerDoc("v1", new Info { Title = "C3 API", Version = "v1" });
|
||||||
});
|
});
|
||||||
|
services.Configure<DonutServiceOptions>(Configuration.GetSection("Donut"));
|
||||||
|
services.AddTransient<IDonutService, DonutService>();
|
||||||
services.AddTransient<ICustomizer, Customizer>();
|
services.AddTransient<ICustomizer, Customizer>();
|
||||||
services.AddScoped<GatewayResponseProcessor>();
|
services.AddScoped<GatewayResponseProcessor>();
|
||||||
services.AddDbContext<C3WebAPIContext>(options => options.UseSqlite("Data Source=C3API.db"));
|
services.AddDbContext<C3WebAPIContext>(options => options.UseSqlite("Data Source=C3API.db"));
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DonutRuntimes" Version="0.9.3-7ae213b.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"IncludeScopes": false,
|
"IncludeScopes": false,
|
||||||
"Debug": {
|
"Debug": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Warning"
|
"Default": "Warning"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Console": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Console": {
|
"Customizer": {
|
||||||
"LogLevel": {
|
"payloadTemplateDir": "../Bin/"
|
||||||
"Default": "Warning"
|
},
|
||||||
}
|
"ApiBridge": {
|
||||||
|
"Host": "127.0.0.1",
|
||||||
|
"Port": 2323
|
||||||
|
},
|
||||||
|
"Donut": {
|
||||||
|
"enable": true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Customizer": {
|
|
||||||
"payloadTemplateDir": "../Bin/"
|
|
||||||
},
|
|
||||||
"ApiBridge": {
|
|
||||||
"Host": "127.0.0.1",
|
|
||||||
"Port": 2323
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue