1. Introdução
Esta publicação tem como objetivo estudar algumas técnicas de engenharia reversa. Todos os materiais são apresentados apenas para fins informativos e não se destinam a ser utilizados para qualquer ganho pessoal.Leitura recomendada após a
primeira parteSe um cirurgião é ensinado como uma pessoa trabalha e lhe dá um bisturi, isso não significa que ele usará esse conhecimento em detrimento de alguém, e um montador experiente não sonha em escrever um super vírus.
Portanto, nessas lições, você não deve procurar dicas de rachaduras e hacks.
Assunto da pesquisa
Continuamos estudando o código de plug-in da documentação do Visual Studio Atomineer Pro (doravante APD). Vamos nos familiarizar com a ferramenta e seus recursos.
Então, suponha que tenhamos uma classe em C ++.
class ClassForReadFile { public: ClassForReadFile(); };
configure o APD para que os comentários estejam no estilo Doxygen. Pegamos o cursor na
classe e pressione
CTRL + SHIFT + D. Temos o seguinte:
class ClassForReadFile { public: ClassForReadFile(); };
O plugin adicionou uma boa descrição da classe. Está tudo ótimo! Nós seguimos em frente. Suponha que uma classe pertença a uma biblioteca e devemos exportá-la. Adicione uma macro e altere a definição da classe
#ifdef DLL_EXPORTS #define DATA_READER_DLL_EXPORTS __declspec(dllexport) #else #define DATA_READER_DLL_EXPORTS __declspec(dllimport) #endif class DATA_READER_DLL_EXPORTS ClassForReadFile { public: ClassForReadFile(); };
Para C ++ (Windows OS), a situação é padrão. Confira nosso plugin. Pressione
CTRL + SHIFT + D e não obtenha o que esperávamos
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
o nome da
definição DATA_READER_DLL_EXPORTS
foi definido como o nome da classe, em vez de
ClassForReadFile , e a descrição da classe foi gerada a partir desse nome. Ou seja, no código do plug-in, essa situação, ou seja, a exportação da classe, não é processada ou está sendo processada com um erro. É isso que tentaremos corrigir.
Etapa 1
Vamos procurar pistas. Primeiramente, como a exportação de funções e classes do C / C ++ é uma situação padrão, ainda tentamos "forçar" o plug-in corretamente. No lugar da
definição DATA_READER_DLL_EXPORTS
, insira a
própria instrução
__declspec e gere a documentação
class __declspec(dllexport) ClassForReadFile { };
E eis que eles obtiveram a descrição correta da classe! Assim, concluímos que no APD há algum código que verifica a presença da string "__declspec" na descrição da classe e ignora seu algoritmo adicional para a construção de documentação.
Descompilamos a biblioteca com o ildasm.exe padrão dos Microsoft SDKs. Encontre a linha "__declspec". Pode ser encontrada nos 2 métodos CmdDocComment :: a e CmdDocComment :: b. Classe um. Vamos sujeitá-lo a um estudo mais aprofundado.
Etapa 2
Devo dizer imediatamente que o que estamos procurando está no método CmdDocComment :: a
Aqui é onde
__declspec se encontra. Somente as linhas mais interessantes são mostradas.
string a (CmdDocComment.GeneratorInfo A_0) Concluímos isso com base no fato de que, após verificação
lista [num3] == "__declspec"
O método delete é chamado
list.RemoveAt (num3);
Raciocínio (pensando alto):
- O método CmdDocComment :: a tem uma variável local que contém uma matriz de seqüências de caracteres
List<string> list = A_0.e;
- O primeiro elemento dessa matriz armazena o início da descrição da função, estrutura, classe, etc., ou seja, a palavra-chave "class", "struct", "union"
list[0] == "struct"
- Cada elemento da matriz contém uma palavra separada. No nosso caso, será {"class", "DATA_READER_DLL_EXPORTS", "ClassForReadFile"}
- Há um loop que percorre todos os elementos da matriz "e", procura o elemento "__declspec" e o remove e tudo o que está entre colchetes
- Há uma condição adicional para sair do ciclo. Esta é a localização das palavras "onde" ou ":". A palavra oficial "onde" é, para ser sincero, não familiarizada, mas ":" é usada ao herdar classes
Defina um novo algoritmo e o objetivo das alterações:
1. as alterações não devem afetar o restante da funcionalidade
2. excluiremos os elementos da matriz "list" de acordo com o algoritmo
- pule o primeiro elemento;
- se o próximo elemento não for ":" e não "where" e não o final da matriz, exclua.
Escreva o ciclo desejado
Resta programá-lo.
Etapa 3
Programe em voz alta. A programação no caso geral é a escrita de códigos-fonte, compilação e vinculação. Mas o ofuscador nos privou dessa oportunidade. Usaremos a
ferramenta dnSpy recomendada. Mudaremos os códigos HEX dos comandos CIL diretamente na biblioteca, o que, como se viu, é muito emocionante e informativo! Vamos começar. Abra o dnSpy, carregue a biblioteca.
selecione while e altere a visualização para IL
Também darei uma lista, embora seja bastante volumosa
Agora temos uma janela nos comandos CIL, sua representação HEX, deslocamento do arquivo e descrição. Tudo em um só lugar. Muito conveniente (obrigado
CrazyAlex25 ).
Vamos prestar atenção ao bloco que menciona "__declspec". Deslocamento de bloco 0x0001675A. Este será o começo de nossas edições. Em seguida, encontre o método RemoveAt. Será útil para nós inalterado. O último byte do bloco é 0x000167BF. Vá para o editor HEX
Ctrl + X e escreva 0x00 neste intervalo. Vamos salvar e verificar o que as alterações levaram.
laço vazio while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; }
Agora vamos implementar a nova lógica. Primeiro, adicione a condição
if (num3 != 0 && num3 < list.Count - 1)
A tabela mostra os novos comandos e sua descrição.
1119 | ldloc.s | Carrega uma variável local com o índice especificado na pilha de computação (formato abreviado). |
---|
2C61 | brfalse.s | Passa o controle para a instrução final se o valor for falso, uma referência nula ou zero. Nota : Se num3 == 0, vá para a etapa de aumentar o iterador do loop. O valor 0x64 é o deslocamento do endereço para a instrução 0x000167BF (consulte a listagem) |
---|
1119 | ldloc.s | Carrega uma variável local com o índice especificado na pilha de cálculo (formato abreviado) |
---|
07 | ldloc.1 | Carrega uma variável local com índice 1 na pilha de computação |
---|
6FF700000A | callvirt | get_Count () - chama um método de objeto com limite tardio e envia o valor de retorno para a pilha de cálculo |
---|
17 | ldc.i4.1 | Empurra o valor inteiro 1 na pilha de computação como int32 |
---|
59. | sub | Subtrai um valor de outro e empurra o resultado para a pilha de cálculo. |
---|
2F55 | bge.s | Transfere o controle para a instrução final (formato abreviado) se o primeiro valor for maior ou igual ao segundo. Nota : Se num3> list.Count - 1, vá para a etapa de aumentar o iterador do loop. O valor 0x55 é o deslocamento do endereço antes da instrução 0x000167BF |
---|
Escrevemos esses bytes começando no deslocamento 0x0001675A. Salve e descompile novamente
Primeira condição while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; }
Agora, adicionamos a verificação de cadeia "where" e ":". Cito o seguinte código HEX sem comentários adicionais:
07 11 19 17 58 6F F9 00 00 0A 72 A3 1D 00 70 28 70 00 00 0A 2D 3F 07 11 19 17 58 6F F9 00 00 0A 72 92 5E 00 70 28 70 00 00 0A 2D 29
descompilar e obter o que você planejou
Novo ciclo while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (num3 != 0 && num3 < list.Count - 1 && !(list[num3 + 1] == ":") && !(list[num3 + 1] == "where")) { list.RemoveAt(num3); num3--; } num3++; }
Com essas alterações, o plug-in irá gerar a seguinte documentação do código:
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
Conclusão
Nesta lição, aprendemos como aplicar nosso conhecimento para corrigir bugs. Obviamente, este exemplo não reflete toda a variedade de erros e seu tratamento, mas isso não é um "crack banal". Corrigimos o bug óbvio sem ter o código fonte e sem reconstruir o aplicativo.