Oficina
Bloco de Notas com linhas numeradas
Seg 28 Mai 2007 20:28 |
- Detalhes
- Categoria: Oficina de Assembly
- Atualização: Quinta, 18 Junho 2009 11:44
- Autor: vovó Vicki
- Acessos: 23201
Inserindo o código de impressão
Já identificamos um espaço ocioso no executável onde é possível incluir o código, já sabemos como calcular a distância de saltos e onde inseri-los, então... vamos ao trabalho.
- Inserindo o segundo salto
Conforme já visto, decidimos fazer um desvio no início do procedimento WndProc. Reveja o trecho de código citado:
Portanto, faremos um salto incondicional - jmp do endereço 401C8D para um endereço após o final do nosso primeiro bloco de código. Deixamos alguns bytes zerados e iniciamos o segundo bloco de código em 40DACD (o primeiro bloco terminou em 40DABF). O salto, então, será de BE3B bytes (40DAC8 - 401C8D - 5 = BE3B).
De posse deste valor, vamos compor a instrução, lembrando novamente que a distância do salto é indicada em 4 bytes na ordem inversa, ou seja, 00 00 BE 3B será indicado como 3B BE 00 00
O programa original deve sofrer as seguintes alterações para a introdução do salto:
Endereço | Código original | Alterar para |
00401C8D | 8BEC MOV EBP,ESP | E9 3B |
00401C8F | 56 PUSH ESI | BE |
00401C90 | 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C] | 00 00 90 |
00401C93 | 83FE CMP ESI,5 |
Observe que ocupamos apenas os dois primeiros bytes (8B e 75) da instrução no endereço 401C90 para poder completar a instrução de salto. Restou o código 0C, o qual foi anulado com uma instrução NOP (90) para manter íntegro o tamanho do código original e não desestruturar outros endereçamentos existentes. Com o salto devidamente inserido, este bloco de código passa a ser:
Endereço | Salto adicionado | |
00401C8D | E9 3EBE0000 | JMP NOTEPAD.0040DACD |
00401C92 | 90 | NOP |
00401C93 | 83FE | CMP ESI,5 |
- Adicionando o código da impressão dos números de linha
Quando alcançar a linha 401C8D, o salto é efetuado e a execução do código continua no endereço 40DACD, exatamente onde vamos inserir o bloco 2 do código adicional. Nele, antes de qualquer outra coisa, vamos refazer as operações suprimidas pela introdução do salto incondicional repetindo o código original:
Endereço | Hexa | Operação | Observações |
0040DACD | 8BEC | MOV EBP,ESP | |
0040DACF | 56 | PUSH ESI | |
0040DAD0 | 8B75 0C | MOV ESI,DWORD PTR SS:[EBP+C] | ; ESI contém a mensagem |
A seguir preparamos os parâmetros para a função SendMessageA da API do Windows e a chamamos. Lembre-se de que os parâmetros precisam ser colocados na pilha na ordem inversa (para que a função possa acessá-los na ordem correta). O endereço da função pode ser obtido através do PE Explorer, na janela "Imports", sob a rubrica USER32:
004064E4 function SendMessageA( hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; external 'user32.dll' name 'SendMessageA' index 537
Endereço | Hexa | Operação | Observações |
0040DAD3 | 6A 00 | PUSH 0 | ; lParam |
0040DAD5 | 6A FF | PUSH -1 | ; wParam |
0040DAD7 | 68 C9000000 | PUSH C9 | ; Mensagem |
0040DADC | FF35 04504000 | PUSH DWORD PTR DS:[405004] | ; Handle da janela filha "edit" |
0040DADE | FF15 E4644000 | CALL DWORD PTR DS:[<&USER32.SendMessage>] | ; Após esta chamada, eax contém a posição Y do cursor |
No retorno da função SendMessageA, o registrador EAX contém a posição Y do cursor na janela-filha "edit", ou seja, a linha atual da janela. Como já designamos o local de armazenamento do valor da variável dwLinha (40DB78), podemos comparar o valor de ambos. Se houver diferença, significa que o cursor mudou de linha e o rodapé precisa ser atualizado. Se ambos forem iguais, podemos voltar ao código original.
Endereço | Hexa | Operação | Observações |
0040DAE8 | 3B05 78DB4000 | CMP EAX,DWORD PTR DS:[40DB78] | ; Posição do cursor mudou? |
0040DAEE | 75 05 | JNZ SHORT 0040DAF5 | ; Sim, imprimir novo número de linha |
0040DAF0 | 83FE 0F | CMP ESI,0F | ; A mensagem atual é WM_PAINT? |
0040DAF3 | 75 64 | JNZ SHORT 0040DB59 | ; Não, vá para o fim da rotina e retorne para WndProc |
Caso o cursor tenha trocado de linha, algumas medidas precisam ser tomadas: atualizar a variável dwLinha em 40DB78 com o novo valor, somar 1 ao número da linha que deve ser impresso porque as linhas iniciam sua numeração em 0 (linha 0 - imprimimos linha 1, linha 1 - imprimimos linha 2, etc) e fazer uma chamada à função wsprintfA para "montar" a string que deve aparecer no rodapé:
0040641C function wsprintf( Output: PAnsiChar; Format: PAnsiChar): Integer; stdcall; external 'user32.dll' name 'wsprintfA' index 692;
Esta função recebe o valor que deve ser "stringado" (o número ajustado da linha), a string "%#05d" que corresponde ao formato de número precedido de zeros em 5 casas (1 será 00001) e o endereço onde deve ser armazenado o resultado (40DB90 + 0A = 40DB9A). O endereço da nossa "variável" para a string do rodapé é 40DB90. Os caracteres " Linha : " ocupam 10 posições, portanto, queremos que, a partir da 11a. posição seja armazenado o número transformado em ASCII no formato indicado pela string de formatação:
Endereço | Hexa | Operação | Observações |
0040DAF5 | A3 78DB4000 | MOV DWPRD PTR DS:[40DB78],EAX | ; Linha mudou, guarde o novo valor |
0040DAFA | 40 | INC EAX | ; Ajusta número de linha |
0040DAFB | 50 | PUSH EAX | ; Põe o novo número de linha na pilha |
0040DAFC | 8D05 80DB4000 | LEA EAX,DWORD PTR DS:[40DB80] | ; String de formatação |
0040DB02 | 50 | PUSH EAX | ; Põe string na pilha |
0040DB03 | 8D05 9ADB4000 | LEA EAX,DWROD PTR DS:[40DB9A] | ; Endereço para o resultado (ASCII) |
0040DB09 | 50 | PUSH EAX | ; Põe endereço na pilha |
0040DB0A | FF15 1C644000 | CALL DWORD PTR DS:[<&USER32.wsprintf>] | ; Transforma número em ASCII |
Agora precisamos obter o contexto do ambiente da janela principal para prepararmos a impressão da string que se encontra pronta no endereço 40DB90 na posição que definimos como rodapé. Para obter e bloquear o contexto utilizamos a função GetDC. Logo a seguir, armazenamos o handle do contexto no endereço 40DBA0 porque precisaremos do mesmo para imprimir a string e para liberar o contexto bloqueado depois de efetuarmos a impressão.
004064D8 function GetDC(hWnd: HWND): HDC; stdcall; external 'user32.dll' name 'GetDC' index 257;
Endereço | Hexa | Operação | Observações |
0040DB10 | FF35 00504000 | PUSH DWORD PTR DS:[405000] | ; Handle da janela principal |
0040DB16 | FF15 D8644000 | CALL DWORD PTR DS:[<&USER32.GetDC>] | ; Obtém contexto do ambiente |
0040DB1C | A3 A0DB4000 | MOV DWORD PTR DS:[40DBA0],EAX | ; Salva Handle do Contexto |
Tudo pronto para imprimir! A função utilizada é a TabbedTextOutA que se encarregará de transferir a string " Linha : 0000x" localizada no endereço 40DB90 para o rodapé da janela principal. Esta função exige uma pá de parâmetros:
00406468 function TabbedTextOut (hdc: HDC; X, Y: Integer; lpString: PAnsiChar; nCount, nTabPositions: Integer; var lpnTabStopPositions; nTabOrigin: Integer): Longint; stdcall; external 'user32.dll' name 'TabbedTextOutA' index 633;
Endereço | Hexa | Operação | Observações |
0040DB21 | 6A 01 | PUSH 1 | ; Origem do Tab |
0040DB23 | 6A 00 | PUSH 0 | ; |
0040DB25 | 6A 00 | PUSH 0 | ; |
0040DB27 | 6A 0F | PUSH F | ; Nro de caracteres (nCount) |
0040DB29 | 8D05 90DB4000 | LEA EAX,DWORD PTR DS:[40DB90] | ; String que deve ser impressa (lpString) |
0040DB2F | 50 | PUSH EAX | ; |
0040DB30 | FF35 70DB4000 | PUSH DWORD PTR DS:[40DB70] | ; Posição Y da string (= altura da janela edit) |
0040DB36 | 6A 00 | PUSH 0 | ; Posição X da string |
0040DB38 | FF35 A0DB4000 | PUSH DWORD PTR DS:[40DBA0] | ; Handle do Contexto |
0040DB3E | FF15 68644000 | CALL DWORD PTR DS:[<&USER32.TabbedTextOut>] | ; Imprime a string |
Ufa ! Está por pouco... só falta liberar o contexto para poder voltar ao código original. A função que utilizamos é a ReleaseDC:
004064DC function ReleaseDC(hWnd: HWND; hDc: HDC): Integer; stdcall; external 'user32.dll' name 'ReleaseDC' index 520;
Endereço | Hexa | Operação | Observações |
0040DB44 | FF35 A0DB4000 | PUSH DWORD PTR DS:[40DBA0] | ; Handle do Contexto |
0040DB4A | FF35 00504000 | PUSH DWORD PTR DS:[405000] | ; Handle da Janela Principal |
0040DB50 | FF15 DC644000 | CALL DWORD PTR DS:[<&USER32.ReleaseDC>] | ; Libera o Handle do Contexto |
Hora de voltar para o código principal. Porém, observando o Stack Pointer (ESP - Ponteiro da Pilha), verificamos que fizemos uma salada com tantos pushes. Avançamos 12 bytes que precisam ser "corrigidos" para evitar erros em referências posteriores. Como sabemos que a pilha "anda ao contrário", para DESCER 12 bytes precisamos SOMAR estas posições (12 dec = 0C hexa).
Endereço | Hexa | Operação | Observações |
0040DB56 | 83C4 0C | ADD ESP,C | ; Corrige o Stack Pointer |
0040DB59 | E9 3541FFFF | JMP 00401C93 | ; Volta para WndProc |