18 05 2016
Como podemos obfuscar aplicações .NET?
Se você trabalha com desenvolvimento de aplicações .NET há algum tempo, provavelmente você já ficou sabendo que é possível decompila-las, resultando em um código bem parecido com o código original. Para dificultar esse processo, ferramentas de obfuscação vêm com o propósito de embaralhar o código contido dentro do executável e dlls, de forma que, mesmo que alguém decompile o executável, o código gerado seria humanamente impossível de entender.
No artigo de hoje vamos analisar duas ferramentas que servem para obfuscar aplicações .NET: o Dotfuscator e o ConfuserEx. E, por fim, veremos algumas vantagens e desvantagens do processo de obfuscação, além da minha opinião sobre esse assunto.
Escolhendo uma aplicação de exemplo
Para testarmos as ferramentas de obfuscação, temos que escolher um aplicativo de exemplo. Normalmente os artigos sobre esse assunto criariam um projeto muito simples (talvez uma calculadora que soma dois números) para demonstrar esse processo. Porém, eu queria algo mais real. Como, por motivos óbvios, eu não posso compartilhar o código de aplicativos em que estou trabalhando na empresa, que tal encontrarmos um exemplo de aplicativo na internet?
Procurando por exemplos de projetos, acabei encontrando este aqui no CodeProject: Social Club – Sample application using WinForms, C#.NET, ADO.NET and MS Access. Ele é basicamente um aplicativo bem simples com um formulário de login e um cadastro de membros de clube. O legal é que ele utiliza ADO.NET para salvar os dados no banco, então, não é um projetinho exatamente tão simples assim.
Vamos baixar o projeto no CodeProject (para o caso do aplicativo não estar mais disponível no CodeProject, eu disponibilizei uma cópia aqui no meu site também). Uma vez baixado e descompactado, abra o projeto no Visual Studio, recompile a aplicação, execute e você verá o resultado:
Nota: caso você não tenha sacado, a senha está localizada dentro das settings do projeto (o que, a propósito, é uma péssima prática). Por padrão, o usuário é “demo” e a senha “demo123”.
Eu resolvi pegar essa aplicação de exemplo só para termos a mesma base na demonstração desse artigo. Se você preferir, faça o teste com o seu próprio projeto.
O que será que conseguimos extrair ao abrirmos o executável desse projeto em uma ferramenta de reflector? Vamos conferir?
Eu costumo utilizar o Telerik JustDecompile quando preciso checar o conteúdo de um assembly .NET. A sua utilização é muito simples. Basta executar o JustDecompile, escolher “Open -> Files” e abrir o .exe ou .dll a ser decompilado:
E veja só o resultado. Todas as classes e métodos do executável decompilados do jeitinho que eles foram definidos no projeto. A única coisa que perdemos são os comentários:
Assustador? Vamos ver agora como podemos embaralhar um pouco o código para que ele fique mais difícil de entender.
Qual obfuscador utilizar?
Agora que já temos a aplicação que queremos obfuscar, qual obfuscador devemos escolher?
A ferramenta de entrada dos obfuscadores já vem junto com o Visual Studio e chama-se Dotfuscator. Ela pode ser executada através do menu “Tools -> PreEmptive Dotfuscator and Analytics“:
Além do Dotfuscator, mostrarei também a utilização do ConfuserEx, que é um obfuscador gratuito e open source, disponível no GitHub.
Testando o Dotfuscator
Ao abrirmos o Dotfuscator, a primeira coisa que temos que fazer é irmos até a seção “Inputs” para adicionarmos as “dlls” e “exes” que queremos obfuscar:
Feito isso, como estamos trabalhando com a versão gratuita do Dotfuscator, nós não temos muitas opções de customização. Dessa forma, para realizar a obfuscação padrão (a mais simples e mais “fraca“), clique no botão “Build Project“:
Se o projeto do Dotfuscator ainda não tiver sido salvo, ele pedirá que você salve o projeto em algum lugar do disco e em seguida gerará o resultado dos assemblies obfuscados no subdiretório “Dotfuscated“. Vamos ver como é que ficou o executável decompilado?
Já deu uma melhorada, não é mesmo? A obfuscação padrão do Dotfuscator renomeia atributos e métodos privados utilizando nomes que não são compreensíveis (a, b, c, etc). Entretanto, tudo o que é nome de classe, método e propriedade pública, fica com o nome original. Dessa forma, conseguimos encontrar o formulário “Login“, porém, fica um pouco difícil descobrir que o método chamado “a” é o método que está efetuando o login (além de ele ficar um pouco encriptado devido à mudança dos nomes dos atributos privados):
Testando o ConfuserEx
Agora que já demos uma olhada no resultado da obfuscação básica do Dotfuscator, vamos testar o ConfuserEx. Porém, antes de continuar, já vou dizendo: a documentação dele é bem confusa (desculpe o trocadilho!), então, se prepare para pesquisar bastante caso você queira fazer coisas avançadas com ele.
Primeiramente, vamos baixar a versão atual da branch “master” do ConfuserEx no GitHub:
Aproveite e baixe também o “master” do projeto “dnlib” no GitHub. Não sei porque, mas, esse projeto está faltando quando baixamos o ConfuserEx. Se você preferir, eu fiz o download e coloquei aqui no meu site o ConfuserEx-master e o dnlib-master, versão de março de 2016.
Uma vez baixados os arquivos zip, descompacte o ConfuserEx em uma pasta e o dnlib em outra. Feito isso, jogue o conteúdo do dnlib descompactado na pasta “dnlib” do ConfuserEx:
Em seguida, abra a solução do Confuser no Visual Studio, compile e execute o projeto “ConfuserEx” (que é um projeto WPF):
Bem complicado, não é mesmo? Se você preferir, eu baixei tudo, compilei, compactei a release pronta e estou disponibilizando a versão de março de 2016 para download aqui no meu site. Basta descompactar esse zip em uma pasta e abrir o executável ConfuserEx.
A primeira coisa que temos que fazer ao executarmos o Confuser é escolhermos o diretório base e os assemblies a serem obfuscados (no nosso exemplo, o executável e a dll do projeto “SocialClub“):
Depois, temos que ir até a aba “Settings” para configurarmos as regras de obfuscação. Poderíamos definir regras diferentes para cada assembly, mas, para esse exemplo, vamos definir uma regra global:
Uma vez clicado no “+” para adicionar a regra, temos que configurá-la. Para isso, clique em “true” e, logo em seguida, no botão para editar a regra:
Na tela “Edit rule“, vamos escolher o preset “Normal” e clicar em “Done“:
Por fim, vamos até a aba “Protect“, clicamos no botão “Protect” e observamos o resultado na caixa de texto:
O resultado será gerado no subdiretório “Confused“. Ao decompilarmos o novo assembly, observe o resultado:
Agora realmente não dá para entender nada do código, não é mesmo?
Dica rápida: evitando que certos elementos sejam obfuscados
Em alguns cenários pode ser que queiramos eliminar alguma assembly, tipo ou membro da obfuscação. Um cenário típico que eu imagino que traga problemas são as classes de um modelo do Entity Framework. Nesse caso, provavelmente vamos querer que as classes e suas propriedades não sejam obfuscados.
Como é que podemos informar que não queremos que um elemento seja obfuscado? Simples, utilizamos o atributo ObfuscationAttribute indicando o valor “Exclude=true“:
// C# [System.Reflection.Obfuscation(Exclude=true)] public class ClasseQueNaoDeveSerObfuscada { }
' VB.NET <System.Reflection.Obfuscation(Exclude:=True)> Public Class ClasseQueNaoDeveSerObfuscada End Class
Obfuscar ou não? Vantagens e desvantagens
OK, agora que já vimos duas ferramentas que podemos utilizar para obfuscarmos o nosso código, vamos dar uma olhada nas vantagens e desvantagens de aplicarmos a obfuscação nos nossos assemblies? Primeiro vamos começar com as vantagens.
Vantagem: torna o código mais difícil de passar por engenharia reversa
A vantagem óbvia que obtemos ao aplicarmos alguma técnica de obfuscação nos nossos assemblies é que fica mais difícil de outras pessoas realizarem engenharia reversa neles. Mesmo que os assemblies sejam decompilados, será praticamente impossível entender o código da aplicação, uma vez que os nomes estarão todos trocados.
Porém, nem tudo são flores. Já existem ferramentas no mercado que fazem o processo inverso de obfuscação, ou seja, pegam um assembly obfuscado e tentam gerar o assembly original novamente. Não sei da eficácia dessas ferramentas, só sei que elas existem.
Por fim, a técnica de obfuscação só serve na realidade para dificultar a engenharia reversa. Para você entender o que estou querendo dizer utilizando exemplos do dia a dia, um alarme em um carro ou uma trava em uma bicicleta não impede que eles sejam roubados, impede? Não! Eles só dificultam o processo.
Desvantagem: stack trace impossível de ser interpretada (depende da ferramenta)
Imagine que uma exceção não tratada seja lançada a partir de um assembly obfuscado. Agora imagina só a stack trace que será gerada nessa exceção! Como os nomes dos métodos foram todos trocados, vai ficar bem difícil saber exatamente aonde o erro está acontecendo. É por isso que a maioria das ferramentas de obfuscação possui uma outra ferramenta adicional que faz a tradução da stack trace. Por exemplo, o ConfuserEx possui o “Stack Trace Decoder“:
Desvantagem: possível perda de performance
Algumas ferramentas de obfuscação utilizam metodologias que podem tornar o código mais lento de ser executado. Em alguns casos, loops ou até mesmo outros tipos de blocos de código são substituídos com “go-tos” desnecessários só para fazer com que o código fique incompreensível.
Desvantagem: impossibilita o uso de reflection e serialização
Se você utiliza reflection interna ou serialização no seu aplicativo, tome muito cuidado. Como o nome de tudo será trocado, possivelmente as áreas do seu código que utilizam essas técnicas deixarão de funcionar.
Desvantagem: dependendo do tipo de obfuscação, não dá para utilizar o assembly como referência em outros projetos
Se você utilizar uma metodologia de obfuscação pesada, onde tudo muda de nome (por exemplo, a obfuscação típica do ConfuserEx), não será possível utilizar o assembly como referência em outros projetos. Ou, melhor dizendo, você até poderá utilizá-lo, mas, os nomes das classes e métodos serão incompreensíveis, tornando o processo praticamente impossível.
A obfuscação do Dotfuscator que é distribuído gratuitamente em conjunto com o Visual Studio só altera os nomes de elementos privados, deixando a interface pública do assembly intocada. Dessa forma, você conseguiria utilizar esse tipo de assembly obfuscado como referência em outros projetos sem maiores problemas.
Concluindo
Nesse artigo você conferiu como funciona a obfuscação de assemblies .NET. Além disso, vimos como podemos obfuscar aplicações .NET utilizando o Dotfuscator (que tem uma versão gratuita disponibilizada junto com o Visual Studio) e o ConfuserEx (gratuito e open source). Por fim, você viu uma lista de vantagens e desvantagens de aplicarmos obfuscação nos nossos assemblies.
E qual é a minha opinião sobre esse tema? Devemos obfuscar os nossos assemblies ou não? Na minha opinião, quase sempre esse processo é totalmente desnecessário. Por mais que alguém consiga fazer a engenharia reversa da sua aplicação, ela precisará de um domínio grande da área de negócios do aplicativo para pelo menos tentar entender o código. A obfuscação só fará com que alguém mal-intencionado tenha mais dificuldades de conseguir o que está querendo. Porém, como você bem sabe, pessoas mal-intencionadas não se deixarão vencer somente porque o seu assembly está obfuscado.
Agora eu queria saber a sua opinião: o que você acha do processo de obfuscação? Vale a pena? A empresa onde você trabalha aplica a obfuscação nos aplicativos? Você já teve problemas com aplicativos obfuscados? Deixe os seus comentários logo abaixo.
Antes de me despedir, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado, ficará sabendo em primeira mão sobre o artigo da próxima semana e receberá também dicas “bônus” que eu só compartilho por e-mail. Além disso, você já deve ter percebido que eu recebo muitas sugestões de temas e eu costumo dar prioridade às sugestões vindas de inscritos da minha newsletter. Inscreva-se utilizando o formulário logo abaixo.
Até a próxima!
André Lima
Image by Pixabay used under Creative Commons
https://pixabay.com/en/padlocks-locks-for-bags-597815/
Trabalhando com o Crystal Reports no WPF Como distribuir aplicações com o Report Viewer?
Parabéns amigo, procurei isso por muito tempo com este passo a passo seu consegui,
mas fiz pelo aplicativo já montado, pois mesmo juntando as partes da outros erros.
Olá Charles, muito obrigado pelo comentário! Fico feliz que esse passo a passo tenha te ajudado no seu projeto.. :)
Abraço!
André Lima
[…] a aplicação e remover o código de proteção. Uns tempos atrás eu escrevi um artigo mostrando como podemos obfuscar as nossas aplicações .NET. O IntelliLock, porém, possui um sistema de obfuscação embutido na própria […]
Boa tarde André,
Interessante este post. Eu pensava em obfuscar uma pequena aplicação que tenho, mas vendo os prós e contras, já mudei de idéia.
Parabéns e forte abraço.
Jonas
Fala Jonas! Obrigado por mais este comentário!
Realmente existem muitas desvantagens quando obfuscamos as nossas aplicações.. Faz muito sentido em alguns cenários de aplicações comerciais amplamente utilizadas, mas para pequenas e médias aplicações, temos que pensar muito bem antes de nos aventurarmos nesse caminho..
Abraço!
André Lima
Bom dia Jonas, utilizo o ConfuserEx disponibilizado aqui pelo André em aplicações windows forms C#, e não notei nenhum problema com a performance de aplicação, estou muito satisfeito com os resultados do aplicativo.
Olá Charles!
Muito obrigado pelo comentário em resposta ao Jonas.. Esse tipo de interação é muito legal, fico feliz por você ter respondido com as suas experiências..
Charles, qual é mais ou menos o tamanho da sua aplicação? Suas soluções rodam em muitas estações? Você acha que tem gente tentando descompilar a sua aplicação e acha que vale a pena obfuscar?
Abraço!
André Lima
Boa tarde André, utilizo o ConfuserEx para ofuscar um software de ERP Comercial de quase 30MB roda em aproximadamente em 25 máquinas simultâneas, nossa metodologia prioriza o desenvolvimento de procedures, functions, e packages no banco de dados para os processamentos, deixando assim a aplicação apenas para controles visuais.
Ao descobrirmos que o .NEt deixa de maneira fácil a descompilação dos executáveis, tornou se urgente um maneira de proteger nosso produto.
Não percebemos nenhuma perca de performance no aplicativo e ainda ganhamos em segurança.
Olá Charles, obrigado pelo retorno!
Que bom que você conseguiu aplicar obfuscação na sua aplicação sem problema algum.. No nosso caso aqui, a aplicação é bem grande, com diversos projetos e fazemos uso de reflection em alguns pontos, o que inviabiliza a obfuscação (sem ter que ficar ajustando muita coisa).. Além disso, mesmo que alguém tenha o código da aplicação, o domínio é bem complicado, até nós mesmo do time de desenvolvimento temos dificuldade às vezes para entender a implementação de algumas funcionalidades..
Enfim, muito obrigado por acompanhar as publicações! Qualquer coisa, estamos aí.. :)
Abraço!
André Lima
Oi André
Sobre necessidade do mundo real de boa obfuscação: eu peguei um job na Workana, fazer uma rotina que aplica um efeito em uma imagem, e entregar o fonte. Não saberíamos se funcionaria até que o cliente aplicasse o efeito e imprimisse. Então eu fiz um demo rápido, ConfuserEx nele, nenhum dos 3 grandes decompilers comerciais conseguiu abrir, então enviei o .exe para o cliente sem medo nenhum. Rotina protegida, rodou aqui e no cliente, funcionalidade garantida, podemos continuar a negociação na Workana.
Mas ei que surgiu uma novidade no ConfuserEx: anti-vírus detectando falsos positivos! Mas, se fosse fácil, acho que a gente nem gostaria de programação, certo? =D Parece que a configuração anti-temper é a responsável pelo falso positivo. Aqui o Windows Defender acusou. Depois farei testes sem o anti-temper.
Qual repo você está usado, já que o original interrompeu as atividades?
Abraços.
Olá Marcio, obrigado pelo retorno!
Sem dúvida alguma, nesse cenário faz todo sentido obfuscar o código.. No nosso caso aqui na empresa, optamos por não aplicar obfuscação porque o código da aplicação é bem extenso, dividido em uma pancada de projetos e fazemos uso de reflection em algumas partes.. Tudo isso acaba inviabilizando.. Além de tudo isso, mesmo que alguém descompile a aplicação, é muito difícil de entender sem ser “de dentro” da empresa.. Até mesmo nós do time de desenvolvimento temos dificuldade de entender algumas funcionalidades que foram implementadas tempos atrás..
Quanto ao ConfuserEx, não tenho utilizado ele ultimamente (como te falei, aqui na empresa a gente não obfuscou a aplicação).. Eu utilizei ele no artigo porque trabalhei tempos atrás com o Confuser.. Quando escrevi o artigo, não tinha me tocado que ele tinha sido descontinuado no começo de 2016.. Uma pena.. Inclusive não tem nenhum fork que tenha commits nos últimos tempos.. O mais commitado foi o “ConfuserEx-Reborn”, mas o último commit é de meses atrás.. :(
Mas que coisa que ele está acusando falso positivo, hein.. Você já tentou aplicar em algum outro projeto com as mesmas opções? Ele sempre está dando falso positivo?
Enfim, se você tiver alguma novidade quanto a esse problema, avisa a gente aqui, OK?
Abraço!
André Lima
Que belo artigo, eu já tinha tentando algumas outras alternativas como o Obfuscar, todas funcionam bem, porém a praticidade do ConfuserEx é sem igual, bem prático, fora que é gratuito, eu desenvolvo um software ERP que roda em várias estações, não tive problemas de perfomance, a única “desvantagem” digamos assim, é a perda do suporte do CLICKONCE, que facilita muito a atualização da aplicação nas organizações, pois o programa atualiza automaticamente nas estações, seria interessante alguma abordagem nesse tipo. Parabéns novamente pelo artigo.
Olá Herbert! Muito obrigado pelo comentário! Nas próximas semanas eu quero escrever sobre plataformas de distribuição de aplicações que fazem a atualização automática (como ClickOnce).. Talvez outras ferramentas acabem solucionando esse problema que você teve com o ClickOnce ao ofuscar a sua aplicação.. Abraço!
Olá, muito bom o POST.
Sou iniciante em programação e não conhecia esse processo de decompilar.
Surgiu uma dúvida lendo a matéria, estou desenvolvendo um projeto que a string de conexão é declarada em uma classe, contendo o endereço do banco de dados, usuário e senha de conexão no mesmo, qse alguém decompilar meu aplicativo é possível que ela tenha acesso a essas informações?
Abraços.
Olá Cleber, obrigado pelo comentário!
Quanto à sua questão, sim, é possível recuperar esse tipo de informação se alguém decompilar o seu aplicativo.. É por isso que o local mais indicado para armazenar strings de conexão é em arquivos de configuração (separados da aplicação), de preferência utilizando algum tipo de criptografia..
Abraço!
André Lima