Por isso resolvi neste post mostrar como alterar métodos (independente dos modificadores de acesso e.g. public, private...) em tempo de execução.
O que fazemos é alterar os endereços dos métodos na página virtual do processo. Primeiro descobrimos o endereço do método de origem e do destino, pedimos permissão para o SO para sobrescrever a memória virtual do processo e em seguida embutimos o novo método. Por fim, executamos um flush na memória para atualizar o endereço.
O Delphi fornece recursos para localizar o endereço de memória dos métodos, mas estes recursos não acessam métodos privados localizados em units externas. Isso não impossibilita nossa prática, mas requer um pouco mais de trabalho.
Obter endereços de métodos globais é fácil, basta usar o símbolo @ antes do método ou a função Addr(). Para métodos de objetos também é possível usar o símbolo @, basta classificar o método com o nome da classe: @TMinhaClasse.MeuMetodo, ou então através da estrutura TMethod.
Obter o endereço de métodos privados requer um pouco mais de esforço. Primeiro precisamos identificar um método público que utiliza o método privado em questão. Em seguida descobrimos o código de máquina originado para a chamada do método privado. Esse código de máquina servirá como base para a localização do endereço do método privado. Por exemplo, para aplicarmos o hack no método UpdateShowing da classe TWinControl, podemos usar o método público UpdateControlState para localizar o código de máquina gerado para a chamada do método UpdateShowing. A image a seguir mostra como fazer isso.
Indo ao que interessa, o que de fato fazemos e incluir após o prolog do método original um jump para o novo método. O primeiro byte da chamada é sobrescrita com a instrução jump com um offset para o endereço do novo método. Isso faz com que o método original continue sendo chamado, porém seu código não será executado. Além do mais, a epilog do método original servirá como retorno para o ponto de origem da chamada do método. Veja o exemplo a seguir:
TMeuForm = class(TForm)
private
procedure MyMethod;
procedure MyMethodHijacker;
public
procedure ExecuteMethod;
end;
O método público ExecuteMethod faz uma chamada para o método privado MyMethod. Vamos alterar o método privado MyMethod para o método MyMethodHijacker através do código injetor:
TInjector.Create(@TMeuForm.MyMethod, @TMeuForm.MyMethodHijacker);
O jump feito pelo método MyMethod para o método MyMethodHijacker fica visível na imagem a seguir:
Assim mudamos o comportamento do método MyMethod para MyMethodHijacker. Vale ressaltar que a alteração pode ser desfeita.
* Antes de executar o hijack do exemplo no método UpdateShowing, verifique o código de máquina gerado pelo seu compilador, logo que pode mudar entre versões do código.
Código fonte disponível no GitHub.
https://github.com/lmbelo/DelphiMethodHijack.git
Até mais!
Nenhum comentário:
Postar um comentário