quinta-feira, 10 de outubro de 2019

[Hack] - Delphi Method Hijack

Algumas vezes nos deparamos com erros relacionados às bibliotecas internas do Delphi. Acontece também com bibliotecas de terceiros. Geralmente o código fonte não está disponível ou a correção implica na recompilação dos fontes, o que inviabiliza a prática. Estes problemas nos deixam dependentes da Q.C. responsável pelo produto para a correção dos problemas. Muitas vezes as releases de correção são demoradas. Há outras situações em que só queremos mudar o comportamento padrão de um método.

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!

sexta-feira, 21 de junho de 2019

Thread Pooling Executor - Parte 2

Baseado no Thread Pooling Executor do JAVA, criei algo semelhante usando Delphi 2006.
Nesta segunda parte, possível acompanhar o status de execução da tarefa, assim como aguardar seu resultado ou cancelá-la.


Segue link do projeto: https://drive.google.com/open?id=1gD4Q1-ppWdiU_oo9c-MxChG-Dda3DbGI

DataSet Helper

Exemplo de como usar variants para iterar um DataSet e simplificar o acesso aos campos. Cada registro do DataSet é acessado por uma variant, promovendo acesso aos campos através do acesso direto a propriedades nomeadas, técnica conhecida como "late-binding". O acesso ao registro do DataSet através de uma variant é feito por um "class helper".

Operações em variants são mais lentas do que em tipos com ligações estáticas, além de consumir mais memória, então deve-se garantir que o cenário da sua aplicação não seja afetado pelo baixo desempenho.

O código fonte está disponível no link: https://drive.google.com/open?id=10muAtIcCyR0Y4i3T3c25r-XaFeTi8QIx

Ex:

procedure TDatSSample.Sample;
var
  LDatS: TDataSet;
  LRec: variant;
  LId: integer;
  LName: string;
  LAge: integer; 
begin
  [...]
  for LRec in LDatsS do begin
    LId := LRec.ID;
    LName := LRec.NAME;
    LAge := LRec.AGE;
    WriteLn(Format('ID: %d Nome: %s Idade: %d', [LId, LName, LAge]));
  end;
end;

sexta-feira, 5 de abril de 2019

ObjectPool

Galera, implementei uma forma de "ObjectPool" no Delphi 2006.
O controle dos recursos é retomado ao Pool através da análise de contexto ativo - ARC. Quem pegou o controle do recurso por último o mantém até que a variável que a referencia perca o escopo, eliminando a responsabilidade de "devolução" e evitando a retenção indevida do recurso.
A estrutura permite outras abordagens que podem ser estendidas através da arquitetura existente.

O projeto, com exemplo, está disponível no link a seguir: PooledObjects

sexta-feira, 22 de março de 2019

Thread Pooling Executor - Parte 1

Baseado no Thread Pooling Executor do JAVA, criei algo semelhante usando Delphi 2006.
Nesta primeira parte, já é possível usar os recursos de execuções concorrentes, mas a forma de acompanhar a execução das tarefas é apenas pelos eventos de execução.

Na próxima parte, adicionarei a opção de acompanhar o status das execuções.

Segue link do projeto: https://drive.google.com/open?id=1GirRolPKa66Q13BheNBlPQlv6EU62g5R

sexta-feira, 1 de fevereiro de 2019

Multicast events

Seguindo o raciocínio do Allen Bauer em MulticastEvents using generics, implementei uma solução para versões do Delphi onde generics não estão disponíveis e os recursos de rtti são limitados. A utilização é relativamente igual, não contando apenas com alguns "açucares de sintaxe".
Para versões do Delphi que não contam com o invocador de eventos "TDynamicInvokeEvent" localizado na unit ObjAuto.pas, deixarei disponível a ObjAutoX.pas. Caso contrário, substitua o uses da unit MuticastEvent.pas.
Diferente do exemplo feito por Allen Bauer, esta implementação se faz indiferente de opções de otimização e de geração de stack frames, mas também conta com algum inline assembly.
O multicast serve como uma lista de ações que devem acontecer quando um evento é executado. Basicamente substitui-se a ação original do evento pelo "Invoke" do multicast, que fica responsável pela delegação das ações.
Implementei duas formas de invocar as ações, via "EventDispatcher" e "MethodDispatcher". Estes se diferenciam pela forma que passam os parâmetros para as ações delegadas.
Sabendo que variants são menos performáticas, o "EventDispatcher" faz a passagem direta dos parâmetros via registradores e a stack, limitando-se a convenção de chamada "register". Mas, comumente não criamos eventos com outras convenções de chamada, então não deve ser problema.
O "MethodDispacher" cria variants contendo os parâmetros passados na invocação do evento, e as ações devem ser chamadas e os parâmetros passados via reflexão. Por não achar necessário, não implementei esse recurso, mas deixei-o como opcional para extensão.
Deixarei o projeto, com exemplos, disponível no link: Multicast Events