Compare commits

...

2 Commits

Author SHA1 Message Date
_xeroxz 2ab613e5d4 Update README.md
3 years ago
_xeroxz 53667a67ac updated for 7/23/2021
3 years ago

@ -1,101 +1,5 @@
![](https://githacks.org/_xeroxz/vmhook-eac/-/raw/a2e38c76b1fb9a53527c2441b06bb25b768d9959/bin/running-with-patch.png)
### vmhook-eac multi-vm
### About
This branch is for EasyAntiCheat drivers which have more than a single virtual machine. Keep in mind that multiple virtual machines does not mean nested virtualization...
This is a small POC to show an interesting design weakness in VMProtect 2 which can aid an attacker in such a way that reading memory can be manipulated in a centralized way. In this POC all `READQ/DW/B` virtual instructions are hooked, when virtualized integrity check routines try and read unwriteable sections, the pointer is changed to an untouched clone of the driver. This means all inlined virtualized integrity checks can be bypassed with a few lines of code. This is not possible without the aid of VMProtect 2's design... So im refering to having reusable vm handlers as a design weakness...
***This is less about EasyAntiCheat and more about a design weakness in VMProtect 2... EasyAntiCheat is mearly used for a real world example, in addition, nothing released here is undetected, it has plenty of detection vectors...***
```
00000603 67.09356689 [vmhook-eac [core number = 20]] READ(Q/DW/B) EasyAntiCheat.sys+0x1000
00000604 67.09357452 [vmhook-eac [core number = 20]] READ(Q/DW/B) EasyAntiCheat.sys+0x1000
00000605 67.09359741 [vmhook-eac [core number = 20]] READ(Q/DW/B) EasyAntiCheat.sys+0x1010
00000606 67.09359741 [vmhook-eac [core number = 20]] READ(Q/DW/B) EasyAntiCheat.sys+0x1010
00000607 67.09362793 [vmhook-eac [core number = 20]] READ(Q/DW/B) EasyAntiCheat.sys+0x1020
```
*note: not all integrity checks are virtualized, there were at least one other outside of virtualization*
#### SHA1 Integrity Checks
Integrity checks outside of the VMProtect 2 virtual machine are not effected by my POC. In particular, a SHA1 hash of both `.text` and `.eac0` is computed, the SHA1 hash function itself is not virtualized so it is not effected by my `READQ/DW/B` hook.
```
00126334 68.50553894 [vmhook-eac [core number = 13]]sha1 hash data = 0xFFFFF80061B91000, len = 0x51d28, result = 0xFFFFFE8158E60BF0
00126335 68.50672913 [vmhook-eac [core number = 13]]sha1 hash data = 0xFFFFF80061C0B000, len = 0x2bc79d, result = 0xFFFFFE8158E60BF0
```
*Side Note: Check out that len? its not aligned, this means you can patch the alignment/padding at the end of both of these sections if you wanted and the SHA1 integrity checks would be fine...*
Thus a hook is placed on this SHA1 hash function and spoofed results are computed... you can locate this function by searching for SHA1 magic numbers: 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0... The SHA1 function should really be virtualized since these magic constant values can be located instantly...
### Solution, Possible Alternatives
* 1.) If EasyAntiCheat were to patch their own driver using `MmMapIoSpaceEx` - `PAGE_READWRITE` (for HVCI support), they could compute a SHA1 hash, then revert the changes, compute a second SHA1 hash... If the hashes are the same, then you know someone is hooking SHA1, or hooking `READQ/DW/B` virtual instructions... In other words, instead of checking for patches, check to see if your patches result in a different hash... ***When i say patch i mean, change some padding/alignment bytes at the end of an unwriteable section***...
* 2.) Map the driver into the usermode service as READONLY, this way the usermode service can just read the mapping and compute a hash... This has its own attack vectors considering it would require calling out to ntoskrnl/external code, however the idea is what matters, having multiple sources of integrity checking is ideal.
### How To Update
#### VM Handler Table Indexes
These vm handler indexes are for EasyAntiCheat.sys 6/23/2021, when the driver gets re-vmprotected these vm handler indexes need to be updated.
```cpp
//
// vm handler indexes for READQ...
//
inline u8 g_readq_idxs[] = { 247, 215, 169, 159, 71, 60, 55, 43, 23 };
//
// vm handler indexes for READDW
//
inline u8 g_readdw_idxs[] = { 218, 180, 179, 178, 163, 137, 92, 22, 12 };
//
// vm handler indexes for READB
//
inline u8 g_readb_idxs[] = { 249, 231, 184, 160, 88, 85, 48, 9, 2 };
```
#### Offsets
`EAC_VM_HANDLE_OFFSET` contains the offset from the module base to the vm handler table, as of right now EAC only uses a single virtual machine in their VMProtect config so there is only a single vm handler table...
`EAC_SHA1_OFFSET` contains the offset from the module base to the sha1 function...
you can locate this function by searching for SHA1 magic numbers: `0x67452301`, `0xEFCDAB89`
`0x98BADCFE`, `0x10325476`, `0xC3D2E1F0`. These crypto functions should be virtualized so their constant values cannot be located using IDA --> search "immidate values".
`EAC_IMAGE_BASE` contains the "ImageBase" value inside of the OptionalHeaders field of the NT
headers... This value gets updated with the actual module base of the driver once loaded into
memory... I didnt want to read it off disk so I just made it a macro here...
```cpp
#define EAC_VM_HANDLE_OFFSET 0xE93D
#define EAC_SHA1_OFFSET 0x4C00
#define EAC_IMAGE_BASE 0x140000000
```
#### VM Handler Table Entry Encrypt/Decrypt
Since EasyAntiCheat is only using a single VM in their VMProtect 2 config, you will only need to update these two lambdas with the new instruction that is used to decrypt virtual machine handler table entries...
You can use [vmprofiler-cli](https://githacks.org/vmp2/vmprofiler-cli/-/releases) to obtain these instructions... Since the entry point of the driver is virtualized you can simply take the "AddressOfEntryPoint" relative virtual address and use it as the `--vmentry` flag...
```cpp
// > 0x00007FF77A233736 mov rcx, [r12+rax*8]
// > 0x00007FF77A23373D ror rcx, 0x30 <--- decrypt vm handler entry...
// > 0x00007FF77A233747 add rcx, r13
// > 0x00007FF77A23374A jmp rcx
vm::decrypt_handler_t _decrypt_handler = []( u64 val ) -> u64 { return _rotr64( val, 0x30 ); };
// > 0x00007FF77A233736 mov rcx, [r12+rax*8]
// > 0x00007FF77A23373D ror rcx, 0x30 <--- inverse to encrypt vm handler entry...
// > 0x00007FF77A233747 add rcx, r13
// > 0x00007FF77A23374A jmp rcx
vm::encrypt_handler_t _encrypt_handler = []( u64 val ) -> u64 { return _rotl64( val, 0x30 ); };
```
You can update this code by dumping the rva's of all virtual machine handler tables using [vmprofiler-cli](https://githacks.org/vmp2/vmprofiler-cli/-/commit/3e8df3258ced14c26b8109d77d359482d98bf785). Then you can dump all of the `READQ/DW/W/B` virtual instruction indexes using the `--indexes READ(X)` commandline argument.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -1 +1 @@
Subproject commit aec227a091e0a2a5a591d0e77df5243270d99672
Subproject commit 74d9d2b687af09c6819c406740c57cd794d3b376

@ -1,56 +1,200 @@
//
// note: these vm handler indexes are for EasyAntiCheat.sys 6/23/2021...
// when the driver gets re-vmprotected these vm handler indexes need to be updated...
//
// EAC_VM_HANDLE_OFFSET contains the offset from the module base to the vm handler table,
// as of right now EAC only uses a single virtual machine in their VMProtect config so there
// is only a single vm handler table...
//
// EAC_SHA1_OFFSET contains the offset from the module base to the sha1 function...
// you can locate this function by searching for SHA1 magic numbers: 0x67452301, 0xEFCDAB89
// 0x98BADCFE, 0x10325476, 0xC3D2E1F0...
//
// EAC_IMAGE_BASE contains the "ImageBase" value inside of the OptionalHeaders field of the NT
// headers... This value gets updated with the actual module base of the driver once loaded into
// memory... I didnt want to read it off disk so I just made it a macro here...
//
#pragma once
#include <scn.hpp>
#include <sha1.hpp>
#include <shithook.hpp>
//
// game cheat offset flash backs...
//
#define EAC_VM_HANDLE_OFFSET 0xE93D
#define EAC_SHA1_OFFSET 0x4C00
#define VM_COUNT 10
#define EAC_SHA1_OFFSET 0x1D8F0
#define EAC_IMAGE_BASE 0x140000000
#define DBG_PRINT( format, ... ) \
DbgPrintEx( DPFLTR_SYSTEM_ID, DPFLTR_ERROR_LEVEL, "[vmhook-eac [core number = %d]]" format, \
KeGetCurrentProcessorNumber(), __VA_ARGS__ )
//
// vm handler indexes for READQ...
//
template < class T > inline T __ROL__( T value, int count )
{
const unsigned int nbits = sizeof( T ) * 8;
if ( count > 0 )
{
count %= nbits;
T high = value >> ( nbits - count );
if ( T( -1 ) < 0 ) // signed value
high &= ~( ( T( -1 ) << count ) );
value <<= count;
value |= high;
}
else
{
count = -count % nbits;
T low = value << ( nbits - count );
value >>= count;
value |= low;
}
return value;
}
inline u8 __ROL1__( u8 value, int count )
{
return __ROL__( ( u8 )value, count );
}
inline u16 __ROL2__( u16 value, int count )
{
return __ROL__( ( u16 )value, count );
}
inline u32 __ROL4__( u32 value, int count )
{
return __ROL__( ( u32 )value, count );
}
inline u64 __ROL8__( u64 value, int count )
{
return __ROL__( ( u64 )value, count );
}
inline u8 __ROR1__( u8 value, int count )
{
return __ROL__( ( u8 )value, -count );
}
inline u16 __ROR2__( u16 value, int count )
{
return __ROL__( ( u16 )value, -count );
}
inline u32 __ROR4__( u32 value, int count )
{
return __ROL__( ( u32 )value, -count );
}
inline u64 __ROR8__( u64 value, int count )
{
return __ROL__( ( u64 )value, -count );
}
struct vm_meta_data_t
{
u32 table_rva;
u8 read_callback_count;
u8 read_callback_indexes[ 256 ];
vm::encrypt_handler_t encrypt;
vm::decrypt_handler_t decrypt;
};
const vm_meta_data_t table_one = { 0x29829,
28,
{
0x17, 0x48, 0x5e, 0x89, 0x93, 0xbc, 0xe1, 0xed, 0xfb, 0x13,
0x1f, 0x21, 0x41, 0x59, 0x78, 0x82, 0x9e, 0xcb, 0xd9, 0xee,
0x6, 0x5d, 0x60, 0x95, 0xb4, 0x2e, 0x6c, 0xbd,
},
[]( u64 val ) -> u64 { return val + 0x6A78538F; },
[]( u64 val ) -> u64 { return val - 0x6A78538F; } };
const vm_meta_data_t table_two = { 0xf415,
31,
{
0x0, 0x1, 0x20, 0x37, 0x44, 0x4a, 0x6b, 0xb7, 0xc6, 0xd2, 0xd,
0x10, 0x3f, 0x90, 0x98, 0xb6, 0xbd, 0xe1, 0x25, 0x61, 0xa0, 0xa6,
0xbc, 0xd0, 0xde, 0xed, 0xf6, 0x4, 0x5c, 0x78, 0xdc,
},
[]( u64 val ) -> u64 { return val * -1; },
[]( u64 val ) -> u64 { return val * -1; } };
const vm_meta_data_t table_three = { 0x10e7d,
25,
{
0xc, 0x12, 0x21, 0x40, 0x5e, 0x7f, 0xa0, 0xbc, 0xf8, 0xfe, 0x10, 0x23, 0x39,
0x3e, 0x59, 0x65, 0x77, 0xc3, 0x7b, 0xd6, 0xea, 0xf7, 0x3f, 0x4d, 0x94,
},
[]( u64 val ) -> u64 { return val + 1; },
[]( u64 val ) -> u64 { return val - 1; } };
const vm_meta_data_t table_four = { 0x7e100,
26,
{
0xc, 0x25, 0x5d, 0x63, 0x75, 0xf8, 0x21, 0x23, 0x38, 0x59, 0x73, 0x85, 0xc5,
0xcb, 0x1c, 0x2c, 0x42, 0x5b, 0x68, 0x89, 0xa7, 0xb3, 0xbd, 0x2e, 0x45, 0xb5,
},
[]( u64 val ) -> u64 { return __ROR8__( val, 0x2D ); },
[]( u64 val ) -> u64 { return __ROL8__( val, 0x2D ); } };
const vm_meta_data_t table_five = { 0x7C100,
29,
{
0x3d, 0x62, 0x67, 0x7a, 0x82, 0xab, 0xdc, 0xf3, 0x13, 0x57,
0x5d, 0x6f, 0x78, 0xae, 0xb1, 0xd2, 0x40, 0x48, 0x5f, 0x85,
0xad, 0xc3, 0x1d, 0x8c, 0x95, 0xa4, 0xb7, 0xc1, 0xe4,
},
[]( u64 val ) -> u64 { return val + 1; },
[]( u64 val ) -> u64 { return val - 1; } };
const vm_meta_data_t table_six = { 0x7b100,
21,
{
0x2a, 0x59, 0xc3, 0xfb, 0x8, 0xc, 0x90, 0xa2, 0xa5, 0xc9, 0xde,
0xe8, 0xf5, 0x4, 0x2b, 0x38, 0x8a, 0xd6, 0x9, 0x78, 0x7e,
},
[]( u64 val ) -> u64 { return val + 1; },
[]( u64 val ) -> u64 { return val - 1; } };
inline u8 g_readq_idxs[] = { 247, 215, 169, 159, 71, 60, 55, 43, 23 };
const vm_meta_data_t table_seven = { 0x7d900,
23,
{
0x49, 0x53, 0x54, 0x55, 0x59, 0x63, 0x65, 0x6f, 0xc5, 0x30, 0x4e, 0x90,
0x91, 0x9f, 0x5e, 0x6e, 0x8a, 0x92, 0xe0, 0x23, 0x47, 0x5f, 0x7f,
},
[]( u64 val ) -> u64 { return _byteswap_uint64( val ); },
[]( u64 val ) -> u64 { return _byteswap_uint64( val ); } };
//
// vm handler indexes for READDW
//
const vm_meta_data_t table_eight = { 0x7c900,
17,
{
0x24,
0x32,
0xac,
0x11,
0x20,
0x87,
0xbe,
0xf,
0x1c,
0x33,
0xa8,
0x40,
0x64,
0x99,
0xc4,
0xd5,
0xfd,
},
[]( u64 val ) -> u64 { return val ^ 0x70D337C5; },
[]( u64 val ) -> u64 { return val ^ 0x70D337C5; } };
inline u8 g_readdw_idxs[] = { 218, 180, 179, 178, 163, 137, 92, 22, 12 };
const vm_meta_data_t table_nine = { 0x7b900,
24,
{
0x43, 0x4c, 0xad, 0x30, 0x66, 0x69, 0x73, 0x80, 0xd3, 0xf4, 0x21, 0x53,
0x60, 0x8a, 0xfc, 0x36, 0x7b, 0x8c, 0x96, 0x9f, 0xc4, 0xc7, 0xea, 0xf8,
},
[]( u64 val ) -> u64 { return val ^ 0x60895574; },
[]( u64 val ) -> u64 { return val ^ 0x60895574; } };
//
// vm handler indexes for READB
//
const vm_meta_data_t table_ten = { 0x7d100,
27,
{
0x34, 0x3f, 0x97, 0x9e, 0xd6, 0x5f, 0x7b, 0x8f, 0xaf,
0xba, 0xcb, 0xef, 0xd, 0x16, 0x4e, 0x64, 0x65, 0xbd,
0xd5, 0xd9, 0xec, 0x15, 0x38, 0x58, 0x91, 0xdf, 0xfc,
},
[]( u64 val ) -> u64 { return val ^ 0x6FC7A2BC; },
[]( u64 val ) -> u64 { return val ^ 0x6FC7A2BC; } };
inline u8 g_readb_idxs[] = { 249, 231, 184, 160, 88, 85, 48, 9, 2 };
const vm_meta_data_t *vm_meta_data[ VM_COUNT ] = { &table_one, &table_two, &table_three, &table_four, &table_five,
&table_six, &table_seven, &table_eight, &table_nine, &table_ten };
inline vm::hook_t *g_vmhook = nullptr;
inline vm::handler::table_t *g_vm_table = nullptr;
inline u64 g_image_base = 0u, g_image_size = 0u, g_image_clone = 0u;
inline inline_hook_t g_sha1_hook;

@ -23,7 +23,7 @@ __declspec( noinline ) void hook_sha1( void *data, unsigned int len, void *resul
//
// if EAC is trying to sha1 hash any data in readonly sections...
// then we hash the clone of the driver before it was patched...
//
//
// note: relocations are the same in the clone so those wont need to be handled...
//
@ -47,20 +47,8 @@ void image_loaded( PUNICODE_STRING image_name, HANDLE pid, PIMAGE_INFO image_inf
{
if ( !pid && wcsstr( image_name->Buffer, L"EasyAntiCheat.sys" ) )
{
if ( g_vmhook && g_vm_table && g_image_clone )
delete g_vmhook, delete g_vm_table, ExFreePool( ( void * )g_image_clone );
// > 0x00007FF77A233736 mov rcx, [r12+rax*8]
// > 0x00007FF77A23373D ror rcx, 0x30 <--- decrypt vm handler entry...
// > 0x00007FF77A233747 add rcx, r13
// > 0x00007FF77A23374A jmp rcx
vm::decrypt_handler_t _decrypt_handler = []( u64 val ) -> u64 { return _rotr64( val, 0x30 ); };
// > 0x00007FF77A233736 mov rcx, [r12+rax*8]
// > 0x00007FF77A23373D ror rcx, 0x30 <--- inverse to encrypt vm handler entry...
// > 0x00007FF77A233747 add rcx, r13
// > 0x00007FF77A23374A jmp rcx
vm::encrypt_handler_t _encrypt_handler = []( u64 val ) -> u64 { return _rotl64( val, 0x30 ); };
if ( vm::g_vmctx && g_image_clone )
delete vm::g_vmctx, ExFreePool( ( void * )g_image_clone );
vm::handler::edit_entry_t _edit_entry = []( u64 *entry_ptr, u64 val ) -> void {
//
@ -68,10 +56,10 @@ void image_loaded( PUNICODE_STRING image_name, HANDLE pid, PIMAGE_INFO image_inf
//
{
_disable();
auto cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0( cr0 );
_disable();
}
*entry_ptr = val;
@ -83,57 +71,50 @@ void image_loaded( PUNICODE_STRING image_name, HANDLE pid, PIMAGE_INFO image_inf
{
auto cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0( cr0 );
_enable();
}
};
auto image_base = reinterpret_cast< u64 >( image_info->ImageBase );
auto handler_table_ptr = reinterpret_cast< u64 * >( image_base + EAC_VM_HANDLE_OFFSET );
//
// Clone the entire driver into a kernel pool, keep in mind relocations will also be
// the same as the original driver! Dont call any code in this clone, only refer to it...
//
vm::g_vmctx = new vm::hook_t();
g_image_base = image_base, g_image_size = image_info->ImageSize;
g_image_clone = ( u64 )RtlCopyMemory( ExAllocatePool( NonPagedPool, image_info->ImageSize ),
image_info->ImageBase, image_info->ImageSize );
//
// allocate memory for a g_vmhook, and g_vm_table...
//
g_vm_table = new vm::handler::table_t( handler_table_ptr, _edit_entry );
g_vmhook = new vm::hook_t( image_base, EAC_IMAGE_BASE, _decrypt_handler, _encrypt_handler, g_vm_table );
g_image_base = image_base, g_image_size = image_info->ImageSize;
const auto callback = []( vm::registers *regs, u8 handler_idx ) {
const auto read_addr = reinterpret_cast< u64 * >( regs->rbp )[ 0 ];
// shoot the tires right off the virtualized integrity checks in about 2 lines of code...
if ( scn::read_only( g_image_base, read_addr ) )
{
DBG_PRINT( " READ(Q/DW/B) EasyAntiCheat.sys+0x%x\n", ( read_addr - g_image_base ) );
// DBG_PRINT( " READ(Q/DW/B) EasyAntiCheat.sys+0x%x\n", ( read_addr - g_image_base ) );
reinterpret_cast< u64 * >( regs->rbp )[ 0 ] = g_image_clone + ( read_addr - g_image_base );
}
};
// install hooks on READQ virtual machine handlers...
for ( auto idx = 0u; idx < sizeof g_readq_idxs; ++idx )
g_vm_table->set_callback( g_readq_idxs[ idx ], callback );
for ( auto idx = 0u; idx < VM_COUNT; ++idx )
{
auto vm_handler_table =
new vm::handler::table_t( g_image_base, EAC_IMAGE_BASE, vm_meta_data[ idx ]->table_rva, _edit_entry,
vm_meta_data[ idx ]->decrypt, vm_meta_data[ idx ]->encrypt );
// install hooks on READDW virtual machine handlers...
for ( auto idx = 0u; idx < sizeof g_readdw_idxs; ++idx )
g_vm_table->set_callback( g_readdw_idxs[ idx ], callback );
for ( auto cnt = 0u; cnt < vm_meta_data[ idx ]->read_callback_count; ++cnt )
vm_handler_table->set_callback( vm_meta_data[ idx ]->read_callback_indexes[ cnt ], callback );
// install hooks on READB virtual machine handlers...
for ( auto idx = 0u; idx < sizeof g_readb_idxs; ++idx )
g_vm_table->set_callback( g_readb_idxs[ idx ], callback );
vm::g_vmctx->add_table( vm_handler_table );
}
//
// hooks all vm handlers and starts callbacks...
//
g_vmhook->start();
vm::g_vmctx->start();
// hook on sha1 since it executes outside of the virtual machine...
// and does an integrity check on .text and .eac0...

@ -11,34 +11,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vmhook", "dependencies\vmho
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Debug|x64.ActiveCfg = Debug|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Debug|x64.Build.0 = Debug|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Debug|x64.Deploy.0 = Debug|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Debug|x86.ActiveCfg = Debug|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Release|x64.ActiveCfg = Release|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Release|x64.Build.0 = Release|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Release|x64.Deploy.0 = Release|x64
{42442E7D-1DEC-455C-BD69-931D908F83A8}.Release|x86.ActiveCfg = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Debug|x64.ActiveCfg = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Debug|x64.Build.0 = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Debug|x64.Deploy.0 = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Debug|x86.ActiveCfg = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Debug|x86.Build.0 = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Debug|x86.Deploy.0 = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Release|x64.ActiveCfg = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Release|x64.Build.0 = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Release|x64.Deploy.0 = Release|x64
{475EA8A7-C1BA-4847-B9C3-198C9738E0C0}.Release|x86.ActiveCfg = Release|x64
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Debug|x64.ActiveCfg = Debug|x64
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Debug|x64.Build.0 = Debug|x64
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Debug|x86.ActiveCfg = Debug|Win32
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Debug|x86.Build.0 = Debug|Win32
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Release|x64.ActiveCfg = Release|x64
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Release|x64.Build.0 = Release|x64
{D257C9F6-C705-49D5-84ED-64C9C513C419}.Release|x86.ActiveCfg = Release|Win32

@ -59,6 +59,7 @@
<ClCompile>
<LanguageStandard>stdcpp17</LanguageStandard>
<TreatWarningAsError>false</TreatWarningAsError>
<Optimization>Full</Optimization>
</ClCompile>
<Link>
<EntryPointSymbol>ScDriverEntry</EntryPointSymbol>

Loading…
Cancel
Save