-
-
Notifications
You must be signed in to change notification settings - Fork 28
Quick start
Conari project welcomes you and wants to tell a little about its architecture, components, features, and just where to start. π‘
Conari is officially distributed as a NuGet package
Just follow the installation instruction from the NuGet page. For example, using dotnet
dotnet add package Conari
First of all, Conari provides several ways to handle your requests to unmanaged memory, to processes, or types, etc. You can use freely everything at the same time without configuring something. Just choose what you need at the moment on the fly and enjoy.
ConariL represents the main IConari implementation for semi-automatic processing known as a lambda way. Supports .dll and .exe modules.
using ConariL l = new(...);
//...
l.bind<Func<TCharPtr, string, string, bool>>("replace")(...);
But through IDlrAccessor you can easily access also DLR.
using ConariL l = new(...);
//...
l._.replace<bool>(string, string, string);
ConariX is an extended IConari implementation to provide a fully automatic processing through its dynamic features known as a DLR way.
using dynamic l = new ConariX(...);
//...
l.replace<bool>(...)
You can also make it more friendly,
using ConariX l = ConariX.Make(new(...), out dynamic d);
l.PE.Export.Names // l.PE is part of ConariX
string v = d.versionString<CharPtr>(); // while this will be generated at runtime
ConariX is not a sealed class and you can even easily implement your own wrapper in a full integration steps,
class MyConari: ConariX
{
public MyConari(string lib)
: base((Config)lib, prefix)
{ }
// ... powerful engine is ready for your awesome applications
}
or just something similar to ours Accessors below.
using net.r_eg.Conari.Accessors...
Accessors are wrappers of the ConariX implementation. They provide the easiest access to anything while they do not contain anything at all. For example, Windows kernel32
dynamic kernel32 = new Kernel32();
kernel32.GetModuleHandleA<IntPtr>("libcurl-x64");
kernel32.GetModuleHandleW<IntPtr>(ustr);
Or Windows user32
dynamic user32 = new User32();
user32.ShowWindow(0x000A0A28, 3);
user32.MessageBoxA(0, "Conari in action", "Hello!", 0);
Easily access whatever you want since everything will be generated and adapted at runtime.
using net.r_eg.Conari.Types;
Conari provides most powerful types and helpers. Some of this may provide several modes for automatic or semi-automatic processing etc.
For example, NativeStruct provides fully automatic way of working with structures without declarations using NativeData chains; E.g.:
using var u = NativeStruct.Make.f<UIntPtr>("start", "end").Struct;
/* Hey! We just generated a structure like
[StructLayout(LayoutKind.Sequential)]
private struct MatchResult
{
public UIntPtr start;
public UIntPtr end;
}*/
While NativeStruct<T> provides semi-automatic way of working with structures using CLR types declarations; E.g.:
using var u = new NativeStruct<MatchResult>();
Some types may aggregate a several types at the same type.
For example, TCharPtr can be configured either as WCharPtr or as CharPtr. But once per domain.
TCharPtr.Unicode = true; // allowed to change once per domain
using NativeString<TCharPtr> data = new("Hello world");
TCharPtr tch = data; // will be considered as WCharPtr since Unicode = true
Some types, on the contrary, just extends features of the available built-in types, such as VPtr which supports adding long numbers to IntPtr (IntPtr + long), a complete comparing >,<,>=,<=,==,!=
between VPtr and int/long, and more.
VPtr n1 = new IntPtr(0xB);
VPtr n2 = new IntPtr(0x7FFF89EB0110);
n1 + n2
n1 > 0xA
Starting with 1.5 Conari has two implementations to handle PE modules.
- PE32/PE32+ through Memory implementation.
- PE32/PE32+ through NativeStream file system implementation.
This, however, does not cover full PE parsing and just provides most required features that will be used at least in our engine. Such as some addresses, characteristics, sections, export table, etc.
You can also use it independently if you need.
using ConariL l = new(...);
l.PE.Magic // Magic.PE64
l.PE.Machine // MachineTypes.IMAGE_FILE_MACHINE_AMD64
l.PE.Characteristics // IMAGE_FILE_EXECUTABLE_IMAGE
// | IMAGE_FILE_LARGE_ADDRESS_AWARE | IMAGE_FILE_DLL
l.PE.Export.Names
l.PE.Export.Addresses
l.Memory.@goto(l.PE.Addresses.IMAGE_NT_HEADERS). ...
...
But here's what's interesting, our implementations are based on Conari itself for a most quickly accessing, just look at QPe.
Also note, for 1.5+ you can even disable all related to PE features such as mangling, list of exported proc, etc., using PeImplType.
That may increase some speed, for example, https://github.com/KirillOsenkov/Benchmarks/pull/5
Conari supports both Unicode and Multibyte null-terminated an unmanaged strings.
You can simply access it through CharPtr, WCharPtr, or TCharPtr types.
To create new,
To manage it,
- NativeStringManager
- ConariL/ConariX since they implements INativeStringManager and IStringMaker
using dynamic l = new ConariX(...);
bool found = l.replace<bool>
(
"Hello {p}".Do(out TCharPtr result), "{p}", "world!"
);
string data = "number = 888;";
found = l.replace<bool>(ref data, "+??;", "2034;");
using ConariX l = ConariX.Make(new ConariX(...), out dynamic x);
x.replace<bool>
(
l._T("Hello {p}", out CharPtr result), l._T("{p}"), "world!"
);
NativeString/BufferedString supports concatenations,
using var str = input + " " + "world";
Optional buffers for receiving and updating values,
using BufferedString<TCharPtr> s = new("Hello {p}!");
l.bind<Func<TCharPtr, string, string, bool>>("replace")(s, "{p}", "world!");
Safe and fast reuse of the allocated memory regions,
using var data = new BufferedString<CharPtr>("Hello!", 8);
// data == Hello! at 0x2674F82F630
data.update("Hello world!");
// data == Hello world! at the same 0x2674F82F630
Automatic context based marshaling,
string data = "number = 888;";
l.replace<bool>
(
ref data, // -> BufferedString<CharPtr> -> CharPtr
"+??;", // -> NativeString<CharPtr> -> CharPtr
"2034;" // -> NativeString<CharPtr> -> CharPtr
)
using net.r_eg.Conari.Native;
The easiest (most ever) access to any data in unmanaged memory is possible through our Native chains. Automatic and semi-automatic processing.
You can easily build accessing to any data at runtime in used source.
.f<int>("a", "b")
.t<CharPtr>("name")
.Raw
- {byte[0x0000000c]} byte[]
[0] 0x05 byte --
[1] 0x00 byte |
[2] 0x00 byte |
[3] 0x00 byte --^ a = 5
[4] 0x07 byte --
[5] 0x00 byte |
[6] 0x00 byte |
[7] 0x00 byte --^ b = 7
[8] 0x20 byte --
[9] 0x78 byte |_ pointer to allocated string (CharPtr)name
[10] 0xf0 byte |
[11] 0x56 byte --
...
In turn the source can be almost anything since we provide support of unmanaged memory, streams, and local content equally through IAccessor and related.
l.Memory
.move(0x3C, Zone.D)
.read<LONG>(out LONG e_lfanew)
.move(e_lfanew, Zone.D)
.eq('P', 'E', '\0', '\0')
.ifFalse(_ => throw new PECorruptDataException())
. ...
Together with NativeData build a most common type.
memory.Native()
.f<WORD>("Machine", "NumberOfSections")
.align<DWORD>(3)
.t<WORD>("SizeOfOptionalHeader")
.t<WORD>("Characteristics")
.region()
.t<WORD>("Magic")
.build(out dynamic ifh);
ifh.NumberOfSections // 6
ifh.Characteristics // IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE | IMAGE_FILE_DLL
ifh.Machine // IMAGE_FILE_MACHINE_AMD64
ifh.Magic // PE64
Conari use Cdecl by default which can do vararg functions since the stack is cleaned up by the caller.
This, however, can be changed and controlled (using events) at any time. But it may affect some features such as IProviderDLR.TrailingArgs.
And, Mangling and Aliases will help to make easy access to any functions or variables.
using(ConariL l = new("Library.dll", CallingConvention.StdCall))
{
//...
l.Mangling = true; // _get_SevenStdCall@0 <-> get_SevenStdCall
l.Convention = CallingConvention.Cdecl; // return back the default convention
l.Aliases["Flag"] = l.Aliases["getFlag"] = l.Aliases["xFunc"]; //Flag() -> getFlag() -> xFunc()->...
// ...
l._.getFlag<bool>();
}
You can use any related bind<>
methods such
l.bind<...>("proc")
Then invoke it immediately,
l.bind<Action<LuaState, string>>("setglobal")(L, "onKeyDown");
or later,
var set = l.bind<Action<LuaState, string>>("setglobal");
...
set(L, "onKeyDown");
Also note, Conari provides an additional delegates for ref/out arguments when using a semi-automatic way. Find it in Types, for example, ActionOut.
This similar to lambda binding except the fact that everything will be generated at runtime.
dlr.whatever_you_want(L, 123, "arg3", ...)
To use a return value, just specify this type
bool data = dlr.whatever_you_want<bool>(L, 123, "arg3", ...)
π
-
π Home
-
π Quick start
-
π Features
-
π Upgrade to v1.3
-
- π C++ β€ C#. Part-1 ([+]πΉ)
- π Complex types and Strings. Part-2 ([+]πΉ)
π
- π’ Q/A