sábado, 27 de agosto de 2016

Porque a industria ratificou offset 0x7c00 do segmento zero (in old x86 systems) ?

Why the industry ratified 0x7c00 offset from 0x0000 segment (in 0ld x86 systems)?
[Warning: Exemplos e descrições discutidas, são para antigas máquinas x86 (IBM computers). Muitas coisas aqui, não são mais válidos em comparação às máquinas atuais.]

Realmente, o endereço 0x7c00 não está relacionado aos sistemas 8086, mas de 8088 (16-bits), este seria o motivo dele não estar sendo citado no manual "Intel Architecture x86" (IA32, como propriamente dito) e 0x7c00 é 31,744 em hexadecimal.

Na época que têm surgido o "IBM PC 5150 PC/AT" com Intel 16-bit, usando apenas 16Kib. Ao ligar, a BIOS processava um "Post-On Self-Test" (POST), depois executava o procedimento
(i.e. Lembre-se de quando o PC está iniciando e aparece o background com algumas opções de apertar alguma tecla para entrar na BIOS; e, ou sobe ou desce algumas informações de dados iniciais sendo testados, exemplo:  "Init USB..... OK!". Isto é um POST em ação! Verificações feita pela BIOS, para saber se o sistema operacional está apto para ser iniciado, lembrando que a RAM não é verificada neste processo. Só é testada na cold boot, o antônimo de "warm"; Qual testa todo o hardware e a RAM; bootstrap é iniciado logo em seguida):



Essa interrupção em alguns lugares é referenciada como "system reboot" ou como "Bootstrap Loader Service", pois é usada geralmente pela BIOS para executar o bootstrap routine. Não é exatamente um "Ctrl+Alt+DEL" (warm boot), mas seria algo "parecido". Quando executado, a interrupção da BIOS inicia o POST e previamente o warm boot é inicializado fazendo o bypass das verificações normalmente feitas num cold boot (i.e. verificação de memória).

Que de certa maneira significa fazer a BIOS certificar da existência de algum dispositivo. Caso houver, o primeiro setor de 512B é carregado para o 0x7c00.

Anteriormente, na época do IBM CP/M-86, a BIOS carregava o MBR em 0x200, e não 0x7c00. Em sistemas de arquitetura 8086 os interrupts vector seria em 0x0 até 0x3ff. Então, 86-DOS não poderia usar entre range de endereço 0x200 à 0x3ff, e assim, o 0x400 foi escolhido.

Forma resumida do bootloader layout:


[obs: Em máquinas "modernas", nada impede de ser mudado alterando o IDT]

Em processadores 8086, o valor inicial de seus blocos de 64Kb é obtido multiplicando por 16 (movendo 4-bits à esquerda), deste modo é feito o alinhamento.
Nas primeiras inicializações do computador, é ler (apenas) o setor do dispositivo que foi escolhido para dar o boot (i.e. A princípio, ela lê o setor de disquete e se a ROM-BIOS não conseguir a leitura, lê o setor do disco rígido). Em seguida, a BIOS (de forma vulgar) carrega esse setor em 0x0000:0x7c00 (segment:offset) e posteriormente, aplica um "jmp" (salto) para o mesmo (i.e Caso as duas leituras falharem, a interrupção 18h [ROM-BASIC] é feita a fim de alguma outra opção para "dar boot". Se não haver nenhuma opção a mensagem de erro é mostrada, esperando ou a reinicialização ou a troca do bootable device.

Por consequência de ser 0x7c00, é pra economizar espaço possível para o SO carregar em 32KiB. Para dar o boot, 512kB; Para os segmentos de dados e pilha carregar os aplicativos, 512kB. E por fim, os 1,024Kb (de 32KiB) podem ser usados espontaneamente até houver uma nova reinicialização do SO, já que só poderá ser definido novamente em próximas inicializações. (i.e. Notou que: 32Kib-1,024Kb=31,744Kb?).

- Tá! Mas... como sabe que será exatamente 512kB? Answer: Já ouviu falar da diretiva .ORG (ORiGin)? Grosso modo, uma "diretiva" e não uma "instrução". Ele faz com que o machine code defina uma nova origem para começar a colocar as instruções do programa compilado.
Exemplo: Num ambiente x86-64 temos .ORG 300h = 00000000h + 00000300h. - Logo, ele diz para o assembler o seguinte: "Assembler, meu velho amigo... estou querendo mudar umas coisas nesse endereço (por default: 0000h )". Na sequência, se obtivermos .ORG 300h, o Assembler vai entender que queremos entrar no endereço default + o valor passado para o .ORG (i.g. Como foi colocado acima). Quando usado .ORG $ + 300h, aplica a diretiva do código do endereço atual + 300h (que é o valor definido para a diretiva).

Ah! Pra não deixar de lado, falando sobre o segmento 0x0000... é uma vantagem de não precisar se preocupar em mudar os seletores de segmento para acessar os Interrupt Vectors e as variáveis da BIOS.


[...] sei lá, só pra não perder o costume.


links recomendado:

** Instruction Manual - 8086 Monitor: For use with the CSP 300 CPU Support Boar
http://www.s100computers.com/Hardware%20Manuals/Seattle%20Computer%20Products/SCP%208086%20Monitor.pdf

** BIOS Interrupt Call
https://en.wikipedia.org/wiki/BIOS_interrupt_call#Interrupt_table

quinta-feira, 3 de março de 2016

Diga NÃO às drogas! falando sobre C novamente... 4dummies

Como você se sente num ambiente onde pessoas se reúnem para discutir (de forma dummies) um assunto que você já tem uma noção? - Pois é, dependendo do ensino da faculdade têm horas que dá sono! Principalmente quando estão falando de C para Windows. :(



NENHUM BOM PROFESSOR IRÁ OFERECER O USO DE DROGAS EM LUGAR ALGUM!
[Diga NÃO às drogas!] Saca seu notebook da mochila, mostre para o professor que àquele momento é uma perfeita ocasião para seus discípulos utilizarem alguma distribuição LINUX que se sinta melhor. Pode até usar Ubuntu! É melhor iniciar um ensino usando Ubuntu utilizando fgetc(); do quê, usar Windows fazendo com que os alunos se comportem feito "idiota" acomodando-se com SYSTEM("PAUSE"); e ainda pra piorar... usando DevC++! mesmo pra quem não conhece o software, é questão de uma simples pesquisa para reconhecer que este é um dos piores IDEs ultra desatualizado (ainda) existente para Windows. Ah! E outra coisa, não sei porque raios a maioria dos instrutores de instituições de ensino têm mania de apresentar uma header que não existe! Sim, estou falando da conio.h, a mesma que não faz parte dos compiladores modernos, quer vê-la funcionando? use MS Compiler 6.0 e dentre outros compiladores mais antigos. ;-)


Se você sabe que tal coisa é uma merda, não precisa ensiná-lo à se acostumar pra depois tentar fazer com que se acostume com outras. Por exemplo, acho totalmente errado o professor que realmente manja do assunto, ensinar para seu pupilo ficar usando gets(), strcpy(), scanf(), sprintf(), system() e outros... Pesquisem, leiam mais[1].


Sério, PARE de usar system("PAUSE");
[...] A não ser lógico, que quer levar uma Injeção de Comandos[2].



Podemos observar no código acima que temos um limite de 9 caracteres para executar qualquer comando que quisermos, mas isso não nos restringe de nada. Já que estamos numa interactive shell, podemos muito bem fazer o que mesmo?

clique na imagem para ampliar

Isso mesmo, criar outra sessão! :)


A função padrão system() , apesar de ser extremamente lenta... quando chamada, sabemos que é executado um específico comando. Assim como em Terminal (Unix-like systems) ou Command Prompt (Windows), a mesma têm críticas falhas a ponto de ser explorado por técnicas conhecidas como execução de código arbitrário ou aquele nome mais simples e famoso: "injeção de comandos" (do inglês, command injection).


Mostrando outra forma de explorar

Adotaremos o seguinte código abaixo (todo comentado)...



No momento que você faz o output do comando inserido, pode muito bem realizar um ataque usando o método clássico de "injeção".

'; uname -a;'

Assim, o terminal irá interpretar que estaríamos passando 2 comandos: "USER" e o "uname -a". Ou seja, da mesma forma que fizéssemos...


e agora, manjou?
Agora, vamos entender o script por completo para melhor exploração...
No início temos a inclusão do cabeçalho stdlib.h, vai servir na compilação quando o compilador ler a função getenv(), na sequencia o define que é um pré-processador, aquele que irá criar uma macro para associar o identificador com um token, que é inserido numa cadeia de caracteres. Logo, na hora da compilação, não dará erro por fato do compilador entender que o identificador MAXSIZE e u pode ser associado com qualquer cadeia de caracteres que contenha 512 (int) e "USER" (char).

stdio.h, clássico.

Uma função principal com void declarado como tipo da função e como argumento, significando que não faz retorno e não recebe argumentos. Logo mais, um ponteiro (aquele asterisco, serve para identificá-lo)! do tipo char nomeado como __env e temos abaixo um buffer nomeado como cmd, considere uma sala com um limite de 512 lugares. Posteriormente, entramos numa condição que diz... o que tiver na função getenv, iremos imprimir na tela do usuário. Até aí normal, porque ela vai chamar por: echo $USER, mas não têm nenhum interpretador de comandos para nos dar um output v3n0m, como mostrado na imagem acima. Não feliz com isso, na parte da qual entraremos com a mensagem... jogamos o diretório do interpretador "/bin/bash" (isso não irá interferir em nada, só vai imprimir o diretório), o %s que é o formato de dados para char e a variável ponteiro __env (pra onde ele está apontando?) - Certamente, apontando para o endereço que está os dados getenv(u). E assim temos como saída: "v3n0m". Ainda não satisfeito com toda esta brecha, abaixo, temos como saída (stdout == output) o valor contido no buffer que serão valores com tamanho de 512 bytes, ou seja, o nosso comando tem que ter no máximo 512 bytes. Poxa, que pouco.. não? (ironizando)

Contudo, numa mente inocente, o script faria uma impressão na tela mostrando o nome do usuário. Numa mente um pouco mais "ousada", poderíamos injetar um comando pra ter acesso ao terminal (normalmente) dentro do privilégio do usuário. Veremos...
** obs: a parte do %%execve... um "%" é o escape, seria para o compilador ler o caracter "%" ao invés de imaginar que estaríamos colocando algum formato de impressão. Ex: %s. - Ao visualizar a imagem abaixo vai esclarecer melhor.



Ok, primeira linha é o que falamos da impressão com "%%execve" e o debaixo é o resultado da função system(). Agora já entendemos né? Vamos explorar?

Raciocinando...
Já que temos um output de algo que está na variável do ambiente USER, podemos então, criar uma variável temporária chamada USER e passar alguns comandos diferentes, assim... manipulando o resultado esperado no output, será que funciona? Vamos ver...

clique na imagem para ampliar

E não é que funcionou? :D

Mesmo que você queira usar algo pra dar um "PAUSE", poxa.. utiliza o getchar().



Quer usar para ter interação com a shell do sistema? Poxa, existem 7 tipos de "exec" (execução), pode obter as informações essenciais no exec(3)[3].
Pra quê mesmo que você precisa do SYSTEM() ? Se for pra ser 0wn4d0, pode continuar que está no caminho certo. :)


Por fim, àquela tradição...



Refer:

[1] GNU Glibc : List of security vulnerabilities
https://www.cvedetails.com/vulnerability-list/vendor_id-72/product_id-767/GNU-Glibc.html
[2] Command Injection - OWASP
https://www.owasp.org/index.php/Command_Injection
[3] exec(3) - Linux manual page
http://man7.org/linux/man-pages/man3/exec.3.html