Como inserir parâmetros em métodos sem parâmetros no código seguro

Olá Desta vez, continuamos a rir da chamada de método normal. Proponho me familiarizar com a chamada de método com parâmetros sem passar parâmetros. Também tentaremos converter o tipo de referência em um número - seu endereço, sem usar ponteiros e códigos inseguros .


Isenção de responsabilidade


Antes de prosseguir com a história, recomendo fortemente que você leia o post anterior sobre o StructLayout . Aqui vou usar alguns recursos, que foram descritos lá.

Também gostaria de avisar que este artigo não contém material que deve ser usado em projetos reais.

Algumas informações iniciais


Antes de começarmos a praticar, vamos lembrar como o código C # é convertido em código assembler.
Vamos examinar um exemplo simples.

public class Helper { public virtual void Foo(int param) { } } public class Program { public void Main() { Helper helper = new Helper(); var param = 5; helper.Foo(param); } } 

Este código não contém nada difícil, mas as instruções geradas pelo JiT contêm vários pontos-chave. Proponho olhar apenas em um pequeno fragmento do código gerado. nos meus exemplos, usarei o código assembler para máquinas de 32 bits.

 1: mov dword [ebp-0x8], 0x5 2: mov ecx, [ebp-0xc] 3: mov edx, [ebp-0x8] 4: mov eax, [ecx] 5: mov eax, [eax+0x28] 6: call dword [eax+0x10] 

Neste pequeno exemplo, você pode observar a convenção de chamada de chamada rápida que usa registradores para passar parâmetros (os dois primeiros parâmetros da esquerda para a direita nos registros ecx e edx) e os parâmetros restantes são passados ​​pela pilha da direita para a esquerda. O primeiro parâmetro (implícito) é o endereço da instância da classe na qual o método é chamado (para métodos não estáticos).

No nosso caso, o primeiro parâmetro é o endereço da instância, o segundo é o nosso valor int .

Então, na primeira linha que vemos a variável local 5, não há nada interessante aqui.
Na segunda linha, copiamos o endereço da instância Helper no registro ecx. Este é o endereço do ponteiro para a tabela de métodos.
Na terceira linha, há cópia da variável local 5 no registro edx
Na quarta linha, podemos ver a cópia do endereço da tabela de métodos no registro eax
A quinta linha contém o carregamento do valor da memória no endereço 40 bytes maior que o endereço da tabela de métodos: o início dos endereços dos métodos na tabela de métodos. (A tabela de métodos contém várias informações armazenadas anteriormente. Por exemplo, endereço da tabela de métodos da classe base, endereço da classe EEC, vários sinalizadores, incluindo o sinalizador de coletor de lixo e assim por diante). Portanto, o endereço do primeiro método da tabela de métodos agora é armazenado no registro eax.
Nota: No .NET Core, o layout da tabela de métodos foi alterado. Agora, existe um campo (com deslocamento de 32/64 bits para sistemas de 32 e 64 bits, respectivamente) que contém o endereço do início da lista de métodos.
Na sexta linha, o método é chamado no deslocamento 16 desde o início, ou seja, o quinto na tabela de métodos. Por que nosso único método é o quinto? Lembro que o objeto possui 4 métodos virtuais ( ToString (), Equals (), GetHashCode () e Finalize () ), que todas as classes terão, respectivamente.

Goto Practice;


Practive:
É hora de começar uma pequena demonstração. Sugiro um espaço em branco tão pequeno (muito semelhante ao espaço em branco do artigo anterior).

  [StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public Test1 Test1; [FieldOffset(0)] public Test2 Test2; } public class Test1 { public virtual int Useless(int param) { Console.WriteLine(param); return param; } } public class Test2 { public virtual int Useless() { return 888; } } public class Stub { public void Foo(int stub) { } } 

E vamos usar essas coisas dessa maneira:

  class Program { static void Main(string[] args) { Test2 fake = new CustomStructWithLayout { Test2 = new Test2(), Test1 = new Test1() }.Test2; Stub bar = new Stub(); int param = 55555; bar.Foo(param); fake.Useless(); Console.Read(); } } 

Como você pode imaginar, a partir da experiência do artigo anterior, o método Useless (int j) do tipo Test1 será chamado.

Mas o que será exibido? O leitor atento, acredito, já respondeu a essa pergunta. "55555" é exibido no console.

Mas vamos ver os fragmentos de código gerados.

  mov ecx, [ebp-0x20] mov edx, [ebp-0x10] cmp [ecx], ecx call Stub.Foo(Int32) mov ecx, [ebp-0x1c] mov eax, [ecx] mov eax, [eax+0x28] call dword [eax+0x10] 

Eu acho que você reconhece o padrão de chamada do método virtual, ele inicia após a chamada do Stub.Foo (Int32) . Como podemos ver, como esperado, o ecx é preenchido com o endereço da instância na qual o método é chamado. Mas como o compilador pensa que chamamos um método do tipo Test2, que não possui parâmetros, nada é gravado no edx. No entanto, temos outra chamada de método antes. E lá usamos o edx para passar o parâmetro. E é claro que não temos instruções, que edx claro. Portanto, como você pode ver na saída do console, o valor edx anterior foi usado.

Há outra nuance interessante. Eu usei especificamente o tipo significativo. Sugiro tentar substituir o tipo de parâmetro do método Foo do tipo Stub por qualquer tipo de referência, por exemplo, uma string. Mas o tipo de parâmetro do método Useless () não muda. Abaixo você pode ver o resultado na minha máquina com algumas informações esclarecedoras: WinDBG e Calculator :)


Imagem clicável

A janela de saída exibe o endereço do tipo de referência em notação decimal.

Total


Atualizamos o conhecimento de métodos de chamada usando a convenção de chamada rápida e imediatamente usamos o maravilhoso registro edx para passar um parâmetro em 2 métodos por vez. Também cuspimos em todos os tipos e, com o conhecimento de que tudo são apenas bytes, obtivemos facilmente o endereço do objeto sem usar ponteiros e códigos inseguros. Além disso, pretendo usar o endereço recebido para propósitos ainda mais inaplicáveis!

Obrigado pela atenção!

O código PS C # pode ser encontrado aqui

Source: https://habr.com/ru/post/pt447254/


All Articles