Descompilando programas

July 13th, 2010

Volta e meia me perguntam sobre a possibilidade de decompilar programas .NET. Por “descompilar” entendo “produzir a partir de um executável um programa fonte em linguagem de alto-nível que, quando compilado, tem a mesma funcionalidade que o executável original”. É importante observar que qualquer programa, em qualquer linguagem de alto nível é “descompilável” pelo menos em linguagem Assembly ou similar, até porque a própria CPU do computador tem que saber executá-lo.

É então possível descompilar programas .NET, de maneira semelhante ao que era possível com descompiladores para o Clipper como “Unclip” ou “Valkyre”, que tanto transtornos causaram nos anos noventa? A resposta curta é sim. Mas continue lendo.

Uma das grandes vantagens dos “assemblies”.NET  (.EXE ou .DLL) é que eles rodam em um “ambiente gerenciado”, onde os programas não podem causar danos aos outros programas rodando no computador do usuário. Este ambiente gerenciado depende basicamente do seguinte para funcionar:

  • Código executável definido em uma “linguagem intermediária – IL” ao invés da linguagem do processador. Este código pode ser “verificado” em tempo de carga e execução pelo compilador “JIT”, de forma que o mesmo não possa efetuar operações não permitidas pelas configurações de segurança, como acessar um endereço de memória não permitido;
  • Informação de tipo em tempo de execução (“metadata”) de forma a validar os “casts”, chamadas de funções entre diferentes DLLs e manter a integridade do sistema de tipos;
  • A primeira compilação (VB ou C# para IL) não deve otimizar o código, pois as otimizações podem impedir que o compilador JIT verifique o código. A presença de otimizações tem o efeito colateral de atrapalhar os descompiladores.

Evidentemente, os fatores acima facilitam a descompilação dos programas. Note que estes recursos têm como efeito principal o aumento da segurança e integridade tanto para os desenvolvedores como para os usuários finais, o que é considerado hoje em dia uma grande vantagem. Por exemplo, a maioria dos erros no gerenciamento manual de memória dos ambientes tradicionais é eliminada. A robustez e a vulnerabilidade à descompilação também são características do outro grande ambiente gerenciado utilizado atualmente, o Java.

Dito isto, como impedir que um usuário do seu software possa descompilá-los? Existem várias maneiras:

  • Impedir o acesso aos executáveis. Um aplicativo web, por exemplo, roda em um servidor e os usuários não têm acesso aos executáveis. Mesmo um aplicativo desktop pode ter parte da sua funcionalidade em WebServices, que também rodam em um servidor.
  • Escrever parte do programa como código não gerenciado, quer como DLL, objeto COM ou mesmo em código não-gerenciado dentro de programas C++ .NET. Evidentemente isto elimina as vantagens do código gerenciado pelo menos em parte do seu programa.
  • Escrever parte do código em Assembler IL usando recursos que dificultam a descompilação, como o uso de identificadores não suportados pelas linguagens C# ou VB.NET, por exemplo, chamando uma função com o mesmo nome de uma palavra reservada de linguagem de alto-nível.
  • Usar um “ofuscator” para alterar o código de forma a impedir a descompilação.

Os ofuscadores (pesquise por “.NET obfuscators” na Web) são a maneira mais fácil e direta de proteger o seu código, pelo menos a níveis comparáveis aos de outras linguagens de alto nível como C/C++ ou Delphi. Basicamente eles processam os assemblies (.EXE/.DLL) e geram outros assemblies que tem a mesma funcionalidade, mas que são mais difíceis de descompilar.

Como isso é feito? Um dos truques é renomear os símbolos (funções, propridedades, campos etc) “private” de seus executáveis. Quando um programa .NET referencia um símbolo dentro do mesmo assembly, ele não o faz por nome e sim através de um índice em uma tabela. O nome é então supérfluo e pode ser trocado para identificadores repetidos ou mesmo palavras reservadas da linguagem, tornando o fonte descompilado impossível de ser recompilado. Alguns produtos possuem recursos bastante sofisticados e o resultado é uma proteção até maior que um programa Delphi ou C++.

Caso você deseje proteger seus programas, os ofuscadores são uma alternativa bastante prática.

Culto à Carga no desenvolvimento de software

June 24th, 2010

>>>> Esta é uma coluna publicada em 2004 na MSDN Magazine Brasil, mas bastante comentada.

Durante a Segunda Guerra Mundial os americanos ocuparam várias ilhas no Pacífico Sul em suporte às operações contra o Japão. Eles construíram nestas ilhas pistas de pouso e armazéns para facilitar a movimentação de carga por via aérea. De forma a não serem massacrados e devorados pela população local – que estava em ampla maioria – os americanos distribuíam parte da carga que circulava pelas bases à população local. Os nativos adoraram ganhar coisas como comida enlatada e tendas. Rapidamente eles se acostumaram com as comodidades da “vida moderna”.

Terminada a guerra os americanos foram embora e abandonaram as instalações, até porque os aviões tinham autonomia maior e as pistas não eram mais necessárias. Os nativos, acostumados com a carga ficaram desolados. Partiram então para imitar o que os militares americanos costumavam fazer e que, na percepção deles, era a causa da vinda dos aviões. Construíram novas pistas de pouso, torres de controle, fones de ouvido de madeira “operados” por nativos e tudo mais de forma que os aviões “pudessem” voltar à pousar. Eles criaram um “Culto à Carga” (“cargo cult”). Evidentemente, apesar do grande esforço empreendido pelos nativos, os aviões não retornaram.

Note que construir as pistas de pouso era a forma encontrada para suprir a razão original de movimentar carga. Os nativos confundiram a forma com a necessidade original e ficavam cultuando a forma sem se importar com a razão.

Nós, desenvolvedores, nos achamos mais espertos que os nativos da Micronésia, mas o “Culto à Carga” é muito comum no desenvolvimento de software. Às vezes gasta-se um grande esforço para repetir um ritual cuja razão original de existir há muito se foi. O meu exemplo preferido é a chamada “notação húngara” para dar nomes às variáveis.

Na década de oitenta, Chales Symoniy, então um arquiteto chefe da Microsoft, defendeu uma convenção na qual os nomes das variáveis na linguagem C deveriam ter o tipo como prefixo. Por exemplo, uma variável do tipo usingned int teria o prefixo uint. Essa notação resolvia um sério problema da linguagem C antiga (padrão “Kernighan & Ritchie”) usada na época, que era a falta de tipagem forte. Podia-se atribuir um “char * para um int e o compilador não reclamava! Com a notação, tinha-se alguma tipagem no “olhômetro”: era só ver se os prefixos nas atribuições eram iguais. Como os nomes das variáveis ficavam muitas vezes impronunciáveis e dada a origem Húngara do Sr. Symoniy, seus colegas por gozação deram o apelido de “notação húngara” e o apelido pegou. Ela foi bastante popularizada pela API do Windows 3.0 e muito utilizada pela Microsoft e pelos programadores em geral.

A notação húngara tinha lá seus problemas. Ela impedia que se abstraíssem os tipos com o uso do comando typedef. Se você desejasse mudar o tipo de uma variável (de int para float, por exemplo), deveria alterar não só a declaração, mas também todos os lugares que usassem a variável, já que o prefixo tinha mudado de “i” para “f”.
Com o passar dos anos, a utilidade da notação húngara foi diminuindo. O padrão C ANSI de 1990 que substituiu o “Kernighan & Ritchie” era fortemente tipado e não permitia coisas como atribuir um char * a um int por acidente. A API do Windows 3.10 de 1991 abandonou o húngaro tradicional, pois já previa futuras extensões de 32 bits e a codificar tipos de 16 bits nos programas traria problemas no futuro. Acabou a guerra. Os aviões voltaram para casa. É o fim da notação húngara então?

Nem tanto. Ainda hoje, em 2004, eu vejo nativos, quer dizer, programadores de linguagem fortemente tipadas como o C# usando notação húngara, mais de 10 anos depois de qualquer razão lógica para seu uso ter evaporado. Na verdade, nas minhas consultorias eu freqüentemente encontro não só a notação húngara como normas e convenções sem o menor sentido. A única explicação que eu recebo sobre sua existência é uma variação do “aqui nós sempre fizemos assim”. Fico então imaginando algum analista bem intencionado de farda verde e com um capacete de tenente do exército americano elaborando um padrão que fazia sentido para resolver um problema que com o tempo desapareceu, mas que continua sendo cultuado pelos nativos em trajes sumários, colares de conchas e ossos amarrados aos cabelos sentados à frente dos computadores.
Conclamo a todos os desenvolvedores que são vítimas de padrões aparentemente sem sentido que questionem a razão da existência desses padrões. Caso as razões não sejam aparentes, adote um novo padrão usando o bom senso. No caso dos nomes de variáveis, acho um bom padrão usar uma expressão baseada em um ou mais substantivos seguido ou não de um adjetivo que expresse o que a variável é. Por exemplo, “TotalPedido”, “NomeUsuario” e “QtdTelasAbertas” e evitar nomes que não tem significado como “temp1”, “var2” e “xxx0”, para não dizer do “húngaro”.

Ao contrário dos habitantes da Micronésia, não temos a desculpa de sermos nativos ignorantes para adorarmos algum “Culto à Carga”.

Mauro Sant’Anna (mas_mauro@hotmail.com) não conhece a Micronésia nem nunca gostou da notação húngara, até porque mesmo antes do padrão ANSI a maioria dos compiladores C já tinha tipagem forte. Para saber mais sobre Culto à Carga leia em http://en.wikipedia.org/wiki/Cargo_cult. Charles Simonyi (http://en.wikipedia.org/wiki/Charles_Simonyi) tornou-se o primeiro turista espacial a repetir a viajem.

Windows Mobile (não Phone 7) ainda está vivo

June 19th, 2010

Quando a Microsoft anunciou sua nova plataforma móvel, o Phone 7, algumas pessoas ficaram preocupadas porque ele não seria compatível com a plataforma atual (WinMo 6. 5). O problema é que existem alguns dispositivos e aplicativos desenvolvidos para esta plataforma, especialmente usando dispositivos da Symbol (agora Motorola) e seus concorrentes.

Em um recente anúncio (http://bit.ly/aP8w4f), a Microsoft declarou seu apoio continuado a plataforma atual e até mesmo uma nova versão para o próximo ano.

Então, me parece que a Microsoft terá duas linhas de sistemas operacionais para telefone/celular:

  • Phone 7: Mais orientada para o consumidor, um pouco fechado (como o iPhone)
  • Windows Embedded Compact 7: uma nova versão da plataforma atual, aberta e compatível

Vejo isso como boa notícia. Sempre gostei de meus dispositivos de CE (estou no meu terceiro telefone WinMo). Eu nunca gostei da abordagem “fechada” da Apple; essa é a razão que eu nunca tive um iPhone. Apesar de perder mercado e um pouco órfãos, ainda existem alguns bons produtos baseados WinMo, como o Samsung Omnia II.

Os dilemas da nuvem

June 17th, 2010

>>>>>> Coluna publicada na .NET Magazine.

Em artigos anteriores nesta coluna eu defendi a idéia de que: 1)existem enormes economias de escala em hospedagem de sites e 2)a infra-estrutura atual, por ter acumulado anos e anos de modificações, é muito mais complexa do que deveria ser. Estes fatores apontam para grandes oportunidades em uma nova plataforma, feita desde o início visando fácil hospedagem e escalabilidade. Idealmente, os aplicativos nesta nova plataforma não deveriam sequer se preocupar com servidores físicos; o ambiente todo seria “virtual”. O Microsoft Azure, conforme inicialmente anunciado seria uma plataforma assim. Ele é baseado no ASP.NET, mas com APIs novas, de forma a viabilizar este “ambiente virtual”. Por exemplo, existe um novo tipo de aplicativo chamado “Worker Role” que é uma espécie de serviço, mas sem as suas complexidades de instalação e administração.

A Microsoft anunciou em novembro de 2009 os detalhes da versão final do Azure, disponível em ambiente de produção a partir do segundo trimestre de 2010. A versão final inclui além do “ambiente virtual” a possibilidade de executar praticamente qualquer aplicativo Windows em “máquinas virtuais”, incluindo aplicativos não desenvolvidos especificamente para o Azure. Evidentemente, neste caso temos que nos preocupar muito mais com o ambiente de execução, negando muitas das vantagens do “ambiente virtual”.

Eu confesso que fiquei um pouco decepcionado, visto que este tipo de oferta não aproveita o ponto forte da Microsoft, que é exatamente a possibilidade de estender a plataforma de desenvolvimento e execução. Ela é semelhante a outras ofertas de hospedagem em máquinas virtuais como a Amazon ou mesmo de provedores tradicionais. O ambiente virtual continua lá, mas não é enfatizado – pelo menos nesta versão.

Por que esta mudança de paradigma? A resposta, em uma palavra é “inércia”. Em primeiro lugar, pouca gente está interessada em modificar aplicativos que já funcionam. Em segundo lugar, o pessoal já está meio acostumado – para o bem e para o mal – com máquinas virtuais. A oferta do “ambiente virtual” é pouco familiar e talvez até mesmo desconfortável.

Além disso, existe nas empresas uma divisão forte em departamento de desenvolvimento e departamento de produção (ou infra-estrutura). Eles usualmente têm orçamentos separados e não gostam de conversar muito um com o outro. A quem o Azure se destina então? A médio e longo prazo, os desenvolvedores teriam que desenvolver aplicativos para ele. Mas visto que não existe ainda nenhum aplicativo desenvolvido, quem poderia comprar o Azure agora? Evidentemente o pessoal de “produção”, que está acostumado com a idéia de máquinas virtuais, mas é absolutamente imune a conversas de “plataformas virtuais”, para as quais eles não têm a menor necessidade, visto a falta de aplicativos para rodar.

A realidade é então que a Microsoft teve que oferecer um produto para o qual existe um mercado agora – as máquinas virtuais, mesmo que isso não a interesse tanto em longo prazo.

Isto significa que nós desenvolvedores devemos esquecer o Azure? Eu acho que não. O principal é que escrevamos os novos aplicativos de forma que eles possam ser facilmente adaptados no futuro par o Azure, mesmo que eles rodem de maneira convencional no presente. Por exemplo, devemos fatorar serviços de forma que o código de negócios esteja bem separado e possa ser colocado no futuro em um “Worker Role”. Ou separa o código de acesso a banco de dados. Na verdade, se implementarmos as boas práticas de desenvolvimento, nossos aplicativos estarão substancialmente bem preparados para uma eventual migração futura para o Azure.

Viva o Scrum

June 17th, 2010

Sempre fui um entusiasta das metodologias ágeis, embora não concordasse com tudo.

Estou estudando Scrum e posso dizer que não faço nenhuma restrição. Todas as coisas fazem sentido e me parece que é um pacote bem acabado.

Caminho para me certificar como instrutor e oferecer cursos.

Novo endereço de blog

June 17th, 2010

O meu blog anterior (http://blogs.vstsrocks.com.br) era hospedado em um “condomínio” e como tal era meio problemático. O último dos problemas: ele foi desligado pela pessoa que “tomava conta” sem me avisar previamente.

Decidi então tomar o controle e hospedar no meu domínio, sem anuncios e problemas além do meu alcance.

Mauro Sant’Anna