mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-01 12:04:46 +02:00
* Observe typed native WEC subscription runtime status * Link typed WEC runtime changelog to PR 424 * Pass a native null source for subscription runtime queries * Ignore unused count storage for native null WEC variants * Keep unavailable WEC source inventories unknown and diagnose native XML reads * Read native WEC subscription XML with bounded explicit Unicode pipes * Use bounded Unicode subscription reads in runtime observations and cleanup * Decode native WEC XML BOMs without unsupported Unicode switch * Describe strict native WEC XML byte decoding * Reference System.Xml explicitly when compiling under Windows PowerShell * Regenerate website changelog snapshots with their proper headers
70 lines
4.2 KiB
C#
70 lines
4.2 KiB
C#
// Read-only WEC runtime observations. No subscription create/save/retry API.
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
namespace Wela.WecRuntime {
|
|
public sealed class Value {
|
|
public string State="Unknown"; public uint? NativeType; public uint Count;
|
|
public int ErrorCode; public string Diagnostic=""; public object Data;
|
|
}
|
|
public static class Native {
|
|
[DllImport("wecapi.dll",CharSet=CharSet.Unicode,SetLastError=true)]
|
|
[return:MarshalAs(UnmanagedType.Bool)]
|
|
static extern bool EcGetSubscriptionRunTimeStatus(string subscription,int property,string source,uint flags,uint size,IntPtr buffer,out uint used);
|
|
const uint MaximumBytes=1048576;
|
|
static void Range(IntPtr buffer,uint size,IntPtr pointer,long length) {
|
|
long offset=pointer.ToInt64()-buffer.ToInt64();
|
|
if(pointer==IntPtr.Zero||offset<16||length<0||offset>size||length>size-offset)throw new InvalidOperationException("Native variant points outside its returned buffer.");
|
|
}
|
|
static string Text(IntPtr buffer,uint size,IntPtr pointer) {
|
|
Range(buffer,size,pointer,2);long offset=pointer.ToInt64()-buffer.ToInt64();
|
|
StringBuilder value=new StringBuilder();
|
|
for(int i=0;i<32768&&offset+2L*i+2<=size;i++) {
|
|
char c=(char)(ushort)Marshal.ReadInt16(pointer,i*2);if(c==0)return value.ToString();value.Append(c);
|
|
}
|
|
throw new InvalidOperationException("Native string is unterminated or exceeds the character limit.");
|
|
}
|
|
// Public only to permit safe allocated-buffer ABI/type regression tests.
|
|
public static Value Decode(IntPtr buffer,uint size,int property) {
|
|
if(buffer==IntPtr.Zero||size<16||size>MaximumBytes||property<0||property>6)throw new InvalidOperationException("Invalid runtime buffer or property.");
|
|
uint count=unchecked((uint)Marshal.ReadInt32(buffer,8));uint type=unchecked((uint)Marshal.ReadInt32(buffer,12));
|
|
Value value=new Value {NativeType=type,Count=count};
|
|
// Count and union storage have no meaning for EcVarTypeNull; Windows may leave them untouched.
|
|
if(type==0) {value.Count=0;value.State="NotAvailable";return value;}
|
|
uint expected=property==0||property==1?2U:property==2?4U:property==5?132U:3U;
|
|
if(type!=expected)throw new InvalidOperationException("Unexpected EC_VARIANT type for runtime property.");
|
|
if(type==2) {value.Data=unchecked((uint)Marshal.ReadInt32(buffer));}
|
|
else if(type==3) {value.Data=unchecked((ulong)Marshal.ReadInt64(buffer));}
|
|
else if(type==4) {value.Data=Text(buffer,size,Marshal.ReadIntPtr(buffer));}
|
|
else {
|
|
if(count>4096)throw new InvalidOperationException("Native source inventory exceeds 4096 entries.");
|
|
string[] values=new string[count];IntPtr pointers=Marshal.ReadIntPtr(buffer);
|
|
if(count!=0)Range(buffer,size,pointers,(long)count*IntPtr.Size);
|
|
for(int i=0;i<count;i++)values[i]=Text(buffer,size,Marshal.ReadIntPtr(pointers,i*IntPtr.Size));
|
|
value.Data=values;
|
|
}
|
|
value.State="Observed";return value;
|
|
}
|
|
public static Value Read(string subscription,string source,int property) {
|
|
if(String.IsNullOrWhiteSpace(subscription)||subscription.Length>128||subscription.IndexOf('\0')>=0||property<0||property>6||source!=null&&(source.Length>32768||source.IndexOf('\0')>=0))throw new ArgumentException("Invalid selected subscription/source/property.");
|
|
uint size=16;
|
|
for(int attempt=0;attempt<3;attempt++) {
|
|
IntPtr buffer=Marshal.AllocHGlobal((int)size);
|
|
try {
|
|
uint used;bool success=EcGetSubscriptionRunTimeStatus(subscription,property,source,0,size,buffer,out used);
|
|
int error=Marshal.GetLastWin32Error();
|
|
if(success) {
|
|
if(used<16||used>size)return new Value {ErrorCode=13,Diagnostic="Native runtime returned an invalid used-buffer length."};
|
|
try{return Decode(buffer,used,property);}catch(Exception e){return new Value {ErrorCode=13,Diagnostic=e.Message};}
|
|
}
|
|
if(error!=122)return new Value {ErrorCode=error,Diagnostic=new Win32Exception(error).Message};
|
|
if(used<=size||used>MaximumBytes)return new Value {ErrorCode=122,Diagnostic="Runtime buffer growth is invalid or exceeds one MiB."};
|
|
size=used;
|
|
}finally {Marshal.FreeHGlobal(buffer);}
|
|
}
|
|
return new Value {ErrorCode=122,Diagnostic="Runtime buffer changed repeatedly; observation is incomplete."};
|
|
}
|
|
}
|
|
}
|