viernes, 13 de abril de 2012

Enumerate COM ports in Windows with Lazarus



As I have only USB ports on my development machine, is quite common at connecting a serial device that I have to go to the device manager to see in which COM port it was installed.


I am developing an application that needs to access a serial device, so I need to get aware of the COM ports installed, either to indicate which to use or to verify the existence of which was already configured.


Researching a bit to not reinvent the wheel, I found this page: http://www.lazarus.freepascal.org/index.php?topic=14313.0 which publishes three interesting functions.

The first function GetSerialPortNames is extracted from the package  synaser (http://synapse.ararat.cz/doku.php/download), returns the COM ports installed on the operating system (in my case: COM3 & COM17). More or less what I need, but only the list without identifying them.

function GetSerialPortNames: string;
var
  reg: TRegistry;
  l, v: TStringList;
  n: integer;
begin
  l := TStringList.Create;
  v := TStringList.Create;
  reg := TRegistry.Create;
  try
{$IFNDEF VER100}
    reg.Access := KEY_READ;
{$ENDIF}
    reg.RootKey := HKEY_LOCAL_MACHINE;
    reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM');//, false);
    reg.GetValueNames(l);
    for n := 0 to l.Count - 1 do
      v.Add(reg.ReadString(l[n]));
    Result := v.CommaText;
  finally
    reg.Free;
    l.Free;
    v.Free;
  end;
end;

The second function GetSerialPortRegNames is a variant of the first in that shows the devices installed (in my case: \ Device \ ProlificSerial0 & \ Device \USBSER000), which is also not very clear.

function GetSerialPortRegNames: string;
var
  reg: TRegistry;
  l  : TStringList;
  n: integer;
begin
  l := TStringList.Create;
//  v := TStringList.Create;
  reg := TRegistry.Create;
  try
{$IFNDEF VER100}
    reg.Access := KEY_READ;
{$ENDIF}
    reg.RootKey := HKEY_LOCAL_MACHINE;
    reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM');//, false);
    reg.GetValueNames(l);
//    for n := 0 to l.Count - 1 do
//      l[n]:= l[n]+'='+ reg.ReadString(l[n]);
    Result := l.CommaText;
  finally
    reg.Free;
    l.Free;
//    v.Free;
  end;
end;


The last function GetComPortList seeks information from another part of the registry and gets the common name (FriendlyName) of the device installed, which is what I'm wanting.



function GetComPortList(PortList: TStrings): integer;
var
  i,idx: integer;
  SerPortNum: integer;
  Reg: TRegistry;
  EnumList: TStrings;
begin
  result := -1;
 
  if not CheckMinOS(osWin2k) then
   exit;
 
  Reg := TRegistry.Create();
  EnumList := TStringList.Create;
  try
    // Anzahl der Schnittstellen ermitteln
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    if Reg.OpenKeyReadOnly('\System\CurrentControlSet\Services\SerEnum\Enum') then
    begin
      SerPortNum := Reg.ReadInteger('Count');
 
      // Registry-Schlüssel der Schnittstellen zwischenspeichern
      for i:=0 to SerPortNum-1 do
        EnumList.Add(Reg.ReadString(inttostr(i)));
      Reg.CloseKey;
 
      // Daten der Schnittstellen ermitteln
      for i:=0 to SerPortNum-1 do
      begin
        // Schnittstellenname ermitteln (z.B. 'COM2')
        if Reg.OpenKeyReadOnly('\System\CurrentControlSet\Enum\'+EnumList.Strings[i]+'\Device Parameters') then
          idx := PortList.Add(Reg.ReadString('PortName')+'=');
        Reg.CloseKey;
        // Bezeichnung wie im Gerätemanager ermitteln (z.B. 'USB Serial Port (COM2)' )
        if Reg.OpenKeyReadOnly('\System\CurrentControlSet\Enum\'+EnumList.Strings[i]) then
          PortList.ValueFromIndex[idx] := Reg.ReadString('FriendlyName');
        Reg.CloseKey;
      end;
    end;
  finally
    EnumList.Free;
    Reg.Free;
  end;
  result := PortList.Count;
end;

But there is a problem with this last feature, it's looking for the information only in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\SerEnum. To test I plugged in two serial devices, a generic USB-to-RS232 adapter and a Blu Samba Q cellphone. One of them appears on the sheet SerEnum but the other appears under USBSER, which makes me assume that depending on how it is programmed the device driver, service name is arbitrary and therefore its location in the tree HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services too.

To solve my problem I took as example the first and the third function and made my own.

function GetSerialPortNamesExt: string;
var
  reg  : TRegistry;
  l,v  : TStringList;
  n    : integer;
  pn,fn: string;
 
  function findFriendlyName(key: string; port: string): string;
  var
    r : TRegistry;
    k : TStringList;
    i : Integer;
    ck: string;
    rs: string;
  begin
    r := TRegistry.Create;
    k := TStringList.Create;
 
    r.RootKey := HKEY_LOCAL_MACHINE;
    r.OpenKeyReadOnly(key);
    r.GetKeyNames(k);
    r.CloseKey;
 
    try
      for i := 0 to k.Count - 1 do
      begin
        ck := key + k[i] + '\'; // current key
        // looking for "PortName" stringvalue in "Device Parameters" subkey
        if r.OpenKeyReadOnly(ck + 'Device Parameters') then
        begin
          if r.ReadString('PortName') = port then
          begin
            //Memo1.Lines.Add('--> ' + ck);
            r.CloseKey;
            r.OpenKeyReadOnly(ck);
            rs := r.ReadString('FriendlyName');
            Break;
          end // if r.ReadString('PortName') = port ...
        end  // if r.OpenKeyReadOnly(ck + 'Device Parameters') ...
        // keep looking on subkeys for "PortName"
        else // if not r.OpenKeyReadOnly(ck + 'Device Parameters') ...
        begin
          if r.OpenKeyReadOnly(ck) and r.HasSubKeys then
          begin
            rs := findFriendlyName(ck, port);
            if rs <> '' then Break;
          end; // if not (r.OpenKeyReadOnly(ck) and r.HasSubKeys) ...
        end; // if not r.OpenKeyReadOnly(ck + 'Device Parameters') ...
      end; // for i := 0 to k.Count - 1 ...
      result := rs;
    finally
      r.Free;
      k.Free;
    end; // try ...
  end; // function findFriendlyName ...
 
begin
  v      := TStringList.Create;
  l      := TStringList.Create;
  reg    := TRegistry.Create;
  Result := '';
 
  try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM') then
    begin
      reg.GetValueNames(l);
 
      for n := 0 to l.Count - 1 do
      begin
        pn := reg.ReadString(l[n]);
        fn := findFriendlyName('\System\CurrentControlSet\Enum\', pn);
        v.Add(pn + ' = '+ fn);
      end; // for n := 0 to l.Count - 1 ...
 
      Result := v.CommaText;
    end; // if reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM') ...
  finally
    reg.Free;
    v.Free;
  end; // try ...
end;

jueves, 12 de abril de 2012

Enumerar puertos seriales en Windows con Lazarus



Como tengo solo puertos USB en mi máquina de desarrollo, es muy común que al conectar un dispositivo serial tengo que ir al administrador de dispositivos para ver en que puerto COM se instaló.


Estoy desarrollando una aplicación que necesita acceder a un dispositivo serial, por lo que necesito hacerlo consciente de los puertos COM instalados, ya sea para indicar cual usar o para verificar la existencia del que ya fue configurado.


Investigando un poco para no reinventar la rueda, me encontré con esta página: http://www.lazarus.freepascal.org/index.php?topic=14313.0 donde se publican tres funciones muy interesantes.


La primera función GetSerialPortNames es extraida del paquete synaser (http://synapse.ararat.cz/doku.php/download), devuelve los puertos COM instalados en el sistema operativo (en mi caso: COM3 y COM17). Mas o menos lo que necesito, pero solo los enumera sin identificarlos. 

function GetSerialPortNames: string;
var
  reg: TRegistry;
  l, v: TStringList;
  n: integer;
begin
  l := TStringList.Create;
  v := TStringList.Create;
  reg := TRegistry.Create;
  try
{$IFNDEF VER100}
    reg.Access := KEY_READ;
{$ENDIF}
    reg.RootKey := HKEY_LOCAL_MACHINE;
    reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM');//, false);
    reg.GetValueNames(l);
    for n := 0 to l.Count - 1 do
      v.Add(reg.ReadString(l[n]));
    Result := v.CommaText;
  finally
    reg.Free;
    l.Free;
    v.Free;
  end;
end;


La segunda función GetSerialPortRegNames es una variante de la primera en la que se muestra el dispositivo instalado en si (en mi caso: \Device\ProlificSerial0 y \Device\USBSER000), lo que tampoco es muy claro.

function GetSerialPortRegNames: string;
var
  reg: TRegistry;
  l  : TStringList;
  n: integer;
begin
  l := TStringList.Create;
//  v := TStringList.Create;
  reg := TRegistry.Create;
  try
{$IFNDEF VER100}
    reg.Access := KEY_READ;
{$ENDIF}
    reg.RootKey := HKEY_LOCAL_MACHINE;
    reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM');//, false);
    reg.GetValueNames(l);
//    for n := 0 to l.Count - 1 do
//      l[n]:= l[n]+'='+ reg.ReadString(l[n]);
    Result := l.CommaText;
  finally
    reg.Free;
    l.Free;
//    v.Free;
  end;
end;


La última función GetComPortList busca la información en otra parte del registro y obtiene el nombre común (FriendlyName) del puerto instalado, que es justamente lo que estoy queriendo. 

function GetComPortList(PortList: TStrings): integer;
var
  i,idx: integer;
  SerPortNum: integer;
  Reg: TRegistry;
  EnumList: TStrings;
begin
  result := -1;

  if not CheckMinOS(osWin2k) then
   exit;

  Reg := TRegistry.Create();
  EnumList := TStringList.Create;
  try
    // Anzahl der Schnittstellen ermitteln
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    if Reg.OpenKeyReadOnly('\System\CurrentControlSet\Services\SerEnum\Enum') then
    begin
      SerPortNum := Reg.ReadInteger('Count');

      // Registry-Schlüssel der Schnittstellen zwischenspeichern
      for i:=0 to SerPortNum-1 do
        EnumList.Add(Reg.ReadString(inttostr(i)));
      Reg.CloseKey;

      // Daten der Schnittstellen ermitteln
      for i:=0 to SerPortNum-1 do
      begin
        // Schnittstellenname ermitteln (z.B. 'COM2')
        if Reg.OpenKeyReadOnly('\System\CurrentControlSet\Enum\'+EnumList.Strings[i]+'\Device Parameters') then
          idx := PortList.Add(Reg.ReadString('PortName')+'=');
        Reg.CloseKey;
        // Bezeichnung wie im Gerätemanager ermitteln (z.B. 'USB Serial Port (COM2)' )
        if Reg.OpenKeyReadOnly('\System\CurrentControlSet\Enum\'+EnumList.Strings[i]) then
          PortList.ValueFromIndex[idx] := Reg.ReadString('FriendlyName');
        Reg.CloseKey;
      end;
    end;
  finally
    EnumList.Free;
    Reg.Free;
  end;
  result := PortList.Count;
end;


Pero hay un problema con esta última función, busca la información solo en HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\SerEnum. Para probar conecté dos dispositivos seriales un adaptador USB-RS232 genérico y un celular Blu Samba Q, uno de ellos figura en la hoja SerEnum pero la otra aparece bajo UsbSer, lo que me hace suponer que dependiendo de cómo está programado el driver del dispositivo, el nombre del servicio que los controla es arbitrario por lo tanto su ubicación en el árbol HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services también.


Para resolver mi problema tomé como ejemplo la primera y la tercera función e hice la mía propia.

function GetSerialPortNamesExt: string;
var
  reg  : TRegistry;
  l,v  : TStringList;
  n    : integer;
  pn,fn: string;

  function findFriendlyName(key: string; port: string): string;
  var
    r : TRegistry;
    k : TStringList;
    i : Integer;
    ck: string;
    rs: string;
  begin
    r := TRegistry.Create;
    k := TStringList.Create;

    r.RootKey := HKEY_LOCAL_MACHINE;
    r.OpenKeyReadOnly(key);
    r.GetKeyNames(k);
    r.CloseKey;

    try
      for i := 0 to k.Count - 1 do
      begin
        ck := key + k[i] + '\'; // current key
        // looking for "PortName" stringvalue in "Device Parameters" subkey
        if r.OpenKeyReadOnly(ck + 'Device Parameters') then
        begin
          if r.ReadString('PortName') = port then
          begin
            //Memo1.Lines.Add('--> ' + ck);
            r.CloseKey;
            r.OpenKeyReadOnly(ck);
            rs := r.ReadString('FriendlyName');
            Break;
          end // if r.ReadString('PortName') = port ...
        end  // if r.OpenKeyReadOnly(ck + 'Device Parameters') ...
        // keep looking on subkeys for "PortName"
        else // if not r.OpenKeyReadOnly(ck + 'Device Parameters') ...
        begin
          if r.OpenKeyReadOnly(ck) and r.HasSubKeys then
          begin
            rs := findFriendlyName(ck, port);
            if rs <> '' then Break;
          end; // if not (r.OpenKeyReadOnly(ck) and r.HasSubKeys) ...
        end; // if not r.OpenKeyReadOnly(ck + 'Device Parameters') ...
      end; // for i := 0 to k.Count - 1 ...
      result := rs;
    finally
      r.Free;
      k.Free;
    end; // try ...
  end; // function findFriendlyName ...

begin
  v      := TStringList.Create;
  l      := TStringList.Create;
  reg    := TRegistry.Create;
  Result := '';

  try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM') then
    begin
      reg.GetValueNames(l);

      for n := 0 to l.Count - 1 do
      begin
        pn := reg.ReadString(l[n]);
        fn := findFriendlyName('\System\CurrentControlSet\Enum\', pn);
        v.Add(pn + ' = '+ fn);
      end; // for n := 0 to l.Count - 1 ...

      Result := v.CommaText;
    end; // if reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM') ...
  finally
    reg.Free;
    v.Free;
  end; // try ...
end;


La función busca los puertos COM enumerados en HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM, luego busca recursivamente  en HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\ por el nombre común (FriendlyName) del dispositivo. Devuelve algo así: "COM3 = Prolific USB-to-Serial Comm Port (COM3)","COM17 = MTK6225 USB Modem Driver (COM17)"

domingo, 5 de junio de 2011

GPU baratos estan volviendo inútiles contraseñas seguras

Piensa que su contraseña de ocho caracteres consistente en caracteres en minúsculas, caracteres en mayúsculas y una rociada de números es suficientemente fuerte para protegerlo de un ataque de fuerza bruta.
Pienselo de nuevo!

Jon Honeyball, escribiendo para PC Pro, tiene una pieza sobria sobre como GPU modernas pueden ser aprovechadas como una poderosa herramienta contra contraseñas que antes eran consideradas a salvo de ataques de fuerza bruta.



Tome una GPU barata (como la Radeon HD 5770) y la herramienta revienta contraseñas gratuita potenciada por GPU 'ighashgpu' (http://www.golubev.com/hashgpu.htm) y poseerá una magra y malvada máquina de reventar contraseñas. Que tan magra y malvada? Bastante:
Los resultados son alarmantes. Trabajando contra contraseñas de login NTLM, una contraseña "fjR8n" puede ser rota en la CPU en 24 segundos, a una tasa de 9.7 millones de suposiciones de contraseña por segundo. En la GPU, toma menos de un segundo a una tasa de 3.300 millones de contraseñas por segundo.
Incremente la contraseña a 6 caracteres (pYDbL6), y la CPU se toma 1 hora y 30 minutos contra solo cuatro segundos el la GPU. Continue a 7 caracteres (fh0GH5h), y la CPU machacará por 4 días, contra unos francamente preocupantes 17 minutos y 30 segundos para la GPU.
Se pone peor. Tire una contraseña de nueve caracteres, aleatoria mazclando entre mayúsculas-minúsculas, y mientras una CPU tomaría unos abrumadores 43 años en romper esto, la GPU lo haría en 48 días.

Sin duda poniendo símbolos ahi dentro lo mantiene seguro, cierto? Equivocado! Tome una contraseña consistente en siete caracteres, contraseñas aleatorias mezclando entre mayúsculas-minúsculas/símbolos como "F6&B is" (note el espacio), eso debe ser dificil para un ataque de fuerza bruta, correcto? Una CPU tomará unos 75 días para recorrer a traves de las posibilidades, mientras una GPU se hace con ella en 7 horas.

Cual es la solución?. Bueno, Honeyball no lo sabe, y tampoco yo para ser perfectamente honesto. Lo que si se es que esto es una advertencia, y una que debe tomerse en serio. A menis que estemos dispuestos a pasarnos a contraseñas de 15-16 caracteres aleatorios, entre mayúsculas, minúsculas y símbolos (las que terminaran en notas PostIt), las contraseñas pronto solo ofreceran protección contra gente honesta.


Esta es una traducción de este artículo: Cheap GPUs are rendering strong passwords useless
Sugiero leer el artículo original por Jon Honeyball, How a cheap graphics card could crack your password in under a second.

martes, 7 de octubre de 2008

Cadenas, Tantras y Expresiones Regulares.

Es sabido que las famosas cadenas o tantras son uno de los tipos mas comunes de ataques basados en ingeniería social, por cierto, quién no se pregunto alguna ves como es que los spamer consiguieron su dirección.

En realidad no hece falta contar con programas sofisticados para procesar uno de estos emails, cargado con cientos de direcciones de correo, y hacer una lista de ellas. Podríamos valernos de expresiones regulares, que es una técnica para encontrar un patrones dentro de un texto dado, para extraerlas.

Para demostrar el uso de expresiones regulares hice un programa que permite extraer coincidencias de un texto cualquiera.
Puede bajarse de aqui.

El programa va a compañado de los códigos fuente, compila bien con Delphi 7. Necesita este componente.

Podemos copiar y pegar contenido en el cuadro de entrada, o leerlo directamente de un archivo guardado, a tal efecto se puede hacer click derecho con el raton en cualquier parte de la ventana y aparecerá un menu con las opciones de leer desde o guardar a un archivo.

No hay necesidad de preprocesar el cointenido a buscar, por ejemplo, podemos guardar una página web y leerla directamente a traves del menu.

Las siguientes reglas permiten coincidir direcciones de correo, l
a explicación de la sintaxis de las mismas quedará para otro post:

  1. [_a-zA-Z\d\-\.]+@[_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+
  2. \w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}
  3. ([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)


En [1] colocamos la expresión regular, en [2] el contenido de algún email o texto que contenga direcciones de coreeo, luego presionamos Evaluar. Los resultados apareceran en [3].

Debo aclarar que este programa en realidad puede extraer cualquier texto de acuerdo a la expresión regular que le introduzcamos, en este caso nos centramos en las direcciones de correo como demostración.

Cada uno es libre de creer y hacer lo que mejor le parezca, mi sugerencia es que si vamos a enviar o reenviar uno de estos mails, nos tomemos el trabajo de eliminar las direcciones que acarrea el mail, o simplemente no caer en este tipo de juego.

lunes, 22 de septiembre de 2008

ETyC

Hoy inició la Exposición de Tecnología y Ciencia (ETyC) edición 2008.

La misma se lleva a cabo las instalaciones de la Facultad Politécnica en el Campus Universitario de San Lorenzo. El acceso es libre y gratuito.

Cada día de la exposición tiene programadas conferencias, charlas, cursos, encuentros, jornadas, talleres y concursos, para distintas áreas de interes, de las cuales solo algunas muy específicas tienen costo. No hay motivo para no ir dar una vuelta y participar de las actividades.

Para mas información y el cronograma de actividades, pueden visitar el sitio del evento.