6 maneiras de fazer a mesma coisa, o que é considerado boas práticas?

As vezes tem tantas maneiras diferentes de fazer o mesmo código que nós ficamos na dúvida quanto a qual maneira usar. O que seria considerado "boa prática" pela comunidade e o que sua equipe entenderia melhor. Suponhamos que você esteja trabalhando dentro de um método de um Domain Service chamado UmDomainServiceChique(objetoDoDominio) que será chamado por uma API. Você tem uma regra de negócio chique para ser verificada que por enquanto chamarei de VerificaMinhaRegraChiqueComplexa(). Você chama UmDomainServiceChique(objetoDoDominio) e caso VerificaMinhaRegraChiqueComplexa() retorne true você vai querer que UmDomainServiceChique faça o que tem que fazer e a api retornar Ok 200, caso contrário você quer que a API responda um erro qualquer, tipo BadRequest, e retornar uma mensagem dizendo que VerificaMinhaRegraChiqueComplexa deu ruim. Eu vejo 6 maneiras de fazer isso, gostaria de saber a opinião de outrs devs sobre qual seria a maneira menos gambiarr

Recuperando classes, janelas e senhas com a API do Windows

Com as API's GetWindowText, GetClassName e o parâmetro WM_GETTEXT de SendMessage é possível obter os textos de qualquer janela da API do windows, bastando ter seu handle. O handle pode ser obtido com WindowFromPoint passando-se como parâmetro a posição do mouse.

Quando você está criando um componente, property editor, component editor ou expert para o Delphi as vezes é necessário saber qual a classe de um Property Editor já existente, por exemplo, para sabermos de onde poderemos herdar para criar o nosso.

Este programa serve para 2 objetivos principais:
1) Obter os handles, nomes e classes de todas as janelas, controles e widgets em que passamos o cursor do mouse.
É especialmente útil para sabermos o nome e classe de uma janela, classe, component editor, property editor ou expert do Delphi, principalmente quando não temos acesso ao fonte ou não conhecemos nada a respeito da mesma e precisamos saber em qual DCU ou BPL compilada ela se encontra (basta pesquisar pelo nome da classe no google que você encontra a unit onde ela está).

2) Revelar ou recuperar senhas perdidas. É importante notar que esse método só recupera senhas que estão mascaradas com asteriscos ("*") mas não recupera senhas mascaradas com a bolinha preta, predominante do windows XP pra frente, graças às novas API's.

Este não é nenhum segredo hacker, na verdade esta API, e o método para obter as senhas, existem desde os primórdios do Windows, e foram muito difundidas e utilizadas até a época do Windows 98.

Onde a recuperação de senhas vai funcionar:

Todos os programas feitos em Delphi (qualquer versão) em que as senhas sejam digitadas em um Edit e nos programas feitos em VB6. Um programa que funcionará também é o Outlook 2007 (do office 2007). Vá no painel de controle e escolha alguma conta de e-mail pop3/smtp com a senha salva. Esta senha é possível de ser revelada facilmente. Não testei as outras versões.

No momento eu não sei como fazer para recuperar as senhas contidas naqueles campos apropriados para senhas (o dos bullets pretos). Se alguém quiser colaborar com essa dica por favor comente.

O sucesso na revelação da senha depende de como o edit e sua classe foram registrados, e com que parâmetros. Em muitos sistemas seria possível obter a senha apenas obtendo-se o "caption" com GetWindowText do controle em questão, já que ele é uma "janela". Em outros o controle deve estar em foco, ou deve ser clicado.

Um FAIL gigante nesse aspecto é que o Visual Studio .Net e o .Net Framework te proporcionam a possibilidade de usar o passwordchar do sistema, ou seja, os Bullets pretos, mas mesmo assim a senha é perfeitamente revelável.

Outra coisa interessante é que o edit do lazarus não revela a senha pelo "SendMessage(hWnd, WM_GETTEXT" e nem permite que o passwordchar seja modificado, mas mesmo assim revela a senha com GetWindowText.

Crie um formulário como o da figura e coloque um timer. A maior parte do código vai nesse timer.


Para usarmos a API do windows para obter respostas textuais temos 3 opções:
1 - Usar vetores de char fixos em x caracteres (usaremos 255)
2 - Usar strings normais, porém pré-setando seu tamanho com setLength para um tamanho obtido com a mensagem WM_GETTEXTLENGTH.
3 - Utilizando ponteiros para chars (pchars) não esquecendo de alocar e desalocar memória para os mesmos.


Usando vetores
unit pegatudo;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, shellapi;

type
  TfrmPegatudo = class(TForm)
    Label2: TLabel;
    lbclasse: TLabel;
    tempo: TTimer;
    lbHandle: TLabel;
    Label4: TLabel;
    lbJanela: TLabel;
    Label5: TLabel;
    lbSenha: TLabel;
    Label7: TLabel;
    procedure tempoTimer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmPegatudo: TfrmPegatudo;

implementation

{$R *.dfm}

procedure TfrmPegatudo.tempoTimer(Sender: TObject);
var
  hWnd: THandle;
  CrPos: TPoint;
  nomejanela,
  nomeclasse,
  senha: array[0..255] of Char;
begin
  try
    GetCursorPos(CrPos); // Identifica a posicao do mouse
    hWnd := WindowFromPoint(CrPos); // Pega o Handle do controle na posição do mouse

    if hwnd <> 0 then
    begin

      //preenche o conteúdo do vetor com char zeros
      FillChar(nomejanela, 255, #0);
      FillChar(nomeclasse, 255, #0);

      //pega o nome da janela
      GetWindowText(hWnd, NomeJanela, 255);
      //pega o nome da classe
      GetClassName(hWnd, nomeclasse, 255);

      //manda uma mensagem para o handle da janela
      //perguntando se ele possui um password char
      if SendMessage(hWnd, EM_GETPASSWORDCHAR, 0, 0) <> 0 then
      begin
        FillChar(senha, 255, #0);
        //Caso positivo manda a mensagem WM_GETTEXT
        SendMessage(hWnd, WM_GETTEXT, 255, integer(@senha)); //o integer aqui faz com que o parâmetro seja passado como ponteiro em vez de vetor
        lbSenha.Caption :=      string(senha);
        //Caso positivo seta o passwordchar para #0
        //o problema de fazer isso é que vai entrar uma vez só no if
        SendMessage(hWnd, EM_SETPASSWORDCHAR, 0, 0);
      end;

      //preenche os campos
      lbHandle.Caption :=     IntToStr(hwnd);
      lbjanela.Caption :=     string(nomejanela);
      lbclasse.Caption :=     string(nomeclasse);
    end;

  except
  end;

end;

end.




Usando Strings
unit pegatudo;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, shellapi;

type
  TfrmPegatudo = class(TForm)
    Label2: TLabel;
    lbclasse: TLabel;
    tempo: TTimer;
    lbHandle: TLabel;
    Label4: TLabel;
    lbJanela: TLabel;
    Label5: TLabel;
    lbSenha: TLabel;
    Label7: TLabel;
    procedure tempoTimer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmPegatudo: TfrmPegatudo;

implementation

{$R *.dfm}

procedure TfrmPegatudo.tempoTimer(Sender: TObject);
var
  hWnd: THandle;
  CrPos: TPoint;
  nomejanela,
  nomeclasse,
  senha: string;
  tamJanela,
  tamClasse,
  tamSenha: integer;

begin
  try
    GetCursorPos(CrPos); // Identifica a posicao do mouse
    hWnd := WindowFromPoint(CrPos); // Pega o Handle do controle na posição do mouse

    if hwnd <> 0 then
    begin

      //captura o tamanho dos elementos
      tamJanela := GetWindowTextLength(hwnd) + SizeOf(Char);
      tamClasse := 256;  //o resultado de GetClassName traz um integer com
      //o número de caracteres copiados. Se for maior que 255 a string ou
      //ponteiro pode ser realocado/redimensionado com setsize/malloc e
      //pode ser usado GetClassName novamente para pegar a string completa
      //caso ela tenha vindo truncada.
      //o mesmo pode ser feito com GetWindowText, mas nesse caso a
      //GetWindowTextLength nos ajuda a obter esse tamanho
      //só é trazido o número de caracteres sem contar o terminator null #0

      SetLength(nomejanela, tamJanela);
      SetLength(nomeclasse, tamClasse);

      //preenche o conteúdo do vetor com char zeros
      FillChar((@nomejanela[1])^, tamJanela, #0);
      FillChar((@nomeclasse[1])^, tamClasse, #0);



      //pega o nome da janela
      GetWindowText(hWnd, pchar(integer(NomeJanela)), tamJanela);
      //pega o nome da classe
      GetClassName(hWnd, pchar(integer(nomeclasse)), tamClasse);

      //manda uma mensagem para o handle da janela
      //perguntando se ele possui um password char
      if SendMessage(hWnd, EM_GETPASSWORDCHAR, 0, 0) <> 0 then
      begin

        tamSenha := SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)+ SizeOf(Char);
        SetLength(senha, tamSenha);
        FillChar((@senha[1])^, tamSenha, #0);
        //Caso positivo manda a mensagem WM_GETTEXT
        SendMessage(hWnd, WM_GETTEXT, tamSenha, integer(@senha[1])); //o integer aqui faz com que o parâmetro seja passado como ponteiro em vez de vetor
        lbSenha.Caption :=      senha;

        //seta o passwordchar para #0 e
        SendMessage(hWnd, EM_SETPASSWORDCHAR, 0, 0);

      end;

      //preenche os campos
      lbHandle.Caption :=     IntToStr(hwnd);
      lbjanela.Caption :=     nomejanela;
      lbclasse.Caption :=     nomeclasse;


    end;

  except
  end;

end;

end.



Veja que SendMessage(hWnd, EM_SETPASSWORDCHAR, 0, 0); faz com que o edit que era do tipo password deixe de ser, e o password aparecerá desmascarado no edit.

As duas versões do código, bem como programinhas cobaia em Delphi, C# e Lazarus podem ser baixados aqui ou na minha página de download de exemplos.

Have Fun ;)

Comentários

  1. Ola obtenho o seguinte erro:

    PegaSenhaLaz.lpr(16,4) Fatal: Cannot open include file "PegaSenhaLaz.lrs"

    Está faltando esse arquivo no projeto?

    ResponderExcluir
    Respostas
    1. Esse código foi escrito em 2011, para windows XP. Era outra versão do Lazarus também. Provavelmente não funciona hoje em dia com o windows moderno. Mas se quiser tentar, crie um projeto do zero e cole esse código em um projeto novo, em vez de baixar esses sources.

      Excluir

Postar um comentário

Postagens mais visitadas deste blog

Busca de CEP com o Lazarus - Parte 1 - UrlEncode

Botão Add This para adicionar seu post em qualquer rede

Uso de memória no SQL Server