Code Examples

Using the Command Line Interface (CLI)

Synchronous actions that do not require callbacks like notifications, logs, and shadow files processing are available from the command line interface.

For diagnostics or during the development phase, using the CLI is a quick way to validate payload or expected results.

JSON is the format used for inputs and outputs, the CLI expects files but con can be used to output results on the console.

Listing commands

To list the commands available, run HSDC32Cmd.exe or HSDC64Cmd.exe, you can also append -Help.

Copy
Rundll32 command usage:
----- General -----
  -Help
  -License <ES server license file>
    The License command must precede all others and must be 
    included in each call to RunDLL Command interface
  -Config <json config file>
     Default: { "registry":"Software\\Lumension Security\\API", "strings":true }
  -RSAPublicKey <RSA public key>
  -RSAPrivateKey <RSA private key>
    key pair is mandatory for commands with 'Sign'
    
----- Identities -----
  -GetSecurityIdentities <output file>
  -GetCertificates <input file> <output file>
  -RequestCertificate <input file> <output file>
  
----- Status & Hardening -----
  -GetHealth <json output file>
  -RelaxHardening <ES maintenance file> <signature file>
  -SignRelaxHardening <json maintenance file>
  -GetConnectivityStatus <json output file>
  -SetConnectivityStatus <1:offline or 2:online>
     Admin priviledges & option 60 set at 2 are mandatory
     
----- Inventory -----
  -GetInventoryAll <json output file>
  -GetInventoryOnline <json output file>
  -HashOpticalDisk <drive path> <json output file>
  
----- Policies -----
  -ImportJsonPolicy <json policy file> <signature file>
  -SignImportJsonPolicy <json policy file>
  -RequestOfflinePolicy <offline policy file> <offline request file>
  -ImportOfflinePolicy <offline answer file>
  -DumpPolicies <policy file> <json dump file>
  -AccessCheckIsSystem <drive path> <json output file>
  -AccessCheckVolume <drive path> <json input file> <json output file>
  -AccessCheckDevice <device> <json input file> <json output file>
  
----- Encryption -----
  -PasswordRecoveryRequest <drive path> <json request file>
  -PasswordRecoveryAnswer <drive path> <json answer file>
  
----- Removable -----
  -GetRemovableInfo <drive path> <json output file>
  -EncryptRemovable <drive path> <json input file>
  -UpgradeRemovable <drive path> <json input file>
  -DecryptRemovable <drive path> <json input file>
  -UnlockRemovable <drive path> <json input file>
  -ChangeRemovablePassword <drive path> <json input file>
  -ExportRemovableAccess <drive path> <json input file> <folder>
  
----- Optical Recorder -----
  -GetRecorderLetters <json output file>
  -GetRecorderInfo <drive path> <json output file>
  -GetRecorderMediumInfo <drive path> <json output file>
  -RecordMedium <drive path> <json input file>
  -CancelMediumRecording <drive path>
  -EraseMedium <drive path> <json input file>
  
----- Cryptography -----
  -GenKeyPair <Algorithm Type> [output directory]
    Generates public and private key files named for the algorithm type
    Supported types are: RSA2048, ECDSA256, ECDSA384, ECDSA521
    If no output directory is provided the file will be generated next to RunDll32.exe
    
----- Support -----
  -GatherDiagnoticInfo <output folder location>
  -GetLogging <output file>

Basic checks

For the following commands, we will use the x64 command line, assume the license file is license.lic, and we will use the output on console by providing con as the output file. Each command line is followed by a example of an output, sometimes partial if too big (such as the list of AD groups).

Get Health

Provides statuses for service and kernel components.

HSDC64Cmd.exe -License license.lic -GetHealth con

Copy
{
   "hardening" : [
      {
         "salt" : "3289H-U67D9-KU16W-43LL6-3NWX5",
         "state" : "off"
      }
   ],
   "health" : "ok",
   "services" : [
      {
         "bitness" : "64",
         "filename" : "\\SystemRoot\\system32\\drivers\\sk.sys",
         "name" : "sk",
         "status" : "running",
         "version" : "5.3.39"
      },
      {
         "bitness" : "64",
         "filename" : "C:\\Program Files\\Ivanti\\Device and Application Control\\Client\\scomc.exe",
         "name" : "scomc",
         "status" : "running",
         "version" : "5.3.51"
      },
      {
         "bitness" : "64",
         "filename" : "\\SystemRoot\\system32\\DRIVERS\\sk_ndis.sys",
         "name" : "sk-ndis-lwf",
         "status" : "running",
         "version" : "4.6.505"
      },
      {
         "bitness" : "64",
         "filename" : "\\SystemRoot\\system32\\drivers\\skfs.sys",
         "name" : "skfs",
         "status" : "running",
         "version" : "5.3.39"
      }
   ]
}

Get Connectivity Status

Indicates if the agent is applying online or offline policies.

HSDC64Cmd.exe -License license.lic -GetConnectivityStatus con

Copy
{
   "mode" : "server",
   "state" : "offline"
}

Get Security Identities

Provides information on the current user record, its SID, and all its resolved groups.

HSDC64Cmd.exe -License license.lic -GetSecurityIdentities con

Copy
{
   "identities" : [
      {
         "domain" : "IDACCLT",
         "group" : false,
         "name" : "Administrator",
         "sid" : "S-1-5-21-3241571410-2056676255-2947696630-500",
         "type" : "local"
      },
      ...
      {
         "group" : true,
         "name" : "Everyone",
         "sid" : "S-1-1-0",
         "type" : "wellknown"
      },
      ...
      {
         "domain" : "BUILTIN",
         "group" : true,
         "name" : "Administrators",
         "sid" : "S-1-5-32-544",
         "type" : "wellknown"
      },
      ...
      {
         "domain" : "Mandatory Label",
         "enabled" : false,
         "group" : true,
         "name" : "High Mandatory Level",
         "sid" : "S-1-16-12288",
         "type" : "wellknown"
      }
   ],
   "restricted" : false
}

Note that enabled is set to false on the High Mandatory Level, which means the user didn't pass UAC, meaning policies on Administrators won't apply.

Get Certificates

Enables you to retrieve user or machine certificates.

HSDC64Cmd.exe -License license.lic -GetCertificates input.json con

Input file describes the filters as:

Copy
{
    "user": true,
    "local": true,
    "private": true
}

with parameters as:

  • user (true: user store, false: machine store),
  • local (false: also call Active Directory in addition of the local store),
  • private (true: keeps only certificates for which a private key is available),
  • identity (only required if listing the public certificates of another user from Active Directory, the value is either an SID or a domain\user name).

Here is an example of current user certificates for which a private key is available:

Copy
{
   "certificates" : [
      ...
      {
         "algorithm" : {
            "asymmetric" : "RSA",
            "bits" : 2048,
            "hash" : "SHA1",
            "identifier" : "1.2.840.113549.1.1.1"
         },
         "from" : "2020-06-17T11:16:09.000Z",
         "issuer" : "Hubert.Bonisseur",
         "private" : true,
         "serial" : "D0DBADC7CA00F34C98F6B94621D2B247",
         "subject" : "Hubert.Bonisseur",
         "thumbprints" : {
            "sha1" : "0427D86DA83D93851F3E57F5F9A9205976BC50AE",
            "sha256" : "BEBE73D0AD17CEDD80735B1C77AE289947D2FC0DF66C7D36BA2B8CEE529632D1"
         },
         "until" : "2120-05-24T11:16:09.000Z",
         "valid" : true,
         "version" : 2
      }
   ]
}

Get Inventory

Either provides a list of devices that have been plugged or are plugged on the machine.

HSDC64Cmd.exe -License license.lic -GetInventoryAll con

HSDC64Cmd.exe -License license.lic -GetInventoryOnline con

Copy
{
   "devices" : [
      ...
      {
         "bus" : "64",
         "class" : "3",
         "classguid" : "{4D36E967-E325-11CE-BFC1-08002BE10318}",
         "devicename" : "\\Device\\Harddisk0\\DR0",
         "drivername" : "\\Driver\\disk",
         "fdocharacteristics" : "256",
         "fdotype" : "7",
         "hardwareid" : "IDE\\DiskVirtual_HD______________________________1.1.0___",
         "instances" : [ "5&1278c138&0&0.0.0" ],
         "name" : "Virtual HD ATA Device, Disk drive, (Standard disk drives)",
         "pdocharacteristics" : "256",
         "pdotype" : "45",
         "timestamp" : "2021-04-26T08:10:30Z"
      },
      ...
      {
         "bus" : "64",
         "class" : "1",
         "classguid" : "{4D36E965-E325-11CE-BFC1-08002BE10318}",
         "devicename" : "\\Device\\CdRom0",
         "drivername" : "\\Driver\\cdrom",
         "fdocharacteristics" : "256",
         "fdotype" : "50",
         "hardwareid" : "IDE\\CdRomMsft_Virtual_CD/ROM_____________________1.0_____",
         "instances" : [ "5&253271dd&0&1.0.0" ],
         "name" : "Msft Virtual CD/ROM ATA Device, CD-ROM Drive, (Standard CD-ROM drives)",
         "pdocharacteristics" : "257",
         "pdotype" : "45",
         "timestamp" : "2021-04-26T08:10:35Z"
      },
      ...
   ]
}

Get Logging

To detect if the SComC service is logging and where.

HSDC64Cmd.exe -License license.lic -GetLogging con

Copy
{
   "scomc" : {
      "fileName" : "c:\\temp\\scomc.log",
      "toConsole" : true,
      "toDbwin" : false,
      "toFile" : true
   }
}

Test and Dump policy

Here we both sign and import a policy. This is done for test purposes, as a private key is not meant to be available on the agent, normal usage would be to use -ImportJsonPolicy.

HSDC64Cmd.exe -License license.lic -RSAPrivateKey sx-private.key -RSAPublicKey sx-public.key -SignImportJsonPolicy "e01 - Policy Default.json"

When policies are properly imported, SComC stores the results in SystemRoot%\sxdata\cchv0030.cch and SystemRoot%\sxdata\skrv0020.skr.

Support may ask you to dump the content of these 2 files with the following commands:

HSDC64Cmd.exe -License license.lic -DumpPolicies C:\Windows\sxdata\cchv0030.cch con

HSDC64Cmd.exe -License license.lic -DumpPolicies C:\Windows\sxdata\skrv0020.skr con

You must use the 64-bit command on 64-bit operating systems, and the 32-bit command on 32-bit operating systems for the later one.

Using the C interface

In addition to the binaries mentioned in the deployment, the SDK contains the following items which are needed to build and link a C program using the API:

  • header files gathering the function descriptions and the error codes as they appear in this documentation
  • lib file for the link stage unless the developer decides to do late binding (if needed, a .lib file can be generated from a .def, which can be extracted from a .dll, Stackoverflow article)

Example 1: Get the license and list all strings

First example initialize the engine from a file, get all strings and cleanup the allocation then the engine.

Here the license is stored in a simple char array, but integrator is encourage do some obfuscation to prevent a license leak that could lead to its revocation in a future API version.

Copy
#include <iostream>
#include <fstream>
#include <vector>
#include "HSDC_Sdk.h"

// It's important to fetch the license from the file 
// exactly, byte-for-byte. To that end you must use binary 
// mode when reading any files. 
std::vector<char> getLicense( const std::string& filepath ) 
{
    // Binary mode and seek to eof.
    std::ifstream licFile( filepath, std::ios::binary | std::ios::ate );
    if( !licFile.is_open() ) {
        //error handling
    }
    std::ifstream::pos_type pos = licFile.tellg();
    std::vector<char> license(pos);
    licFile.seekg(0, ios::beg);
    licFile.read(&license[0], pos);
    return license;
}

void Test()
{   
    auto license = getLicense("c:\\dcapi\\endpoint.lic");
    HSDCError err = HSDCStartup(
        HSDCVersion_1_0, 
        "{ \"registry\":\"Software\\\\Lumension Security\\\\API\", \"strings\":true }", 
        &license[0]);
    // error handling here …
    
    char* jstr = nullptr;
    err = HSDCGetStrings(&jstr);
    // error handling here …
    
    printf(jstr);
    
    err = HSDCFreeObject(jstr);
    // error handling here …
    
    err = HSDCCleanup();
    // error handling here …
}

Example 2: Getting status

This example initializes the engine, then gets the health, hardening, and connectivity statuses, and then cleans up the allocation then the engine.

Copy
#include "HSDC_Sdk.h"

static char license[1236] = {
    …, 0x00
};

void Test()
{
    HSDCError err = HSDCStartup(
        HSDCVersion_1_0, 
        "{ \"registry\":\"Software\\\\Lumension Security\\\\API\", \"strings\":true }", 
        license);
    // error handling here …
    
    DWORD status = 0, hardening = 0;
    char* jstr = nullptr;
    err = HSDCGetHealth(&status, &hardening, &jstr);
    // error handling here …
    
    printf("Status %d\r\n", status);
    printf("Hardening %d\r\n", hardening);
    printf(jstr);
    
    err = HSDCFreeObject(jstr);
    // error handling here …
    
    status = 0;
    jstr = nullptr;
    err = HSDCGetConnectivityStatus(&status, &jstr);
    // error handling here …
    
    printf(jstr);
    
    err = HSDCFreeObject(jstr);
    // error handling here …
    
    err = HSDCCleanup();
    // error handling here …
}

Example 3: Building inventory

This example initializes the engine, gets the inventory, then cleans up the allocation then the engine.

Copy
#include "HSDC_Sdk.h"

static char license[1236] = {
    …, 0x00
};

void Test()
{
    HSDCError err = HSDCStartup(
        HSDCVersion_1_0, 
        "{ \"registry\":\"Software\\\\Lumension Security\\\\API\", \"strings\":true }", 
        license);
    // error handling here …
    
    char* jstr = nullptr;
    err = HSDCGetInventory(HSDCInventoryStatusOnline, &jstr);
    // error handling here …
    
    printf(jstr);
    
    err = HSDCFreeObject(jstr);
    // error handling here …
    
    err = HSDCCleanup();
    // error handling here …
}

Example 4: Importing a JSON policy

This example initializes the engine, gets the inventory and then cleans up. The process for applying a policy is:

  1. Create your signing keys.
    • The private key is used to sign the policy, indicating that it originated at the "server".
    • The public key is used by the agent to verify the private key signature on the policy.
  2. Create a JSON policy.
    • Remember to set the unique GUID value. If this is not unique, the agent will presume that it has received this policy already. We plan to replace this GUID requirement in future by using a SHA-based identity system (like git)
    • Do not use a policy with a date-time older than 2 weeks. These will be discarded.
    • The policy is NOT currently cumulative, and so a full policy file is expected.
    • Be careful. You can lock out your computer by specifying the wrong values. We highly recommend that you use a virtual machine if you are not sure of what you are doing.
  3. Import the policy.
Copy
#include "HSDC_Sdk.h"

// It's important to fetch these files exactly, byte-for-byte. 
// To that end you must use binary mode when reading any files. 
std::vector<char> ReadFile( const std::string& filepath ) 
{
    std::ifstream file( filepath, std::ios::binary | std::ios::ate );
    if( !file.is_open() ) {
        //error handling
    }
    std::ifstream::pos_type pos = file.tellg();
    std::vector<char> bytes(pos);
    file.seekg(0, ios::beg);
    file.read(&bytes[0], pos);
    return bytes;
}

void Test()
{
    std::vector<unsigned char> license = ReadFile(L"C:\\Secured\\sx-private.key");
    HSDCError err = HSDCStartup(
        HSDCVersion_1_0, 
        "{ \"registry\":\"Software\\\\Lumension Security\\\\API\", \"strings\":true }", 
        &license[0]);
    // error handling here …
    
    const wchar_t* test = L"D:\\JSONTests\\06 - Policy Floppy.json";
    std::vector<unsigned char> privateKey = ReadFile(L"C:\\Secured\\sx-private.key");
    std::vector<unsigned char> publicKey = ReadFile(L"C:\\Windows\\sxdata\\sx-public.key");
    std::vector<unsigned char> policy = ReadFile(test);
    
    std::vector<unsigned char> signature(512, 0); // more than needed
    int signatureLength = signature.size();
    err = HSDCCryptoSignBuffer(L"rsa2048", &policy[0], policy.size(), 
        &privateKey[0], privateKey.size(), &publicKey[0], publicKey.size(), 
        &signature[0], &signatureLength);
    // error handling here …
    
    signature.resize(signatureLength);
    
    err = HSDCPolicyImport((char*)&policy[0], &signature[0], signature.size());
    // error handling here …
    
    err = HSDCCleanup();
    // error handling here …
}

Example 5: Processing logs

This example initializes the engine, registers the log callback, and prints the payloads upon reception.

Note:

  1. The first call is happening up to 30 seconds after the registration.
  2. The files processed are located in the C:\Windows\sxData\Shadow folder (default) with a .log.final extension.
Copy
#include "HSDC_Sdk.h"

static char license[1236] = {
    …, 0x00
};

void HSDCAPI LogProcessorCallBack(DWORD cookie, const char* jstr)
{
    printf("Log Notification, Cookie %u\n%s\n", cookie, jstr);
}

void Test()
{
    HSDCError err = HSDCStartup(
        HSDCVersion_1_0, 
        "{ \"registry\":\"Software\\\\Lumension Security\\\\API\", \"strings\":true }", 
        license);
    // error handling here …
    
    DWORD cookieLogs = 0;
    err = HSDCRegisterLogProcessor(&cookieLogs, LogProcessorCallBack);
    // error handling here …
    
    ::MessageBox(NULL, L"wait...", L"The End!", MB_OK);
    
    err = HSDCUnregisterLogProcessor(cookieLogs); 
    // error handling here …
    
    err = HSDCCleanup();
    // error handling here …
}

Example 6: Processing shadow files

This example initialize the engine, registers the shadow file callback, print the payloads upon reception and extract the content.

Note:

  • The first call is happening up to 30 seconds after the registration.
  • The files processed are located in the C:\Windows\sxData\Shadow folder (default) with a .dat.final extension.
Copy
#include "HSDC_Sdk.h"

static char license[1236] = {
    …, 0x00
};

HSDCError HSDCAPI ShadowStreamCallBack(unsigned long long offset, const unsigned char* buffer, unsigned int length, void* parameters)
{
    HANDLE hFile = (HANDLE)parameters;
    // parameter validation here …
    
    DWORD written = 0;
    BOOL res = ::WriteFile(hFile, buffer, length, &written, nullptr);
    // error handling here …
    
    return HSDCErrorSuccess;
}

void HSDCAPI ShadowProcessorCallBack(DWORD cookie, const char* jstr, const wchar_t* filename)
{
    printf("Shadow Notification, Cookie %u\nFilename %s\n%s\n", cookie, filename, jstr);
    
    HFILE  h = INVALID_HANDLE_VALUE;
    // opening/creating output file here …
    
    if (h != INVALID_HANDLE_VALUE)
    {
        char* jstr2 = nullptr;
        HSDCError err = HSDCExtractShadowStream(filename, &jstr2, ShadowStreamCallBack, h);
        // error handling here …
        
        err = HSDCFreeObject(jstr2);
        // error handling here …
        
    }
    
    // closing handle here …
}

void Test()
{
    HSDCError err = HSDCStartup(
        HSDCVersion_1_0, 
        "{ \"registry\":\"Software\\\\Lumension Security\\\\API\", \"strings\":true }", 
        license);
    // error handling here …
    
    DWORD cookieShadows = 0;
    err = HSDCRegisterShadowProcessor(&cookieShadows, ShadowProcessorCallBack);
    // error handling here …
    
    ::MessageBox(NULL, L"wait...", L"The End!", MB_OK);
    err = HSDCUnregisterShadowProcessor(cookieShadows);
    // error handling here …
    
    err = HSDCCleanup();
    // error handling here …
}

Example 7: Handling Errors

All API calls return a member of the HSDCError enum as defined in the header file described in HSDC_Sdk_Errors.h. Also included in that header are names for the enums and their count. The user does not need to use these directly, however, and can use the HSDC_ErrorGetName function instead to translate the enum to a string for display or logging. This is declared in the header described in HSDC_Sdk.h.

Copy
void RegisterCallback(){
    DWORD cookieLogs = 0;
    err = HSDCRegisterLogProcessor(&cookieLogs, LogProcessorCallBack);
    Logger::LogInfo( "The operation returned %s", HSDCErrorGetName(err) );
}

Using the .Net interface

The HSDCNet.dll exposes the HSDC.API class which is calling the unmanaged dll using dllimport.

Notes:

  • Error codes returned in C++ are thrown using HSDC.APIException objects.
  • Memory management is handled internally; regular .Net values and objects are returned.
  • The Library is built with .Net Framework 3.5 Client Profile, and handles both 32- and 64-bit environments.

Example 1: Notifications, Logs, and Shadows subscription

This example subscribe to all event-driven actions exposed by the API to display logs, shadows, and notifications.

Copy
private static void Main(string[] args)
{
    try
    {
        HSDC.API.Startup(HSDC.Version.v1, null, ASCIIEncoding.ASCII.GetString(license));
        
        //string dictionnary = HSDC.API.GetStrings();
        
        UInt32 logCookie = HSDC.API.RegisterLogProcessor(
            (cookie, jstr) =>
            {
                Console.WriteLine("LogProcessor notification {0}\r\n{1}", cookie, jstr);
                Console.WriteLine("Press any key to exit...");
            });
            
        UInt32 shadowCookie = HSDC.API.RegisterShadowProcessor(
            (cookie, jstr, filename) =>
            {
                Console.WriteLine("ShadowProcessor notification {0}\r\n{1}\r\n{2}", cookie, jstr, filename);
                
                string extracted = String.Format("{0}.extracted", Path.GetFileNameWithoutExtension(filename));
                using (FileStream fs = File.Create(extracted))
                {
                    string jstr2 = string.Empty;
                    HSDC.API.ExtractShadowStream(
                        filename,
                        ref jstr2,
                        (offset, buffer, p) =>
                        {
                            Console.Write(".");
                            fs.Write(buffer, 0, buffer.Length);
                            
                            return (UInt32)HSDC.Error.Success;
                        },
                        IntPtr.Zero);
                    Console.WriteLine();
                    
                    fs.Seek(0, SeekOrigin.Begin);
                    SHA1 sha1 = new SHA1Cng();
                    byte[] sha1value = sha1.ComputeHash(fs);
                    Console.WriteLine("SHA-1: {0}", BitConverter.ToString(sha1value).Replace("-", ""));
                    
                    fs.Seek(0, SeekOrigin.Begin);
                    SHA256 sha256 = new SHA256Cng();
                    byte[] sha256value = sha256.ComputeHash(fs);
                    Console.WriteLine("SHA-256: {0}", BitConverter.ToString(sha256value).Replace("-", ""));
                }
                
                Console.WriteLine("Press any key to exit...");
            });
        UInt32 notificationCookie = HSDC.API.RegisterNotificationProcessor(
            (cookie, session, jstr) =>
            {
                Console.WriteLine("NotificationProcessor notification {0} in session {1}\r\n{2}", cookie, session, jstr);
                Console.WriteLine("Press any key to exit...");
            });
            
        //HSDC.API.FetchLogs(true, 5);
        
        Console.WriteLine("Press any key to exit...");
        System.Console.ReadKey(true);
        
        HSDC.API.UnregisterNotificationProcessor(notificationCookie);
        HSDC.API.UnregisterShadowProcessor(shadowCookie);
        HSDC.API.UnregisterLogProcessor(logCookie);
        HSDC.API.Cleanup();
    }
    catch (Exception e)
    {
        Console.WriteLine("{0}\r\n{1}\r\n{2}", e.GetType().Name, e.Message, e.StackTrace);
    }
}