Créer des exécutables liés au runtime par défaut du système

vehicule
parce que c’est plus léger, et plus portable…

Reprenons notre code « simple » :

#include <stdio.h>

int main(int argc, char **argv)
{
	printf("Hello Kiwi\n");
	return 0;
}

Introduction

Afin de construire cet exécutable, aussi simple soit-il, le compilateur doit savoir ou « trouver » la fonction printf.
printf fait heureusement partis des fonctions « de base » et est inclus dans les runtimes C/C++ de Visual Studio.

Visual Studio devra donc lier notre exécutable à la librairie contenant le code de printf :

sous Visual Studio 2010, avec msvcr100.dll

  Version      : 0
  Machine      : 14C (x86)
  TimeDateStamp: 4DF2B873 Sat Jun 11 02:36:03 2011
  SizeOfData   : 00000015
  DLL name     : MSVCR100.dll
  Symbol name  : _printf
  Type         : code
  Name type    : no prefix
  Hint         : 1495
  Name         : printf

sous Visual Studio 2012, avec msvcr110.dll

  Version      : 0
  Machine      : 14C (x86)
  TimeDateStamp: 50988325 Tue Nov 06 04:25:25 2012
  SizeOfData   : 00000015
  DLL name     : MSVCR110.dll
  Symbol name  : _printf
  Type         : code
  Name type    : no prefix
  Hint         : 1584
  Name         : printf

Mais comment fait Windows pour son fonctionnement interne ?
Nous savons que les runtimes C/C++ de Visual Studio 2010 ou 2012 ne sont pas installés par défaut sous Windows, alors comment fait-il pour se passer de ces librairies ?

Runtimes du système

Prenons un programme, dont nous sommes sûrs qu’il utilise la sortie console (et donc a priori printf) : l’Invite de commandes (cmd.exe).

Dump of file c:\Windows\System32\cmd.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    msvcrt.dll
              4AD01000 Import Address Table
              4AD2286C Import Name Table
              FFFFFFFF time date stamp
              FFFFFFFF Index of first forwarder reference

      6FF636AA    48F exit
      [...]
      6FF5A73F    560 wcsrchr
      6FF59910    4EA memcpy
      6FF6C5B9    4F3 printf
      6FFF2900    1DB _iob
      6FF63E00    49F fprintf
      [...]

Windows utilise donc la librairie msvcrt.dll (%SystemRoot%\System32), soit les runtimes par défaut du système. Ce qui paraît logique…

Cette librairie est très pratique :

  • pas de déploiement de runtimes !
  • nous sommes sûrs de la trouver sur toutes les versions de Windows
  • sauf exceptions, elle répond à la plupart de nos usages

Limitation : pas de librairie de débogage des runtimes système présente sur les systèmes (la compilation et les programmes ne fonctionneront qu’en version « release »).

Comment pourrions-nous aussi en bénéficier ?

  • Utilisons le contenu du Windows Driver Kit !
  • recréons le fichier .lib permettant de nous lier à ce runtime par défaut fastidieux !

Pré-requis

Le Windows Driver Kit (WDK) contient les fichiers .lib nécessaires et il est tout à fait possible de modifier les propriétés de son projet C/C++ pour abandonner l’utilisation des librairies de Visual Studio pour pointer vers celles du WDK.

C’est assez hasardeux ; les résultats ne sont pas toujours au rendez-vous.
Peut-être pouvons-nous faire plus simple et tirer encore plus parti du WDK ?

Les plateformes MsBuild

Microsoft nous offre énormément de fonctionnalités/possibilités avec ses nouvelles versions de Visual Studio (2010, 2012…).
L’un d’elle est de pouvoir personnaliser ses environnements de compilation.

  1. se placer dans le répertoire des définitions des plateformes de compilation : %ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\Platforms (ou ProgramFiles(x86))
  2. décompresser le fichier ddk2003.zip, cela doit donner l’arborescence suivante
    C:\PROGRAM FILES\MSBUILD\MICROSOFT.CPP\V4.0\PLATFORMS
    +---Win32
    |   |
    |   \---PlatformToolsets
    |       +---ddk2003
    |       |       Microsoft.Cpp.Win32.ddk2003.props
    |       |       Microsoft.Cpp.Win32.ddk2003.targets
    |       |
    |       +---v100
    |       \---v90
    |
    \---x64
        |
        \---PlatformToolsets
            +---ddk2003
            |       Microsoft.Cpp.x64.ddk2003.props
            |       Microsoft.Cpp.x64.ddk2003.targets
            |
            +---v100
            \---v90
  3. éventuellement, modifier les fichiers .props pour pointer sur un autre emplacement du WDK, ou sur d’autres librairies que celles de Windows 2003 (elles ont l’avantage de couvrir l’ensemble des versions de Windows)

Visual Studio

Il suffit de placer le projet en « release » puis de sélectionner les outils de plateforme ddk2003 dans les propriétés du projet :
plateforme
… et de compiler

C:\[...]\Release>ConsoleApplication1.exe
Hello Kiwi

C:\[...]\Release>dumpbin /imports ConsoleApplication1.exe | findstr /i .dll
    msvcrt.dll
    KERNEL32.dll

C:\[...]\Release>dir ConsoleApplication1.exe
[...]
27/05/2013  02:41             7 680 ConsoleApplication1.exe

Notes : l’utilisation du WDK peut nécessiter la récupération de quelques « headers » non présent dans le WDK.

Bonus Visual Studio 2012

Un modèle tout prêt et optimisé pour cette nouvelle plateforme de compilation « Application console Win32-DDK » : console_ddk2003.zip.
A poser (non décompressé) dans le répertoire : %userprofile%\Documents\Visual Studio 2012\Templates\ProjectTemplates

template_c

Visual Studio – Compilation C, dépendances et taille d’exécutable

Quelques données brutes post-compilation d’un code source simple :

#include <stdio.h>

int main(int argc, char **argv)
{
	printf("Hello Kiwi\n");
	return 0;
}

Rien de particulier, ce code devrait juste donner un exécutable des plus léger.

Compilation dynamique

Plateforme Taille Dépendances
VC9 (Visual Studio 2008) 7 168 msvcr90.dll
kernel32.dll
VC10 – (Visual Studio 2010) 6 144 msvcr100.dll
kernel32.dll
VC11 – (Visual Studio 2012) 7 168 msvcr110.dll
kernel32.dll
  • Avantage : code très léger
  • Inconvénient : nécessite la présence de runtimes spécifiques sur les environnements ciblés

Compilation statique

Plateforme Taille Dépendances
VC9 – (Visual Studio 2008) 53 248 kernel32.dll
VC10 – (Visual Studio 2010) 46 080 kernel32.dll
VC11 – (Visual Studio 2012) 62 976 kernel32.dll
  • Avantage : plus de dépendances aux runtimes !
  • Inconvénient : taille conséquente ! le contenu des librairies est incorporé au binaire

Compromis

Plateforme Taille Dépendances
WDK 7600.16385.1 6 144 msvcrt.dll
kernel32.dll
  • Avantage : code très léger ! (utilise les runtimes du système, plus de dépendances externes)
  • Inconvénient : certaines fonctionnalités récentes ne sont pas disponibles dans ce runtime

Cryptographie rapide sous Windows

Comment obtenir un hash MD5 sous Windows, déchiffrer un bloc DES ?

j’ai totalement conscience d’évoquer dans ce post des algorithmes considérés comme dépassés, mais ces derniers sont toujours massivement utilisés dans les mécanismes internes de Windows
mimikatz 2.0 s’en sert lui aussi massivement ;)

Limitations

Les algorithmes évoqués dans ce post sont seulement les suivants :

MD4 LM DES (ECB)
MD5 NTLM RC4
SHA1    

Déjà vu sur Internet *

  • assemblage de bouts de code provenant d’Internet (réimplémentation des algorithmes)
  • utilisation des librairies d’OpenSSL
  • utilisation de la CryptoAPI
  • utilisation de la CNG

* je passe sur l’utilisation de CSP/PKCS#11 pour communiquer avec son HSM préféré ;)

A notre disposition sous Windows

Comme déjà listé, il est possible d’utiliser nativement la CryptoAPI, ou sa nouvelle version la CNG… mais n’est-ce-pas un peu lourd pour de si simples besoins ?

Windows incorpore déjà beaucoup de routines dans ses librairies système et, si l’on en utilise une grande partie via les SDK référençant ces API, l’on en occulte quelques-unes très pratiques !

Une majorité de ces API se situent dans la librairie advapi32.

API clairement identifiées

MD4Init MD5Init A_SHAInit
MD4Update MD5Update A_SHAUpdate
MD4Final MD5Final A_SHAFinal

API derrières SystemFunctionXXX

RtlEncryptDES1block1key SystemFunction001
RtlDecryptDES1block1key SystemFunction002
RtlEncryptDESMagicBlock1key SystemFunction003
RtlEncryptDESblocksECB SystemFunction004
RtlDecryptDESblocksECB SystemFunction005
RtlDigestLM SystemFunction006
RtlDigestNTLM SystemFunction007
RtlLMResponseToChallenge
NTLMv1 aussi sans doute
SystemFunction008
SystemFunction009
RtlDigestMD4only16Bytes SystemFunction010
SystemFunction011
RtlEncryptDES2blocks2keys SystemFunction012
SystemFunction014
SystemFunction020
SystemFunction022
RtlDecryptDES2blocks2keys SystemFunction013
SystemFunction015
SystemFunction021
SystemFunction023
RtlEncryptDES2blocks1key SystemFunction016
SystemFunction018
RtlDecryptDES2blocks1key SystemFunction017
SystemFunction019
RtlEncryptDES2blocks1DWORD SystemFunction024
SystemFunction026
RtlDecryptDES2blocks1DWORD SystemFunction025
SystemFunction027
RtlEqualMemory16Bytes SystemFunction030
SystemFunction031
RtlEncryptDecryptRC4 SystemFunction032
SystemFunction033
RtlCheckSignatureInFile SystemFunction035
Clé de session RPC
fonctions non identiques
SystemFunction028
SystemFunction029
SystemFunction034

Téléchargement

La majorité des API sont référencées via des SystemFunctionXXX qui peuvent déjà être liées par la librairie advapi32.lib du SDK ou WDK.

Malheureusement pas les API A_SHAFinal, A_SHAInit, A_SHAUpdate, MD4Final, MD4Init, MD4Update, MD5Final, MD5Init, MD5Update et RtlCheckSignatureInFile (SystemFunction035).

J’ai donc créé trois fichiers :

Exemple de petit code

Il est là pour exposer l’utilisation du CRYPTO_BUFFER, du MD5, SHA1 et DES-ECB

const BYTE data[] = "my super secret is a kiwi :)";
const BYTE key[] = "42 & 0xdeadbeef of course !";

CRYPTO_BUFFER
	bData    = {sizeof(data) - 1, sizeof(data) - 1, (PBYTE) data}, 
	bKey     = {sizeof(key) - 1, sizeof(key) - 1, (PBYTE) key},
	bCipData = {0, 0, NULL},
	bDecData = {0, 0, NULL}
;

MD5_CTX md5ctxInput, md5ctxOutput; // MD5 digest in context
SHA_CTX shactxInput, shactxOutput;
SHA_DIGEST shaInput, shaOutput;

NTSTATUS status;

MD5Init(&md5ctxInput);
MD5Update(&md5ctxInput, bData.Buffer, bData.Length);
MD5Final(&md5ctxInput);	// original data MD5 hash

A_SHAInit(&shactxInput);
A_SHAUpdate(&shactxInput, bData.Buffer, bData.Length);
A_SHAFinal(&shactxInput, &shaInput); // original data SHA1 hash

status = RtlEncryptDESblocksECB(&bData, &bKey, &bCipData);
if(status == STATUS_BUFFER_TOO_SMALL)
{
	if(bCipData.Buffer = (PBYTE) LocalAlloc(LPTR, bCipData.Length))
	{
		bCipData.MaximumLength = bCipData.Length;
		status = RtlEncryptDESblocksECB(&bData, &bKey, &bCipData);
	}
} else status = STATUS_NONCONTINUABLE_EXCEPTION;

if(NT_SUCCESS(status)) // Cipher operation OK ?
{
	status = RtlDecryptDESblocksECB(&bCipData, &bKey, &bDecData);
	if(status == STATUS_BUFFER_TOO_SMALL)
	{
		if(bDecData.Buffer = (PBYTE) LocalAlloc(LPTR, bDecData.Length))
		{
			bDecData.MaximumLength = bDecData.Length;
			status = RtlDecryptDESblocksECB(&bCipData, &bKey, &bDecData);
		}
	} else status = STATUS_NONCONTINUABLE_EXCEPTION;
} 

if(NT_SUCCESS(status)) // Decipher operation OK ?
{
	MD5Init(&md5ctxOutput);
	MD5Update(&md5ctxOutput, bDecData.Buffer, bDecData.Length);
	MD5Final(&md5ctxOutput); // deciphered data MD5 hash

	A_SHAInit(&shactxOutput);
	A_SHAUpdate(&shactxOutput, bDecData.Buffer, bDecData.Length);
	A_SHAFinal(&shactxOutput, &shaOutput); // deciphered data SHA1 hash
		
	// data compare works too, but it's for expose MD5 & SHA1 functions ;)
	wprintf(L"MD5  match : %s\n",
		RtlEqualMemory(md5ctxInput.digest, md5ctxOutput.digest, MD5_DIGEST_LENGTH) ? L"OK" : L"KO");
	wprintf(L"SHA1 match : %s\n",
		RtlEqualMemory(shaInput.digest, shaOutput.digest, SHA_DIGEST_LENGTH) ? L"OK" : L"KO");
}

LocalFree(bDecData.Buffer);
LocalFree(bCipData.Buffer);

Correction d’un en-tête Windows

Les seules API un tant soit peu présentes dans les en-têtes standards de Windows sont les suivantes :

  • RtlEncryptMemory
  • RtlDecryptMemory
  • RtlGenRandom

Lors de l’utilisation de ces fonctions, avec les fichiers proposés ou non, si les erreurs suivantes apparaissent lors de la création des liens :

error LNK2001: symbole externe non résolu _SystemFunction036
error LNK2001: symbole externe non résolu _SystemFunction040
error LNK2001: symbole externe non résolu _SystemFunction041

C’est que l’en-tête ntsecapi.h déclare incorrectement les prototypes des fonctions.
Il faut malheureusement les corriger :

BOOLEAN
RtlGenRandom(
    __out_bcount(RandomBufferLength) PVOID RandomBuffer,
    __in ULONG RandomBufferLength
    );
//...
NTSTATUS
RtlEncryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

NTSTATUS
RtlDecryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

en rajoutant la convention d’appel WINAPI (__stdcall) :

BOOLEAN WINAPI
RtlGenRandom(
    __out_bcount(RandomBufferLength) PVOID RandomBuffer,
    __in ULONG RandomBufferLength
    );
//...
NTSTATUS WINAPI
RtlEncryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

NTSTATUS WINAPI
RtlDecryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

Visual Studio 2012 (Express)

Juste quelques petits liens pratiques :

Les versions Express des outils Microsoft sont des versions allégées, sans limite d’utilisation dans le temps : http://www.microsoft.com/express

vs2012

Certaines fonctionnalités ne sont pas disponibles : http://msdn.microsoft.com/library/hs24szh9.aspx (comme la compilation x64 ou les MFC pour Visual C++)

En bonus pratique :

En ce qui concerne l’Update 1 ou 2, le SDK et le WDK, il n’existe pas de version autonomes de ces programmes, mais elles peuvent être construites via le commuatateur /layout

DHCP sous Windows – Callout

Les serveurs DHCP Microsoft, à partir de Windows 2003 Server, disposent d’une fonctionnalité très intéressante et rarement utilisée : le callout

Cette fonctionnalité, très bien documentée (http://msdn.microsoft.com/library/windows/desktop/aa363372.aspx), permet de notifier des librairies externes du traitement de requêtes DHCP.
Une fois n’est pas coutume, Microsoft laisse l’opportunité à ces librairies d’altérer les paquets, requêtes, et même de stopper le traitement d’une requête !

Le besoin

Pour une raison quelconque, j’ai du réfléchir à une problématique : comment ne pas distribuer d’adresses IP à une certaine catégorie de machines pouvant se brancher sur le même réseau physique/logique que des machines légitimes.

Des solutions existent déjà :

  • IpSec
  • NAC

et même d’autres moins efficaces (*) :

  • IP fixe pour tout le réseau
  • Blacklistage des adresses MAC sur les équipements frontaux
  • Réservations DHCP pour tous les postes

…Inapplicables pour diverses raisons sur l’environnement ciblé.
(*) L’on part ici du principe qu’une personne prenant volontairement une IP du réseau d’entreprise ou changeant son adresse MAC, commet une action malveillante. Cela arrive rarement par hasard.

Un début de solution

Quelques recherches Google mènent obligatoirement vers cette entrée Technet : http://blogs.technet.com/b/teamdhcp/archive/2007/10/03/dhcp-server-callout-dll-for-mac-address-based-filtering.aspx

L’on y apprend que le blacklistage d’adresses MAC au niveau DHCP n’est pas un besoin loufoque et que Microsoft a « développé » une librairie pouvant prendre en charge une liste noire ou blanche d’adresses.
A priori celle-ci est maintenant devenue une fonctionnalité native du service DHCP de Windows Server 2008 r2.

Vu le nombre de commentaires exprimant des problématiques de mise en service, et ce genre d’avertissements Microsoft :

The current callout DLL shall no longer be available after December 15, 2010.
[…]
Known Issue:

  1. This callout dll may not work on localized builds (non english builds).

… cela n’encourage guère à son utilisation !

Un peu de code

Grâce au MSDN : http://msdn.microsoft.com/library/windows/desktop/aa363373.aspx, l’on peut rapidement coder sa propre librairie (ici sans fichier de log ou de paramétrages … (pas envie, pas besoin…)

/*	Benjamin DELPY `gentilkiwi`
	http://blog.gentilkiwi.com
	benjamin@gentilkiwi.com
	Licence : http://creativecommons.org/licenses/by-nc-sa/3.0/fr/
*/
#include <windows.h>
#include <dhcpssdk.h>

#define MAC_ADDRESS_SIZE			6
#define MAC_SOURCE_ADDRESS_OFFSET	28

const BYTE macToBlack[][MAC_ADDRESS_SIZE] = {
	{0x00, 0x0c, 0x29, 0x00, 0x00, 0x00},
	{0x00, 0x50, 0x56, 0x00, 0x00, 0x00}
};

HMODULE nextLibrary = NULL;
LPDHCP_NEWPKT nextLibraryCalloutNewPkt = NULL;

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	if(ul_reason_for_call == DLL_PROCESS_DETACH && nextLibrary)
		FreeLibrary(nextLibrary);

	return TRUE;
}

DWORD CALLBACK DhcpNewPktHook(IN OUT LPBYTE *Packet, IN OUT DWORD *PacketSize, IN DWORD IpAddress, IN LPVOID Reserved, IN OUT LPVOID *PktContext, OUT LPBOOL ProcessIt)
{
	DWORD retour = ERROR_SUCCESS, m;
	*ProcessIt = TRUE;

	for(m = 0; m < sizeof(macToBlack) / MAC_ADDRESS_SIZE; m++)
	{
		if(RtlEqualMemory(*Packet + MAC_SOURCE_ADDRESS_OFFSET, macToBlack[m], MAC_ADDRESS_SIZE / 2))
		{
			*ProcessIt = FALSE;
			retour = DHCP_DROP_INVALID;
			break;
		}
	}

	if(*ProcessIt && nextLibraryCalloutNewPkt)
		retour = nextLibraryCalloutNewPkt(Packet, PacketSize, IpAddress, Reserved, PktContext, ProcessIt);
	
	return retour;
}

DWORD CALLBACK DhcpServerCalloutEntry(IN LPWSTR ChainDlls, IN DWORD CalloutVersion, IN OUT LPDHCP_CALLOUT_TABLE CalloutTbl)
{
	LPDHCP_ENTRY_POINT_FUNC nextEntry;

	RtlZeroMemory(CalloutTbl, sizeof(DHCP_CALLOUT_TABLE));

	if(nextLibrary = LoadLibrary(ChainDlls))
		if(nextEntry = (LPDHCP_ENTRY_POINT_FUNC) GetProcAddress(nextLibrary, DHCP_CALLOUT_ENTRY_POINT))
			nextEntry(ChainDlls + wcslen(ChainDlls) + 1, CalloutVersion, CalloutTbl);

	if(CalloutTbl->DhcpNewPktHook)
		nextLibraryCalloutNewPkt = CalloutTbl->DhcpNewPktHook;
	CalloutTbl->DhcpNewPktHook = DhcpNewPktHook;

	return ERROR_SUCCESS;
}

Sans oublier notre export C d’un __stdcall

LIBRARY
EXPORTS
	DhcpServerCalloutEntry = DhcpServerCalloutEntry

Cette librairie rejette ainsi les demandes émanant d’adresse MAC commençant par 00:0c:29 ou 00:50:56, soit provenant de machines virtuelles VMware.

Installation

Sous HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\DHCPServer\Parameters

  • CalloutEnabled de type DWORD, à 1 pour activer les Callouts, 0 pour les désactiver
  • CalloutDlls de type REG_MULTI_SZ, contenant la liste des librairies de Callouts (séparées par un saut de ligne)

dhcp_callout

Le service DHCP (DHCPServer) doit être redémarré pour prendre en charge ces modifications.

Test

C:\Documents and Settings\Administrateur>ipconfig /renew

Configuration IP de Windows

Une erreur s'est produite lors du renouvellement de l'interface Connexion au réseau local : impossible de contacter votre serveur DHC. Le délai d'attente de la demande est dépassé.

Diaboliquement efficace pour quelques lignes :)