CLR via Windbg - string vs securestring

Ayer en el Octoberconference organizado por el grupo de usuarios de Málaga tuve la oportunidad de dar una charla de depuración en entornos de producción. Es una sesión muy especial que me trae muy gratos recuerdos de mi trabajo en soporte de Microsoft.

Durante la sesión vemos la importancia de la minuciosidad ante un problema en un entorno de producción, captura de volcados de memoria, análisis básico win32 y análisis en .NET.

Los asistentes siempre se sorprenden de la facilidad con la que se puede obtener los valores de las variables (passwords, strings de conexión, requests en asp.net...) en memoria. Unos 10 minutos antes de empezar, Unai Zorrilla me sugirió que hiciésemos una demo con SecureString para que se viese la utilidad de esa clase.

A si que vamos a por ello...

Partimos de este código

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;

namespace ConsoleApplication1
{
  class Program
   {
     static void Main(string[] args)
     {
       using (SecureString ss = new SecureString())
       {
           string noss = string.Empty;
           while (true)
           {
             ConsoleKeyInfo cki = Console.ReadKey();
             if (cki.Key == ConsoleKey.Enter) break;
             ss.AppendChar(cki.KeyChar);
             noss += cki.KeyChar.ToString();
           }
          Console.WriteLine("secure:"+ss);
          Console.WriteLine("No secure:"+noss);
          Console.ReadLine();
       } 

     }
 }
}

Ejecutamos la aplicación e introducimos algo de texto pulsando enter, vemos los resultados de los Console.WriteLine...y nos quedamos ahi, no damos al segundo enter que si no perdemos la ejecución

Y ahora abrimos nuestro entorno de depuración de volcados favorito, el windbg :)

Pulsamos F6 y seleccionamos el proceso de la aplicación

Según nos adjuntamos al ejecutable, pasamos a cargar la extensión SOS en memoria. Esta hará posible que depuremos aplicaciones .NET desde WinDBG de forma muy sencilla.

.loadby sos mscorwks

Y ahora estamos preparados para investigar la memoria del proceso. Vamos a listar los objetos manejados de los stacks de los diferentes threads (~ = thread,  *=todos, e=ejecutar, !dumpstackobjects = mostrar objetos .net del stack del thread en curso)

~*e !dumpstackobjects

Si cotilleamos un poco la salida encontraremos estos valores (las direcciones hex pueden cambiar)

0580ebdc 0168981c System.String prueba prueba
0580ebe0 01683984 System.Security.SecureString

Son las referencias a los objetos que estamos buscando. Si volcamos el System.String, vemos la siguiente estructura

0:010> !do 0168981c
Name: System.String
MethodTable: 790fcb30
EEClass: 790fca90
Size: 44(0x2c) bytes
GC Generation: 0
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: prueba prueba --> juas que chivato, no? reconoce el string y me lo muestra tal cual :)
Fields:
MT Field Offset Type VT Attr Value Name
791018e0 4000096 4 System.Int32 0 instance 14 m_arrayLength
791018e0 4000097 8 System.Int32 0 instance 13 m_stringLength
790fe534 4000098 c System.Char 0 instance 70 m_firstChar
790fcb30 4000099 10 System.String 0 shared static Empty
>> Domain:Value 00374ea8:790d81bc <<
7912b1d8 400009a 14 System.Char[] 0 shared static WhitespaceChars

Si no nos lo mostrase tal cual, en negroesta resaltado lo que parece ser la dirección de comienzo del string que contiene la memoria (dir base de la clase string + 0x0c). De modo que vamos a volcarla directamente a ver que vemos.

0:010> du 0168981c +0x0c
01689828 "prueba prueba"

Demasiado fácil :P Vamos a volcar ahora el secure string a ver que es lo que vemos...

0:010> !do 01683984
Name: System.Security.SecureString
MethodTable: 791915e4
EEClass: 792316d8
Size: 20(0x14) bytes
GC Generation: 0  //mhhh ya no se chiva :/
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
791916a0 4001e6e 4 ...ty.SafeBSTRHandle 0 instance 01689700 m_buffer
791018e0 4001e6f 8 System.Int32 0 instance 13 m_length
79107584 4001e70 c System.Boolean 0 instance 0 m_readOnly
79107584 4001e71 d System.Boolean 0 instance 1 m_enrypted
79107584 4001e72 b00 System.Boolean 0 shared static supportedOnCurrentPlatform
>> Domain:Value 00374ea8:1 <<

Véis que en este caso no hay nada que cante ni ninguna dirección de donde se pueda rascar, quizás tenga que ver con que SecureString fue el resultado de una petición del gobierno de USA para proteger cierta información en memoria, por eso no se deja ver el contenido :)

Gracias por la idea Unai! (y a los asistentes por aguantarme)

Happy Hacking!

 

PD ->  .detach para liberar el proceso :P