Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Temas relacionados > Seguridad
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 27-03-2007
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Eliminar ciertos datos de la memoria RAM

Hola,

Si uno utiliza un programa como WinHex puede acceder a la memoria RAM que está ocupando un programa determinado. Si haces uso de este programa puedes visualizar la parte de memoria RAM ocupada por un programa, con sus identificadores, variables, valores...

Y aquí está la cuestión, en los valores. Supongamos un programa que puede ser registrado por los usuarios. Para ello el usuario ha de proporcionar al programa su "clave" de usuario y un número de serie. Dentro del programa hay al menos dos funciones "críticas"... o que a mí me lo parecen luego de investigar el asunto un poco.

A ver. Como digo son dos funciones, básicamente. Una de ellas comprueba que el número de serie proporcionado por el usuario resulta válido para la clave de usuario que también se especifique. Esta función no me parece tan crítica como la siguiente: la función que se encarga de obtener un número de serie válido realmente para el usuario, precisamente, para compararlo con el que el usuario proporciona.

Bien. El caso es que el usuario puede ejecutar el programa y simular su registro especificando una clave de usuario y un número de serie al azar. Acto seguido el programa comprueba el número de serie, y, obviamente, no es correcto (sería complicadísimo averiguarlo al azar) así que el programa cierra el cuadro de diálogo correspondiente y aquí paz y después gloria.

Pero no. Si usuario tiene preparado el programa WinEx o lo ejecuta (todavía con nuestro programa en ejecución, o sea en memoria) con WinEx el usuario puede visualizar la memoria RAM ocupada por el programa, y, sorpresa, sorpresa, en la memoria RAM se encuentra el número de serie válido para el usuario... tal como fue generado en la función correspondiente.

He intentado ya unas cuantas cosas. Al final he conseguido (creo) reducir el problema, concretarlo en la función que digo que se encarga de crear un número de serie correcto para el usuario, de tal manera que pueda compararlo con el que proporcione el usuario. Es aquí donde está el problema, y el caso es que la función digamos que es similar a esta:

Código Delphi [-]
function ObtenerNumeroSerie(numeroBase:
 integer; claveUsuario: string): string;
var
  A: Integer;
begin
  result := '';
  if (numeroBase <> 0) and (claveUsuario <> '') then
    Result := numeroBase * Length(claveUsuario) * 77;
end;

Como puede verse (y disculpad que me enrrolle... pero intento dejar claro el asunto lo mejor que sé hacerlo), como puede verse, digo, la función se basa en cierto número base, que a su vez se combina con la "clave del usuario"... pero el caso es que la función tiene que terminar resultando en un número de serie válido para la clave de usuario, el mismo que podríamos nosotros generar en el "generador" que tengamos preparado para cuando alguien registre el programa.

Obviamente tiene que ser así. El usuario proporciona un número de serie y una clave de usuario. El generador de números de serie lo que hace es conformar un número de serie dado en base a un número base y a una clave de usuario. El programa (no el generador) tiene que hacer lo mismo: tiene que poder generar el número de serie que se corresponde con el número base y la clave de usuario que este a su vez proporcione.

Y tiene que hacerlo porque de este modo podremos luego comparar: el número de serie proporcionado por el usuario y el número de serie que tiene realmente habría de ser.

Pues bien. He ahí el problema. Al generarse en nuestro programa el número de serie dentro de la función susomentada... ¡se está dejando en la memoria RAM el número de serie! Es decir que el resultado de la siguiente función:

Código Delphi [-]
function ObtenerNumeroSerie(numeroBase:
 integer; claveUsuario: string): string;
var
  A: Integer;
begin
  result := '';
  result := 'pepe';
  result := result + 'juan';
end;

... puede "verse" con el programa WinEx. Con este programa puede verse "pepe", por un lado, y luego "pepejuan", por otro... es decir, estamos viendo el "result" de la función...

Obviamente no todos los usuarios van a recurrir a un programa como WinEx. Pero a mí me lo ha comentado un usuario del programa que me traigo entre manos; me ha contado cómo lo ha hecho y yo he podido reproducir el sistema muy fácilmente... claro está que lo difícil era saber cómo hacerlo.

Ahora bien. Termino ya, disculpadme, por favor. ¿Qué puedo hacer? El número de serie "correcto" ha de generarse para poder compararlo con el que proporcione el usuario; ¿Cómo puedo evitar que el resultado de la función "ObtenerNumeroSerie" permanezca en memoria únicamente lo preciso? Es decir, sólo cuando se realiza la comprobación. Digamos que se realiza la comprobación y ¡zas!, el número correcto tiene que desaparecer de la memoria RAM...

Bueno. Si necesitáis más información procuraré proporcionarla. Disculpadme el pedazo de rollo que acabo de soltaros y muchas gracias a todos de antemano.
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #2  
Antiguo 27-03-2007
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Bueno. Otra vez yo. Dándome cuenta de que lío las cosas demasiado cuando pueden simplificarse bastante. Olvídense si quieren de números de serie, generadores, registros, claves de usuario...

Simplemente inicien una nueva aplicación en Delphi. Sitúen un botón en el formulario principal. Escriban una función en la implementación del formulario principal:

Código Delphi [-]
function Resultado(): string;
begin
  Result := 'pepe';
end;

Y en el evento "Onclick" del botón añadido al formulario codifiquen lo siguiente:

Código Delphi [-]
procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := Resultado();
  s := '';
end;

Compilen el programa... compílenlo... ¿Ya? Vale, vale.

Salgan de Delphi y ejecuten la aplicación para acto seguido hacer clic en el botón de marras. Ejecuten entonces el WinEx (vale la versión de evaluación) y visualizen la memoria RAM (ALT + F9) de la aplicación que acabamos de compilar y ejecutar.

Para terminar busquen "pepe" en el editor hexadecimal de WinEx. ¿Verdad que está "pepe" ahí? Ahora bien, ¿qué demonios hace "pepe" ahí? ¿Cómo puede quitarse a "pepe" del medio? Y que parezca un accidente, claro.

Gracias de antemano a todos.
__________________
David Esperalta
www.decsoftutils.com

Última edición por dec fecha: 27-03-2007 a las 13:10:39.
Responder Con Cita
  #3  
Antiguo 27-03-2007
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Una cosa más... no se les ocurra cambiar la función inmediatamente anterior por:

Código Delphi [-]
function Resultado(): string;
begin
  Result := 'pepe' + DateToStr(Time);
end;

Porque entonces si buscan a "pepe" le encontrarán una vez (al ejecutar el programa) y otra vez más cuando pulsen en el botón... y en esta última lo encontrarán en el pasado... "pepe30/12/1899"...

No hagan esto. ¡Porque se pueden volver tan locos como yo!

PD. El que no sabe es como el que no ve. Confucio
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #4  
Antiguo 27-03-2007
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Caray, pues preguntando cómo defenderte de eso, acabas de dar un manual de como hacer eso.

Yo, pues no sé, pero ¿qué no puedes escribir en la misma variable otro valor para borra a pepe?

// Saludos
Responder Con Cita
  #5  
Antiguo 27-03-2007
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Gracias Román por interesarte por el tema.

Bueno. Antes de nada voy a corregirme a mí mismo:

Código Delphi [-]
DateToStr(Time)

Eso retorna al paso a "pepe" porque no se hacen las cosas bien, como puede verse. En todo caso debe ser así o así:

Código Delphi [-]
DateToStr(Date)
TimeToStr(Time)

Pero, bueno. Es lo de menos... me disculpo porque me pongo quizás demasiado nervioso sin ton ni son y no me paro a mirar las cosas como debe ser...

Vamos al caso. Dices Román que podría dar otro valor a la variable para eliminar el anterior valor... pero el caso es que creo haberlo intentado y no funciona. Es decir.

Código Delphi [-]

const
  NUMERO_BASE = 123;

function Resultado(numeroBase, claveUsuario: string): string;
begin
  result := numeroBase * Length(claveUsuario);
end;

function CompruebaResultado(claveUsuario, numeroSerie: string): boolean;
begin
  result := ( numeroSerie = Resultado(NUMERO_BASE, claveUsuario) );
end;

A esto puede reducirse el caso. Como puede verse en la función "Resultado" se genera el valor que no debería quedarse en la memoria RAM... necesitamos ese valor para compararlo en la función "CompruebaResultado", pero, no debería permanecer en la memoria más tiempo que el preciso.

Y sin embargo aparece... pero he probado y no vale hacer algo así:

Código Delphi [-]
function CompruebaResultado(claveUsuario, numeroSerie: string): boolean;
var
  s: string;
begin
  s := Resultado(NUMERO_BASE, claveUsuario);

  result := ( numeroSerie =  s);

  s := '';
end;

No; El número de serie sigue apareciendo. Y parece ser que es porque donde deja su "huella" en la memoria RAM es en la propia función "Resultado". Pero claro, aquí si que obviamente no podemos hacer algo como esto:

Código Delphi [-]
function Resultado(numeroBase, claveUsuario: string): string;
begin
  result := numeroBase * Length(claveUsuario);
  result := ''; // No puede ser claro está
end;

Se me ocurre al hilo de escribir aquí que tal vez si en lugar de una función fuese un procedimiento que retornara el "número de serie" en un parámetro pasado por referencia... o sea:

Código Delphi [-]
procedure Resultado(numeroBase, claveUsuario: string; var numeroSerie: string);
begin
  numeroSerie := numeroBase * Length(claveUsuario);
end;

... Tal vez sí que podríamos luego hacer algo así:

Código Delphi [-]
function CompruebaResultado(claveUsuario, numeroSerie: string): boolean;
var
  s: string;
begin
  Resultado(NUMERO_BASE, claveUsuario, s);
  result := ( numeroSerie =  s);
  s := '';
end;

Tengo que probar a ver...
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #6  
Antiguo 27-03-2007
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Muchas gracias Seoane... pero, ¿estás tú seguro de que Pepe no aparece por ahí? ¡A mí no se me va de la memoria!

Edito: Sí que funciona como dices Seoane. Aparece Pepe cuando efectivamente uno quiere comprobar "Pepe", precisamente. Pero si uno trata con "Juan" (es decir, no "valida") entonces "Pepe" no aparece tal cual...

Tengo que tratar ahora de llevar el código al programa que digo, y veremos a ver, porque no termino de comprenderlo... Lo que ya no sé es si probar pasando como parámetro por referencia a Pepe... puesto que la solución que planteas parece más elegante, no sé.
__________________
David Esperalta
www.decsoftutils.com

Última edición por dec fecha: 27-03-2007 a las 13:45:27.
Responder Con Cita
  #7  
Antiguo 27-03-2007
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Hola,

Lamento molestar otra vez... Seoane... he intentado implementar el código (y la idea... dentro de mis posibilidades) en el programa de marras y el asunto no ha funcionado: sigue viéndose en la memoria RAM lo que no debía verse.

Y entonces me he preguntado cómo es posible si he probado tu ejemplo y yo mismo he dicho que me había funcionado... pero lo he vuelto a probar y el asunto no funciona Seoane... y ahora no sé si es que no nos ponemos de acuerdo (sin duda por culpa mía) acerca de lo que hay que quitar y lo que no...

En tu ejemplo, en todo caso (se "compruebe" o no se "compruebe") que el usuario escribió "Pepe"... este sigue apareciendo en la memoria RAM... ¿tú cómo lo ves?

Gracias en todo caso.

PD. Es verdad Román que acaso he proporcionado información de cierta relevancia a alguien... pero no era mi intención: no le dí importancia en ese sentido. No sé si porque doy por supuesto que al final lo explicado no va a servir... si al cabo se consigue eliminar de la memoria RAM el valor de que tratamos.
__________________
David Esperalta
www.decsoftutils.com
Responder Con Cita
  #8  
Antiguo 27-03-2007
Avatar de seoane
[seoane] seoane is offline
Miembro Premium
 
Registrado: feb 2004
Ubicación: A Coruña, España
Posts: 3.717
Poder: 24
seoane Va por buen camino
Bien, en estos casos lo mejor es tener control sobre la memoria que se esta usando.
Código Delphi [-]
procedure Resultado(Str: PChar; L: Integer);
begin
  // Pues yo si que uso DateToStr ¿que pasa?
  StrLCopy(Str,PChar('Pepe'+DateToStr(Time)),L);
end;

function Comprobar(Str: String): Boolean;
var
  Buffer: PChar;
begin
  GetMem(Buffer,32);
  try
    Resultado(Buffer,31);
    Result:= StrComp(Buffer,PChar(Str)) = 0;
  finally
   //Borramos la memoria
    FillChar(Buffer^,32,#0);
    FreeMem(Buffer);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMEssage(BoolToStr(Comprobar('Hola')));
end;
Responder Con Cita
  #9  
Antiguo 27-03-2007
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.286
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Cita:
Empezado por dec
Una de ellas comprueba que el número de serie proporcionado por el usuario resulta válido para la clave de usuario que también se especifique. Esta función no me parece tan crítica como la siguiente: la función que se encarga de obtener un número de serie válido realmente para el usuario, precisamente, para compararlo con el que el usuario proporciona.
Bueno, no es mi intención "desmontar" la teoría inicial, pero aquí el fallo está en el planteamiento.
De todas formas el problema de la memoria RAM es interesante también.

Aunque cada uno programa los sistema de registro como desea, normalmente los programas no suelen utilizar el sistema mencionado, porque es muy sencillo detectar el número correcto utilizando un Debugger. No hace falta "rebuscar" en la memoria RAM, basta con ejecutarlas instrucciones del programa y revisando los registros y la pila.

Por ejemplo, "debuggando"(¡qué mal suean esto!) el programa mencionado por dec, con OllyDBG podemos llegar fácilmente (y eso que yo no soy experto en estos temas, pero con un par de minutos me ha bastado...) a este punto:
imagen

En la parte izquierda se ven las instrucciones y en la derecha la pila y los registros.
Normalmente en estos casos (crackendo un programa, cosa que yo nunca he hecho...) se busca un punto de partida (1); Si este es acertado, a partir de ahí se ejecutan las instrucciones hasta llegar a los procedimientos donde se evalua y calcula el número de serie.

(1) Para comenzar se buscan los formularios de Registro, prodecimientos con nombres sospechosos (TestID, Register, RegisterOK,...), Strings "sospechosos" como "El número de registro es incorrecto", "El número de serie no es válido",...
¿Por qué comento esto? Porque sabiéndolo, alguien que diseñe un sistema de protección debe tener en cuenta:
(a) Los procedimientos que tienen que ver con el registro y números de serie nunca deben llamarse: Registro, CalcularSN, TestRegistro,... ni cualquier otro nombre que de pistas sobre olo que están haciendo.
(b) Los mensajes de error relativos al registro deben estar codificados y nunca directamente en el código; La codificación no importa, basta un XOR, o cambiar una letra por otra, lo importante es que si revisas la memoria no aparezcan directamente.
(c) Algunas cosillas más....

Si "cuesta" encontrar en punto de entrada para comenzar a "debuggar" las cosas se hacen un poco más complicadas.

En cuanto al número de registro o números de serie, la premisa es que en ningun momento el número de serie debe estar completo en memoria. En una variable o en una cadena ni debe ser devuelto por una función.

Lo lógico es hacerlo "a partes" y una vez particionado no hacer comparaciones directas.
Un ejemplo sencillo es no hacer esto:

Código Delphi [-]
// 1234 calculado previamente...
if (SN = '1234') then
...

Y en su lugar hacer esto:

Código Delphi [-]
j := Ord('Q') - Ord('N');
// 1234 calculado previamente...
if (SN[1] = '1') and (SN[2] = (4 DIV 2)) and (SN[3] = j) and ... then
...

Comparar las partes desordenadas,....
Hay infinidad de técnicas, que van complicando el tema más... Pero algunas son básicas como las comentadas antes.
Sobre todo ¡Nunca debe estar el SN correcto almacenado de forma contigua en memoria!

Porque si no pasan cosas como esta. LLegado al punto de la comparación te puedes encontrar el nombre del registro, el SN incorrecto y el SN correcto con el que se está comparando...

AÑADO: En esta última imagen tampoco es muy afortunado el nombre del procedimiento ChessRk (contando que el programa se llama Chess); ¿Qué os sugiere ChessRk, sabiendo que el programa está en inglés?
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.

Última edición por Neftali [Germán.Estévez] fecha: 27-03-2007 a las 15:41:24.
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
eliminar datos con dbgrid UREÑA Conexión con bases de datos 20 30-03-2007 23:41:32
Eliminar Datos eficientemente k_rito Conexión con bases de datos 5 27-11-2006 00:44:45
Cómo obtener ciertos datos de los archivos ejecutables y librerías StartKill Varios 1 14-08-2006 23:10:01
Eliminar del memoria los querys Aura OOP 4 01-04-2004 17:40:40
Eliminar datos de una tabla DBF vicvil Tablas planas 4 16-05-2003 21:17:46


La franja horaria es GMT +2. Ahora son las 19:05:00.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi