public interface IUniversal
{
object Action(object param);
}
Nota del autor
Este blog es un archivo de los artículos situados previamente en Lobosoft.es y ha dejado de ser actualizado. Las nuevas entradas pueden encontrarse en www.lobosoft.es. Un saludo,
Lobosoft.
miércoles, 30 de mayo de 2012
Interfaz universal
viernes, 25 de mayo de 2012
Reflexionando sobre genéricos
Crear una clase genérica dinámicamente a partir de un objeto
public class Data<T>
{
public T Value { get; set; }
}
Data<int> data = new Data<int>();
data.Value = 1;
Type dataType = typeof(Data<>);
Type genericType = dataType.MakeGenericType(value.GetType());
genClassesArray.Add(Activator.CreateInstance(genericType));
Obtener el tipo anidado de una clase genérica
Type nestedType = value.GetType().GetGenericArguments()[0];Llamar a un método genérico con el tipo de un dato obtenido en tiempo de ejecución
public Data<T> GetDataWithValue<T>(string stringValue)
{
return new Data<T>() { Value = (T)Convert.ChangeType(stringValue, typeof(T)) };
}
MethodInfo method = typeof(MyClassDemo).GetMethod("GetDataWithValue");
MethodInfo genericMethod = method.MakeGenericMethod(value.GetType());
var result = genericMethod.Invoke(this, new object[] { value.ToString() });
martes, 22 de mayo de 2012
Journal of Feelsynapsis n.º 4
lunes, 15 de agosto de 2011
Los dictadores benevolentes del lenguaje
Quality of implementation, however, is critically important in the satisfaction enjoyed by users of the language. It is the experiential difference between writing Ruby and JavaScript. The factor that, in my view, most affects this quality of implementation is the vision of the original creator. Where the vision is maintained by a single individual, quality thrives. Where committees determine features, quality declines inexorably: Each new release saps vitality from the language even as it appears to remedy past faults or provide new, awaited capabilities.[...]Benevolent dictators make decisions about the language in consultation with the community of users. They are notable for being willing to extend the spirit of the language by making hard decisions that large committees almost always eschew.[...]A similar benevolent dictatorship exists with C#. The language, as I have mentioned before, is remarkably well tended by a core group in Microsoft headed by Anders Hejlsberg. (Although there is an ECMA standard for C#, Microsoft is the reference implementation, and the company decides the new features.) The result is a language widely loved by its users (Miguel de Icaza: "It's a beautiful languages that is such a pleasure to program in") and admired by others. Lua, Ruby, D, and Perl are other successful instances of this model.
Leído en Dr. Dobb's. "In Praise of Benevolent Language Dictators", de Andrew Binstock.
miércoles, 27 de julio de 2011
Manejando minidumps en .NET
BOOL WINAPI MiniDumpWriteDump(
__in HANDLE hProcess,
__in DWORD ProcessId,
__in HANDLE hFile,
__in MINIDUMP_TYPE DumpType,
__in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
public class MiniDumpWriter
{
[Flags]
public enum DumpType : uint
{
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
MiniDumpWithFullMemory = 0x00000002,
MiniDumpWithHandleData = 0x00000004,
MiniDumpFilterMemory = 0x00000008,
MiniDumpScanMemory = 0x00000010,
MiniDumpWithUnloadedModules = 0x00000020,
MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
MiniDumpFilterModulePaths = 0x00000080,
MiniDumpWithProcessThreadData = 0x00000100,
MiniDumpWithPrivateReadWriteMemory = 0x00000200,
MiniDumpWithoutOptionalData = 0x00000400,
MiniDumpWithFullMemoryInfo = 0x00000800,
MiniDumpWithThreadInfo = 0x00001000,
MiniDumpWithCodeSegs = 0x00002000,
MiniDumpWithoutAuxiliaryState = 0x00004000,
MiniDumpWithFullAuxiliaryState = 0x00008000,
MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
MiniDumpIgnoreInaccessibleMemory = 0x00020000,
MiniDumpValidTypeFlags = 0x0003ffff,
};
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct MiniDumpExceptionInformation
{
public uint ThreadId;
public IntPtr ExceptionPointers;
[MarshalAs(UnmanagedType.Bool)]
public bool ClientPointers;
}
[DllImport("dbghelp.dll",
EntryPoint = "MiniDumpWriteDump",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode,
ExactSpelling = true, SetLastError = true)]
static extern bool MiniDumpWriteDump(
IntPtr hProcess,
uint processId,
IntPtr hFile,
uint dumpType,
ref MiniDumpExceptionInformation expParam,
IntPtr userStreamParam,
IntPtr callbackParam);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
static extern uint GetCurrentProcessId();
/// <summary>
/// Generates a memory dump and save it into a file.
/// </summary>
/// <param name="fileName">The file where dump must be stored.</param>
/// <param name="dumpType">The type of memory dump generated.</param>
/// <returns></returns>
public static bool Write(string fileName, DumpType dumpType)
{
using (FileStream fs = new FileStream(fileName,
FileMode.Create,
FileAccess.Write,
FileShare.None))
{
MiniDumpExceptionInformation exInfo;
exInfo.ThreadId = GetCurrentThreadId();
exInfo.ClientPointers = false;
exInfo.ExceptionPointers = Marshal.GetExceptionPointers();
bool value = MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
fs.SafeFileHandle.DangerousGetHandle(),
(uint)dumpType,
ref exInfo,
IntPtr.Zero,
IntPtr.Zero);
return value;
}
}
}
class Program
{
static void Main(string[] args)
{
try
{
object o = null;
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
o.ToString();
}
catch (System.Exception e)
{
string dumpFile = @"Something\Like\lobosoft.dmp";
MiniDumpWriter.Write(dumpFile, MiniDumpWriter.DumpType.MiniDumpWithFullMemory);
}
}
}
Hay que tener en cuenta que los ficheros generados son bastante voluminosos y que la ruta donde los guardará nuestra aplicación debe establecerse a una ubicación donde contemos con permisos suficientes como para que pueda ser escrito. Una vez generado podremos usar Visual Studio, o la Debugging Diagnostic Tool para analizar el error. Pero esto será objeto de una próxima entrada.
viernes, 25 de febrero de 2011
Políglotas
Lo expuesto, por supuesto, sería extensible a multitud de aspectos de nuestra profesión, y no solamente a la programación. Y, “aunque los experimentos haya que hacerlos con gaseosa”, no es menos cierto que de vez en cuando no está de más ser un poco innovador y abrirse a nuevas experiencias. Además, resulta divertido.
martes, 18 de enero de 2011
Conocer la MAC de un equipo remoto
arp –a 192.168.0.1
Siendo así resulta fácil interpretar que podemos llevar a cabo esa solicitud desde nuestro código. En el caso de .NET, la forma más rápida de hacerlo es invocando directamente a la función SendARP() de la API IP Helper (librería iphlpapi.dll). El código de ejemplo que incluyo simplemente hace eso: recibe la IP sobre la que queremos recabar información de la consola, la convierte a UInt32, ya que es el tipo que recibe SendARP() –aunque podríamos haber recuperado ese valor de la propiedad IPAddress, habiendo convertido previamente la cadena con la dirección IP a un objeto de tipo IPAddress, lo cierto es que dicha propiedad está obsoleta desde hace bastante tiempo y Microsoft desaconseja usarla-, y realizamos la llamada a la API. Esta nos devolverá, si es capaz de recuperarla, la MAC correspondiente a la IP en cuestión. Y ya, simplemente, nos quedará mostrar la información sobre esa dirección física del modo adecuado.
viernes, 28 de mayo de 2010
Cómo añadir nuestros ensamblados en el GAC a Visual Studio
Cuando desarrollamos una librería que deseamos que sea usada por muchas de nuestras aplicaciones o desplegamos una (o varias) que incluyen componentes, controles o funcionalidad disponible como, por ejemplo, la de una API, deseamos que esté disponible para nuestras aplicaciones. El GAC resulta, como bien sabéis, el lugar idóneo desde el que gestionar las distintas versiones de las DLLs de código administrado en nuestro sistema. Sin embargo, aunque registremos las DLLs en el mismo, estas no aparecen en el diálogo de Visual Studio para agregar una referencia a nuestro proyecto. Esto ocurre porque Visual Studio busca los ensamblados en rutas del tipo C:\Windows\Microsoft.NET\Framework\vx.y.z, pero aunque el CLR sea capaz de cargar ensamblados del GAC, este es independiente de nuestro IDE preferido.
Supongamos que ubicamos nuestras DLLs en la ruta C:\SomePath\LsAssemblies y las registramos en el GAC usando gacutil.exe. Cuando Visual Studio (en su versión x.y) busca los ensamblados al añadir una referencia lo hace a través de la clave de registro
HKLM\Software\VisualStudio\x.y\AssemblyFolders
Cada subclave representa una carpeta de ensamblados. Aunque el nombre no tiene la menor relevancia, el valor predeterminado debe contener la ruta completa a la carpeta que contendrá los nuestros. Por ejemplo:
HKLM\Software\Microsoft\VisualStudio\9.0\AssemblyFolders\LobosoftAssemblies \ (Default value): C:\SomePath\LsAssemblies
Como apunte final, cabría señalar que si en la ruta donde situamos las DLLs dejamos también los archivos para la depuración (PDB) y la documentación (XML), Visual Studio los usará cuando cargue la DLL para permitir la depuración en ese ensamblado y mostrar la documentación de Intellisense.
En la Visual Studio Gallery he encontrado también una extensión que nos ayuda a referencias DLLs del GAC. Su nombre es Muse.VSExtensions y permite mostrar un diálogo “Add GAC Reference” en nuestro Visual Studio 2010.
miércoles, 26 de mayo de 2010
Seguridad en WordPress
Tras el ataque sufrido en Lobosoft me he visto obligado a adelantar un poco la dedicación al blog (que tenía pensado incrementar, como ya dije, hacia mediados de junio), llevar a cabo una limpieza del mismo y actualizar, ya no la versión del mismo pero sí algunos plugins que podrían estar en el origen del ataque (fallo por mi parte al utilizar demasiados, aunque alguna de la funcionalidad extendidad del blog está basada, mucho me temo, en ellos).
El script que “me colaron” básicamente se encarga de permitir al atacante subir un archivo a la web con lo cual, dicho pronto y claro, la seguridad completa del sitio queda claramente comprometida. He estado buscando en Internet y parece que es un script genérico que ha sido instalado en numerosos servidores, sobre todo tipo de CMS y foros (y otras aplicaciones web), por lo que en sí no es más que una herramienta más que utilizar para atacar el sitio o usarlo como plataforma para fines oscuros y perversos. ;)
Uno de los blogs que he encontrado con el problema en cuestión es Brian's World, que muestra el código en cuestión. En mi caso no habían llegado ni tan siquiera a inyectarlo en uno de los archivos de Wordpress, sino que se encontraba replicado en varios archivos PHP con nombre similares (con un prefijo añadido) a los contenidos en un par de plugins y en el tema de Wordpress que da estilo al blog.
Dejo por aquí una extensa lista de referencias a la seguridad en Wordpress (algunas aplicables a otros CMS) que nunca está de más tener en cuenta. E incluso así, a pesar de lo que afirme nuestro amigo GigA, me temo que nada es demasiado seguro.
Feliz lectura.
Para saber más:
En castellano.
- Consejos para tener un WordPress seguro.
- Cinco consejos de seguridad para WordPress.
- WordPress Security Cheat Sheet 1.0 (en español).
- ¿Cómo proteger tus blogs Wordpress de los hackers?
- Proteger el archivo wp-config.php.
- Cómo incrementar la seguridad de una web creada con Wordpress.
- 10 pasos para proteger tu panel de administrador.
- Reubicar wp-content y wp-config.php con Wordpress 2.6.
- (In)seguridad en WordPress, por Stefan Esser.
En inglés.
- Wordpress Security Whitepaper.
- Wordpress Security Tips and Hacks.
- WordPress Security – A Comprehensive Guide.
- 11 Best ways to improve WordPress Security.
- WordPress Security, Upgrades and Backups.
- 16 Excellent Wordpress Security Plugins To Secure Your Blog.
- Don’t Get Hacked: WordPress Security Tips.
Más allá de WordPress.
La imagen que ilustra la entrada, que me ha encantado, es de Antivirus WordPress.
jueves, 20 de mayo de 2010
Primeros pasos con Reflexil (2)
namespace EjemploReflexil
{
public class Sorpresa
{
public static void Infectar()
{
MessageBox.Show("¡Pardillo!", "Sorpresa",
MessageBoxButtons.AbortRetryIgnore,
MessageBoxIcon.Exclamation);
}
}
}
[/csharp]
Primeros pasos con Reflexil (1)
martes, 18 de mayo de 2010
Inserción de vídeos en WordPress MU
Hace unos días estuve haciendo unas pruebas para migrar Lobosoft a un sistema WordPress MU (la versión multiblog de WordPress que, además, fue con la que comencé la andadura de Lobosoft en un principio para pasar con posterioridad a la que utilizo ahora). Finalmente no me será necesario usarla, pero durante el proceso de configuración de la misma tuve que recordar viejos conocimientos además de ampliar la configuración de WordPress MU para permitir la inclusión del código HTML necesario para que la importación de las entradas que incluyen algún vídeo finalizase con éxito. De no hacerlo así, WordPress limpiaba literalmente el HTML de la entrada eliminando cualquier posibilidad de incluir vídeos de servicios como YouTube o Google Vídeo, por ejemplo.
WordPress lleva a cabo un análisis del HTML que incluimos en una determinada entrada a través de la variable $allowedposttags, un array incluido en el archivo kses.php dentro de del directorio wp-includes de nuestra instalación de WordPress (o WordPress MU, como es el caso del ejemplo). Dicho array contiene, como su nombre indica, las etiquetas HTML permitidas dentro de las entradas del blog. El formato de dicho array (que, a su vez, contiene a otros) es “nombre de etiqueta” => valor, siendo el valor un array sin contenido cuando la etiqueta es un atributo HTML y un array con valores cuando se trata de una etiqueta HTML con atributos. En resumen, se forma una colección de estructuras arbóreas que representan la estructura de un tag HTML. A buen seguro con un ejemplo queda todo más claro. Veamos cómo es definida en $allowedposttags la etiqueta del enlace (anchor):
[php]
'a' => array(
'class' => array (),
'href' => array (),
'id' => array (),
'title' => array (),
'rel' => array (),
'rev' => array (),
'name' => array (),
'target' => array()
)
[/php]
Resulta sencillo ver aquí la estructura de la etiqueta para un link, así como comprobar que no se permite el uso de mapas de imágenes en las entradas de WordPress (a menos que nosotros nos encarguemos de extender el array, por supuesto).
Así, para permitir la inclusión de vídeos en las entradas simplemente tenemos que ampliar el array principal definido para la variable $allowedposttags con el código siguiente:
[php]
'embed' => array (
'id' => array (),
'type' => array (),
'style' => array (),
'height' => array (),
'width' => array (),
'wmode' => array (),
'src' => array (),
'object' => array (
'height' => array (),
'width' => array ()
),
'param' => array (
'name' => array (),
'value' => array ()
),
)
[/php]
Sencillo, ¿verdad?
sábado, 8 de mayo de 2010
Ciencia y brujería
I think, fundamentally, open source does tend to be more stable software. It’s the right way to do things. I compare it to science vs. witchcraft.
In science, the whole system builds on people looking at other people results and building on top of them.In witchcraft, somebody had a small secret and guarded it but never allowed others to really understand it and build on it. Traditional software is likewitchcraft. In history, witchcraft just died out.
The same will happen in software. When problems get serious enough, you can’t have one person or one company guarding their secrets. You have to haveeverybody share in knowledge.
Linus Torvalds.
martes, 23 de febrero de 2010
La perfidia del software
En una de las asignaturas que estoy cursando este cuatrimestre se nos ha planteado un acercamiento a los “problemas retorcidos” o, usando un término que prefiero, “problemas perversos” (o pérfidos, o impíos, resulta difícil dar una traducción exacta a los conocidos en ámbitos anglosajones como wicked problems). Los problemas perversos fueron definidos por Rittel y Webber dentro del contexto de la planificación social, donde una aproximación meramente científica o técnica no sería suficiente para dar respuesta a aquellos aportando, por tanto, una solución total. Son problemas, por tanto, de difícil cuando no imposible solución (y no tienen nada que ver, por cierto, con las clases de complejidad computacional).
El desarrollo de software puede verse como un problema perverso ya que cumple con seis de las diez características que los definen, a saber:
- No existe una formulación definitiva del problema.
- No se puede saber cuándo termina el problema.
- Las soluciones que se pueden aportar no son verdaderas o falsas, sino buenas o malas.
- Cada problema es esencialmente único.
- Todas las soluciones para un problema de este tipo se pueden poner en práctica una única vez, ya que no existe una oportunidad para aprender mediante ensayo y error.
- Los problemas perversos no cuentan con un conjunto de soluciones posibles que pueda ser rigurosamente descrito.
Todo lo anterior nos lleva a que podamos afirmar que es más rápido crear una solución al problema que estimar cuánto tiempo tomará crear una solución que posea un determinado grado de exactitud y, aun así, la estimación seguirá estando mal.
La regla fundamental para manejar problemas perversos es que no deben ser tratados como los problemas clásicos. Según Rittel y Webber: "El enfoque de los sistemas clásicos ... se basa en la suposición de que un proyecto ... se puede organizar en fases distintas: “ comprender los problemas “,” recoger información”, “sintetizar la información”, “buscar soluciones” y similares. Con los problemas perversos, sin embargo, este tipo de sistema no funciona. No se puede comprender el problema sin conocer su contexto, uno puede buscar, no significativa de información sin la orientación de un concepto de solución, no es posible entenderlo primero, y luego tratar de resolverlo. "
La mejor manera de abordar los problemas perversos es de hablar de ellos, trabajar desde posibles enfoques y de forma colaborativa donde aparezcan en escena los intereses, prioridades y limitaciones existentes. Es imposible usar herramientas de análisis usuales antes de, ya no reducir el problema, sino posiblemente saber cómo enfocarlo. Durante esta fase puede ser interesante utilizar herramientas como Compendium.
Una vez implicados en el alcance de una solución (posiblemente de compromiso), dada la variabilidad del problema, que puede ir cambiando conforme se esté trabajando en él, resulta interesante aplicar metodologías ágiles para facilitar posibles cambios y evitar que supongan un problema a añadir. En este aspecto resulta muy útil, por ejemplo, el enfoque que aporta SCRUM. Pero esto podrá ser materia para otra entrada, dado lo interesante del estudio del software desde esta perspectiva.
jueves, 29 de octubre de 2009
¡Sálvame!
Llevas horas trabajando frente al ordenador y, de repente, la pantalla cambia de estado. Se fue la luz y se ha apagado, aparece la pantalla azul de la muerte o, simplemente, la imagen de fondo del escritorio porque la aplicación que estábamos usando se ha cerrado inesperadamente debido a algún problema. Bueno, problema ninguno, ¿verdad? Hemos ido salvando puntual y religiosamente nuestro trabajo cada 10 ó 15 minutos… Ah, ¿no? ¿En serio? ¡Pero cómo se te ocurre!
Esta situación se da más a menudo de lo que debería, y de lo que nos gustaría admitir. Precisamente un programador no debería encontrarse en la tesitura de haber perdido el trabajo de una hora, o dos, o ‘n’, por no guardar los cambios que estaba realizando en el código fuente. No debería pasar… pero pasa.
Si trabajáis con Visual Studio, sabréis que el IDE graba automáticamente los archivos que han sido modificados cada vez que realizamos una ejecución de la aplicación que estemos desarrollando. Esto es así para evitar posibles pérdidas de información si, por un casual, nuestra aplicación vuelve inestable al sistema o Visual Studio no es capaz de controlarla por algún motivo. Sin embargo, si el error se ha producido en otro momento (porque Visual Studio se cierre automáticamente, por ejemplo), podríamos encontrarnos ante un verdadero problema, ¿verdad? Bueno, que no “panda el cúnico”, como decía un conocido personaje televisivo de origen mexicano. Visual Studio realiza copias de seguridad de nuestro código cada cierto tiempo, siempre que tengamos establecida la opción correspondiente dentro del menú de “Herramientas, Opciones” (“Tools, Options”). En el grupo “Entorno” (“Environment”) encontraremos la opción “Autorrecuperación” (“AutoRecover”) que, de estar activada, irá guardando en los plazos que pueden establecerse ahí mismo las modificaciones que hagamos sobre nuestro proyecto.
¿Dónde se encuentran los archivos cambiados? Bien, estarán en la ruta:
My Documents\Visual Studio {versión}\Backup Files\{Nombre del proyecto}
Comenzando por el nombre “~AutoRecover”. De hecho, son los usados por Visual Studio cuando muestra las opciones de autorrecuperación tras un problema, pero si no fuera así ya sabemos dónde buscar las copias de seguridad.
Ah, y recordad: un Control+S (o Control+G, según el idioma) de cuando en cuando, no viene nada mal.
P.D.: Y no, no es algo que me haya ocurrido a mí, malpensados... ;)
domingo, 30 de agosto de 2009
Un microgestor de descargas casero
Llevo tiempo dándole vueltas a adentrarme en el estudio de una nueva carrera (sí, soy un poco masoquista), aunque la aparición de Bolonia con sus pros y sus contras (más de estos últimos, según parece) me daba que pensar en cuanto a mi decisión. Obviamente, con el trabajo únicamente me puedo plantear estudios a distancia, por lo que acceder a mi más deseada titulación (Biología) queda, de momento, un poco lejos. Ya el año pasado estuve dándole vueltas a los másteres homologados para el EEES (Espacio Europeo de Educación Superior), en concreto a uno de redes y seguridad informática que me llamaba poderosamente la atención. Sin embargo, y mientras nuestra titulación no obtenga por parte de quienes gobiernan la deferencia que merecen, y se nos otorguen las atribuciones (es decir, tanto obligaciones como derechos) que deberíamos ostentar creo que dejaré correr lo del máster y, a lo sumo, me adentraré en algún estudio por el mero placer de aprender.
En fin, volviendo a lo que iba, este fin de semana he estado mirando alguna información sobre los Grados que empiezan a impartirse este curso académico, y algunos de los que se iniciarán en el próximo, siempre en la UNED. Además de las guías de la carrera y las de cada asignatura, resulta interesante echar un vistazo a los exámenes de años anteriores, para hacerse a la idea del nivel que alcanzan los conocimientos esperados y los contenidos que entran en juego a la hora de evaluar nuestro trabajo.
Uno de los centros asociados más interesantes a este respecto es el de Calatayud, que cuenta en su página web con un depósito de exámenes realmente apabullante. Accedemos a la carrera deseada, marcamos las asignaturas de las que queremos consultar los exámenes y tenemos ante nosotros los correspondientes a los últimos años (desde 2005 en adelante). Según sea la carrera, pueden rondar cerca de los mil exámenes en total para cada una de ellas. Los exámenes aparecen en PDF, y se pueden descargar o visualizar simplemente con pulsar sobre el enlace correspondiente. Así que nada, me dispuse a descargar unos pocos y aunque sólo quería los de los últimos años, el proceso era harto engorroso: “Botón derecho->Guardar enlace cómo…->Aceptar” y con Google Chrome, en el mejor de los casos (ya que descarga los PDF directamente) ir pulsando en cada enlace. Cuando llevaba unos pocos, como habréis imaginado, me he dicho: ¿por qué no hago un programita para automatizar esto? Dicho y hecho, se tarda menos en ello que en descargar los exámenes de una asignatura. El programa no es nada del otro mundo, y lo cierto es que tiene poco interés, pero ya que está hecho, me he dicho: ¿por qué no divago un poco en el blog y dejo el código fuente?
[CSharp]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
namespace Lobosoft.Utilidades
{
public class Downloader
{
WebClient webClient = new WebClient();
Regex urlRegEx = new Regex(@"(((f|ht){1}tp://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)");
///
/// Descarga los archivos apuntados por los enlaces al path indicado.
///
///
La lista de enlaces. ///
La ruta donde descargarlo. public void GetLinksContent(string html, string extension, string outputPath)
{
string filename;
foreach (string url in GetLinksUrl(html, extension))
{
filename = url.Substring((url.LastIndexOf('/') + 1), url.Length - url.LastIndexOf('/') - 1);
webClient.DownloadFile(url, Path.Combine(outputPath , filename));
}
}
// Obtiene todas las URL dentro de los enlaces existentes en la web cuyo código HTML examinamos
List GetLinksUrl(string htmlSource, string extension)
{
List links = new List();
// Obtiene todas las URL de la web
foreach (Match match in urlRegEx.Matches((htmlSource)))
{
//Pero únicamente agrega aquellas que terminen con la extensión dada.
// Podría sustituirse la expresión regular en tiempo de ejecución para devolver únicamente
// las que nos interesan, pero así puede quedar algo más genérico.
if (match.Value.EndsWith(extension))
{
links.Add(match.Value);
}
}
return links;
}
}
}
[/CSharp]
Como veis, no tiene demasiado misterio. Un método, GetLinksUrl() se encarga de buscar en el código HTML de la página las URLs que contenga, y lo devuelve como una lista de enlaces a GetLinksContent(), que se encarga de ir recorriéndola y descargando los archivos a una determinada carpeta que habremos creado previamente. La llamada a este método se llevaría a cabo con el código fuente de la página en cuestión. Podemos haberlo leído de un archivo local, o bien recuperarlo de Internet mediante un WebClient (con su método estático DownloadString(url)), o si lo usamos desde una aplicación Windows, utilizando un objeto WebBrowser y accediendo a su propiedad DocumentText en un momento determinado). El misterio (o no tanto) estará en saber de qué carrera ando buscando información ;)
Bueno, y ahora, a hacer lo mismo en Python. Estoy mirando una librería, PycURL, que pinta bastante bién, y vamos a ver si consigo lograrlo. Si me pongo a ello os contaré algo por aquí, claro está. ¡Vamos a ello!
jueves, 27 de agosto de 2009
Evitando fallos de carga dinámica de ensamblados
En ocasiones necesitamos cargar un tipo contenido en una DLL de forma dinámica, sin haber establecido una referencia cuando generamos nuestra aplicación. Es el caso de un sistema de plugins en el que una aplicación debe cargar en tiempo de ejecución una serie de DLLs que le ofrecen una funcionalidad adicional. En .NET disponemos de la poderosa herramienta de la reflexión (reflection) para cargar una DLL dinámicamente e instanciar un objeto de una determinada clase para hacer uso del mismo. En principio su uso es bastante sencillo y no suele darnos demasiados problemas pero ¿qué ocurre cuando una DLL que cargamos dinámicamente hace referencias a otras? Si esas librerías están ubicadas en la ruta de nuestro ejecutable no tendremos mayor problema. Recordemos que el comportamiento establecido por diseño en .NET para localizar una DLL es el siguiente:
- Busca en el GAC.
- Intenta localizarla en el directorio que contiene el ejecutable.
- La busca en un subdirectorio dentro de la ruta del ejecutable, cuando se ha establecido a través de un archivo de configuración.
En el caso en que nuestras DLLs no estén en ninguno de estos lugares, se producirá una excepción al intentar cargar el ensamblado correspondiente.
Para la primera situación, la solución es fácil: basta con instalar en el Global Assembly Cache la DLL en cuestión. Sin embargo, no es algo que convenga hacer con todas nuestras DLLs: el GAC debe mantenerse tan limpio como sea posible.
En el segundo caso, por supuesto, bastaría con tener todas las DLLs en la ruta del ejecutable en cuestión, pero no es algo recomendable ni siempre debe ser así. Comenzaríamos a tener librerías duplicadas en el sistema y las aplicaciones terminarían por ser difíciles de mantener.
Para el tercer caso, la solución pasa por incluir unas etiquetas que especifiquen dónde buscará el CLR nuestro ensamblado. Hay que llevar a cabo una firma del ensamblado para obtener su strong name, usar los atributos publicKeyToken y la versión del ensamblado para terminar incluyendo en nuestro archivo de configuración algo como lo siguiente (se trata de un XML, claro está, aunque sustituyo un carácter para que no se interprete como tal y sea visible):
[configuration>
[runtime>
[assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
[dependentAssembly>
[assemblyIdentity name="Dummy" culture="neutral"
publicKeyToken="589048994a485948"/>
[codeBase version="1.0.1423.13588"
href="FILE://C:/Lobosoft/Dummy.dll"/>
[/dependentAssembly>
[/assemblyBinding>
[/runtime>
[/configuration>
Pero, ¿qué ocurre si las DLLs pueden estar en distintas ubicaciones, no tenemos posibilidad de firmar digitalmente las mismas, o se da algún otro impedimento para optar por esta solución? En ese caso, tal vez debamos optar por controlar el evento AssembyResolve, asociado al dominio de aplicación actual. Su uso es bastante simple:
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolveEventHandler);
Al incluir este par de líneas, en el momento en que deba resolverse si un determinado ensamblado ha sido cargado. Esto ocurre, por ejemplo, al hacer una llamada al método CreateInstance() de un determinado Assembly, para instanciar un objeto de un tipo determinado. En el caso de que alguna de las DLLs referenciadas por el ensamblado no esté disponible, nuestro manejador de evento procederá a llevar a cabo la carga correspondiente:
[Csharp]
Assembly AssemblyResolveEventHandler(object sender, ResolveEventArgs args)
{
//Este manejador es llamado únicamente cuando el CLR intenta cargar un ensamblado y se produce un error.
//Recupera la lista de ensamblados referenciados
Assembly needAssembly, objExecutingAssemblies;
string assemblyTemporalPath = "";
/* myAssembly, en este caso, es el objeto que intentó realizar la carga fallida. Es el Assembly que hemos cargado
* mediante reflection, y que ha lanzado el evento al hacer un CreateInstance() sobre él. Si quisiéramos llevar a
* cabo el control sobre nuestra aplicación podríamos sustituir myAssembly por Assembly.GetExecutingAssembly();
*/
objExecutingAssemblies = myAssembly;
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
{
if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
{
assemblyTemporalPath = DllBasePath + @"\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
break;
}
}
needAssembly = Assembly.LoadFrom(assemblyTemporalPath);
return needAssembly;
}
[/Csharp]
Es el caso para un Assembly (myAssembly) que pudimos cargar con un Assembly.LoadFile(DllBasePath + “\DllName.dll”), y que al recuperar sus tipos e intentar instanciar uno de ellos con Assembly.CreateInstance() ha intentado acceder a otra DLL de la que haga uso o herede. Si la ubicación de esa DLL no coincide con los tres casos que especificábamos antes, la carga dará un error. Será entonces cuando entre en acción el manejador del evento AssemblyResolve y llevará a cabo la carga de las DLLs necesarias para continuar con la creación de la instancia de nuestro objeto.
martes, 25 de agosto de 2009
Examinando los ADS
Hace unos días veíamos cómo podíamos crear varios streams alternativos de datos o ADS en un fichero dentro de un sistema de archivos NTFS. Aunque el manejo y creación de los streams es simple, lo cierto es que los sistemas operativos de Microsoft no han incluido una gestión de los mismos orientada al usuario hasta Windows Vista, y aun en Windows 7 el soporte es mínimo. Se trata, ciertamente, de una característica no destinada al usuario común de los sistemas, sino más bien orientada a usuarios avanzados como los desarrolladores, pero a causa del desconocimiento de esta característica puede ser usada con fines menos benévolos de los que podría esperarse.
En la primera entrada creamos varios ADS sobre un fichero de texto, pero no éramos capaces de saber si un determinado archivo contiene o no ADS. Existe una forma bastante simple y fácil de constatarlo, que no es otra que intentar copiar el fichero a un sistema de archivos que no sea NTFS. En ese caso, Windows nos mostrará una alerta ante la pérdida de estos streams alternativos, si llevamos a cabo la operación:
De hecho, la inclusión de un fichero dentro de un ZIP, por ejemplo, también provoca la pérdida de los streams alternativos. Sin embargo, existe una herramienta que realiza una copia del archivo con sus ADS correspondientes: ntbackup, muy usada para realizar copias de seguridad y con la que tendremos oportunidad de enfrentarnos algo más adelante, cuando llevemos a cabo tareas de gestión de los ADS.
Si os fijáis, junto al nombre del stream se incluye la cadena :$DATA, es decir, está accediendo al bloque de datos del stream indicado. Si recordáis, existía un stream principal equivalente al bloque DATA de sistemas como FAT, que contenía los datos del archivo en cuestión. Por tanto, es posible “invocar” al stream principal del archivo llamándolo con
Mifichero.txt::$DATA
Lo que introduce un pequeño gran problema de seguridad en el acceso a los archivos. Uno de los problemas de anteriores versiones del servidor web IIS era que un usuario malicioso podía ver el contenido de cualquier archivo simplemente llamándolo desde la URL del modo siguiente:
http://miserver/fichero.aspx:$DATA
De hacer esto, IIS no era capaz de reconocer una llamada ASP.NET, por lo que el fichero no se interpretaba por parte del runtime y era servido directamente, quedando expuesto el código fuente.
Sobran las palabras si añadimos que no se trata de un método muy práctico para constatar la existencia de ADS en el sistema. Eso sí, nos sirve además para librarnos de los ADS, ya que el fichero resultante de la copia no los contendrá si decidimos restaurarlo a su ubicación original. Sin embargo, a partir de Windows Vista existe un nuevo parámetro sobre el comando dir, el /R, que permite obtener un listado de estos ADS.
Existen aplicaciones más avanzadas, que podemos usar incluso en Windows XP ó 2000, como LADS. Creada por Frank Heyne su descarga es gratuita y, como veremos, nos permite analizar un directorio y su contenido (con recorrido recursivo mediante el parámetro /s) , exponiendo todos los ADS encontrados y su tamaño. Como curiosidad, veamos su ejecución sobre algunos de los archivos usados en la entrada de introducción a los ADS:
Como veis, el fichero dinos.jpg incluye el identificador de zona que os comentaba, ya que fue descargado de Internet. Por otro lado, MiFichero.txt incluye otro ADS llamado Texto, con el texto del “hola mundo” que incluí en un primer momento en la entrada.
En cuanto al identificador de zona, como curiosidad, el valor se corresponde con la siguiente tabla:
0: Local Machine
1: Intranet
2: Trusted
3: Internet
4: Untrusted
1000 o superior: User Defined
También veíamos el otro día que era posible incluir otros archivos dentro de un stream usando copy o type. Sin embargo, el funcionamiento de ambos comandos no es el mismo. En la siguiente imagen podemos observar, gracias a LADS, que hemos incluido varios archivos en MiFichero.txt. Los de tamaño pequeño (Calculadora.exe, dinos, dinos.jpg) fueron llevados a cabo mediante un comando copy. El tamaño es mucho menor del esperado, y no contienen el archivo completo. Para incluir un archivo dentro de un ADS podemos usar el comando type. Es el caso de los streams calcu.exe y dinos2.jpg.
De hecho, es posible ejecutar la calculadora de Windows desde este stream. Para ello, usaremos la orden start indicando como parámetro el nombre del archivo que contiene el ADS y el nombre del stream. En el ejemplo siguiente vemos cómo, por motivos de seguridad, hay que especificar la ruta completa del archivo que contiene el ADS (la captura de pantalla de error se correspondería con la primera ejecución, que devuelve el mensaje de "Acceso denegado" en la consola de comandos, en el segundo caso se muestra la calculadora de Windows):
Incluso es posible comprobar, dentro de los procesos de sistema, que la calculadora en cuestión se ha ejecutado desde dentro de un ADS. A continuación muestro las capturas de pantalla del Administrador de Tareas del sistema
y del Explorador de Procesos de SysInternals.
Como vemos, los ADS ofrecen un interesante y amplio campo de estudio. En próximas entradas intentaremos centrarnos algo más en la programación y la seguridad.
domingo, 23 de agosto de 2009
ADS, una introducción
El sistema de archivos NTFS alberga algunas características que, no por poco conocidas por el usuario son menos usadas por los desarrolladores. Entre otras, cuenta con los flujos alternativos de datos, o Alternate Data Streams (ADS, también conocidos como named streams), que permiten adjuntar información de muy diverso tipo a los ficheros tradicionales. Los ADS van más allá de los metadatos, y permiten incluir tanta o más información como el fichero que los contiene, aunque sus características y, en especial, el escaso conocimiento o publicidad que se ha dado hasta la fecha sobre su existencia constituyen, como cualquier característica desconocida, una vulnerabilidad que puede aprovechar esta debilidad para llevar a cabo ataques sobre nuestros sistemas.
Ya que llevo tiempo deseando escribir sobre el tema, y debido a que en los últimos meses he andado con el blog “a medio gas” por diversos motivos y compromisos adquiridos, creo que lo mejor será presentar una introducción a los ADS para, a continuación, profundizar en el uso que podemos darles desde nuestras aplicaciones.
Como ya refería al comienzo, los ADS son una característica propia de NTFS, por lo que no está disponible en otros sistemas de archivos (FAT, Ext3, …). Es más, si un archivo creado en un sistema que use NTFS incluye uno o más flujos alternativos, éstos desaparecerán al copiar el archivo a otro sistema de archivos que no los soporte. Bueno, en realidad serán eliminados todos menos uno: el que tiene el archivo de forma predeterminada. Pero veamos en qué consiste exactamente un ADS.
En la siguiente gráfica, podemos ver cómo se estructura la información (datos y metadatos) de un archivo en FAT y NTFS. Vemos que en el antiguo sistema de archivos, un hipotético archivo de llamado “nombre.ext” contiene una serie de atributos (sólo lectura, archivo, oculto…) y un bloque de datos, es decir, el contenido real del archivo. En NTFS, por el contrario, el archivo contiene los atributos además de una información de seguridad (permisos) y una serie de streams asociados. El stream principal es el predeterminado que mencionaba antes. No tiene un nombre asociado y se correspondería con el bloque de datos del archivo en FAT. Es decir, al llevar un archivo NTFS a FAT mantendríamos la información de atributos y la del stream principal que se correspondería al bloque de datos, pero se perdería el resto de información, incluidos los streams o flujos alternativos. De hecho, este stream principal es obligatorio, y si por un casual creásemos un archivo directamente accediendo a uno de los streams con nombre (alternativos), el sistema crearía un stream principal de 0 bytes, vacío y sin nombre. Cabría señalar que los atributos se corresponden a todo el archivo, no al stream principal, por lo que todos los flujos son afectados por un cambio en aquellos.
Cuando un programa abre un fichero NTFS, realmente está accediendo al stream principal. Para acceder a uno de los streams alternativos debe interponerse un símbolo de dos puntos ( : ) entre el nombre del archivo y el del stream al que deseamos acceder. Al corresponderse los dos puntos con el identificador de una unidad, para acceder a un stream llamado “MiStream” dentro del archivo “C”, tendríamos que hacerlo con la secuencia con “C: MiStream”, pero el sistema siempre resuelve la ambigüedad creada dando prioridad a la identificación de unidades. Por esto, si lo que deseamos es acceder a un stream, deberíamos hacerlo con “.\C:MiStream”.
Por último, antes de entrar en algunos ejemplos sobre cómo usar los ADS desde el propio sistema operativo, indicar que los directorios también pueden tener asociados diversos ADS con nombre, pero no tienen ninguno principal o por defecto.
Desde la propia consola de comandos de Windows podemos crear y acceder a los ADS de un fichero o directorio. Obviamente, no es la mejor forma de hacerlo; sin embargo, los sistemas operativos Windows 2000 y Windows XP no ofrecen herramientas específicas para trabajar con esta característica de NTFS.
Podemos crear un archivo de texto normal y consultar su tamaño desde la consola de comandos:
Crear un stream alternativo, de nombre “Texto” con un contenido textual es tan simple como escribir sobre ese stream. Como puede observarse, un comando type solamente muestra el contenido del stream principal, el de datos. Para consultar el contenido textual del stream alternativo creado debemos usar el comando more (ni tan siquiera el type funciona, como veremos más adelante en otro ejemplo).
Si vemos el contenido del directorio, observaremos que el tamaño del fichero no ha cambiado, aunque sí la hora de modificación.
Es posible copiar un archivo dentro de un stream. Como vemos en el ejemplo, hemos incluido un archivo de imagen dentro del archivo de ejemplo sin que cambie su tamaño, considerablemente menor al de la imagen incrustada.
Por último, vemos que es posible ver el contenido del stream de texto, pero se nos hace algo más dificultoso recuperar el del binario con la imagen. En la próxima entrada veremos cómo podemos acceder a este contenido en particular.
Ahora bien: ¿para qué se usa, o qué usos podemos darle a los ADS? Bueno, en principio Microsoft introdujo los ADS en NTFS para dar soporte a funcionalidades avanzadas de sistemas UNIX, OS/2 y Macintosh, pero en la actualidad se le dan usos tan interesantes como incluir un identificador de zona (en el ADS Zone.Identifier) con información del la zona del mundo desde la que se ha descargado un determinado fichero de Internet. También algunos metadatos de los archivos, como el nombre de autor, título, comentarios, etc. son almacenados en respectivos ADS. En nuestro caso, como desarrolladores, deberíamos plantearnos la información que queremos almacenar en estos compartimentos y si es necesario realmente usarlos. En principio no debería incluirse información vital para nuestras aplicaciones, ya que una copia del archivo a través de un sistema que no use NTFS haría que se perdiese esta información. Tal vez podría sernos útil para almacenar información fácil de recuperar pero que, con el uso de los ADS, nos permite un acceso más inmediato. Sería el caso de las imágenes a pequeña escala para archivos de imagen (thumbnails), metadatos para documentos… De esta manera el archivo podría almacenarse en cualquier sistema de ficheros, pero en un NTFS mejoraríamos el rendimiento y el acceso a esta información no vital pero sí útil.
Esto es todo, de momento. En sucesivas entradas veremos algunas características más avanzadas de los ADS y, sobre todo, investigaremos un poco en torno al uso que podemos hacer de los mismos desde nuestras aplicaciones.


















