Ir para conteúdo

heisenbergOff

Membros
  • Total de itens

    7
  • Registrado em

  • Última visita

Reputação

0 Neutro

Sobre heisenbergOff

  • Data de Nascimento 30/01/1994

Dados Pessoais

  • Biografia
    Apenas um noob fã de programação funcional e freeBSD.
  • Local
    Curvelo
  • Sexo
    Masculino
  1. OBS: Não foi feita nenhuma correção ortográfica, portanto, relevem/indiquem os erros de grafia.
  2. Tipos Primitivos, Vetores, Laços, e Condições Antes de mergulhar nas funcionalidades da orientação a objetos com JavaScript, a de nos lembrarmos do básico. Este capítulo aborda os seguintes tópicos: Os tipos de dados primitivos em javascript, como strings( palavras ) e números. Arrays, Arranjos Operadores simples, como +, -, *, delete e typeof Instruções de controle de fluxo, como laços e condições while, if...else. Controle de Fluxo com JavaScript. Variáveis ANATOMIA DE UMA DECLARAÇÃO 'Instrução' 'nome da variável' +----\---------------/------+ | +---\-----+ +-----/-----+ | | | var | | racas | | | +---------+ +------+----+ | +---------------------------+ | | Instrução de Declaração Variáveis são usadas para armazenar dados; são a representação abstrata dos valores concretos. Ao escrever programas, é conveniente usar variáveis em vez de dados reais, pois é mais prático escrever autor do que 'John Ronald Reuel Tolkien'; especialmente se no decorrer do código você precisar dele diversas vezes. Os dados armazenados em uma variável podem ser alterados após a atribuição inicial, fazendo jus ao seu nome: "Variável". Para usar variáveis precisamos das seguintes etapas: Declarar a variável Inicializá-la, ou seja, dar a ela algum valor Para declarar uma variável use a palavra reservada var como mostrado no código abaixo: // Isto é um comentário var nome_do_personagem; var jogador; var classe; var nivel; var raca; var tendencia; var divindade; var tamanho; var idade; var sexo; var altura; var peso; var olhos; var cabelos; var pele; var habilidades; // força(FOR), destreza(DES), constituição(CON), inteligência(INT), sabedoria(SAB), carisma(CAR) var pontos_de_vida; // (PV) total, ferimentos_pvs_atuais, dano_por_contusao, deslocamento Ao nomear suas variáveis você pode usar qualquer palavra(A-Za-z), número(0-9), o caractere de sublinhado(_) e o sinal de dólar($). No entanto, a palavra não pode começar com um número, o que significa que a seguinte declaração de código é inválida: var 4_dano; var 8_regeneracao_de_pts_vida; Inicializar uma variável é "dar" a ela um valor pela primeira vez - um valor inicial. Você pode fazê-lo das seguintes maneiras: Primeiro declarar a variável; depois a inicializar Ou declarar e incializá-la em uma única instrução Observe o exemplo: // primeiro a declaração var nome_do_personagem; // depois a inicialização nome_do_personagem; = "Hugo de Payens"; // declaração e inicialização em uma única expressão var raca = "Humano"; Agora, a variável chamada raca contém o valor "Humano". Você pode declarar, e opcionalmente inicializar, várias variáveis em uma única instrução var; basta separar as declarações com uma vírgula, conforme mostrado no seguinte trecho de código: var nome, itens, mensagem_de_boas_vindas = 'Bem-vindo', pontos_de_vida = 4, nacao; Para uma melhor legibilidade, tais variáveis podem ser escritas uma por linha, veja: var jogador = 'Lucas', classe = 'Maiar de Aule', nivel = 1, tendencia = 'Cruel', divindade = ''; // Ainda não sei Observe que usei raca e não raça. Não é recomendado o uso de acentos, cedilha ou quaisquer outros caracteres especiais para nomear suas instruções. Recomendo a verem uma discussão interessante em: https://stackoverflow.com/questions/1661197/what-characters-are-valid-for-javascript-variable-names. Variáveis são case-sensitive Ser case-sensitive quer dizer, minúsculas são diferentes de maiúsculas. Você pode comprovar essa afirmação facilmente usando o console do seu navegador. Digite as seguintes instruções seguidas de um Enter cada: var idade = 23; var IDADE = 1.23; console.log( idade ); console.log( IDADE ); Para poder utilizar o auto-complete na inserção da terceira linha, digite nom e em seguida pressione Tab ou seta para direta. O console auto completa o nome nom pra nome. Para CASE siga o mesmo processo: digite as primeiras letras da palavra e pressione Tab ou seta ->. Veja a imagem da execução dos passos acima: No decorrer do texto daremos apenas um exemplo de código em vez de uma captura de tela: > var idade = 23; > var IDADE = 1.23; > nome; 23 > NOME; 1.23 Os sinais maior que ( > ) indicam o que você digita; e o restante é o resultado da avaliação impresso no Console.Sempre que estiver diante de todo e qualquer exemplo de código, digite-o você mesmo. Logo após, experimente mudar um valor aqui outro lá, brinque e experimente mais sensações até entender realmente o seu funcionamento. Operadores var x = 2; var y = 0; var z = -2; var S = ( x + y ) * x - z * y / ( x + x ); console.log( S ); S | /|\ S - S / \ /|\ /|\ S * S S / S / | | \ /|\ x /|\ /|\ ( S ) S * S ( S ) / | | \ /|\ z y /|\ S + S S + S | | | | x y x x Os operadores tomam um ou dois valores (ou variáveis), realizam uma operação e retornam um valor. Veja o exemplo de uso mais simples que posso imaginar, apenas para esclarecer a terminologia: > 1 + 2 3 No código acima: O símbolo + é o operador. A operação é a adição Os valores de entrada são 1 e 2 ( os operandos ) O valor que obtemos como resultado é o 3 Essa construção é chamada de expressão ANATOMIA DE UMA EXPRESSÃO operandos (e expressões) / \ +--/+ +---\-------+ | 4 | * | ( x - 3 ) | +---+ | +-----------+ | operador Em vez de usar valores como 1 e 2 diretamente na expressão, você pode usar variáveis. Você também pode usar uma variável para armazenar o resultado da operação conforme demonstra o exemplo a seguir: > var mod_de_destreza = 5; > var outros = 2; > var total_iniciativa_modificador = mod_de_destreza + outros; > total_iniciativa_modificador; 7 Abaixo uma tabela contendo as operações aritméticas básicas: Operador | Operação | Exemplo ---------+-----------------------+----------------------------------------------------------- + | Adicão | 1 + 2; -> 3 ---------+-----------------------+----------------------------------------------------------- - | Subtração | 99.99 - 11; -> 88.99 ---------+-----------------------+----------------------------------------------------------- * | Multiplicação | 2 * 3; -> 6 ---------+-----------------------+----------------------------------------------------------- / | Divisão | 6 / 4; -> 1.5 ---------+-----------------------+----------------------------------------------------------- % | módulo, resto da div. | 6 % 3; -> 0 | 5 % 3; -> 2 As vezes é útil testar se um | | número é par ou ímpar. Com o operador % fica simples. | | Todo número impar retorna 1 quando dividido por 2 e 0 | | caso contrário. Exemplo: 4 % 2; -> 0 | 5 % 2; -> 1 ---------+-----------------------+----------------------------------------------------------- ++ | incrementa 1 ao valor | É post-increment quando o valor de entrada é incrementado | | depois que ele é retornado, por exemplo: | | > var a = 123; | | > var b = a++; | | > b; | | 123 | | > a; | | 124 | | O oposto ocorre com o pre-increment. O valor de entrada | | primeiro é incrementado e em seguida retornado, por exemplo: | | > var a = 123; | | > var b = ++a; | | > b; | | 124 | | > a; | | 124 ---------+-----------------------+----------------------------------------------------------- -- | decrementa 1 ao valor | post-decrement | | > var a = 123; | | > var b = a--; | | > b; | | 123 | | > a; | | 122 | | pre-decrement. | | > var a = 123; | | > var b = --a; | | > b; | | 122 | | > a; | | 122 ---------+-----------------------+----------------------------------------------------------- A expressão var ataque = 150; também é uma operação; É a operação de atribuição simples, e = é o operador de atribuição simples. Há também uma família de operadores que é a combinação de uma operação aritimética seguida de uma atribuição. Esse comportamento é chamdado de operação de atribuição composta. Elas podem tornar parte do seu código mais compacto. Veja alguns exemplos: > var pontos_de_vida = 10; > pontos_de_vida += 70; 80 Com base no exemplo anterior podemos concluir que, pontos_de_vida += 70; é apenas uma maneira simplificada de escrever pontos_de_vida = pontos_de_vida + 70. Por exemplo: > pontos_de_vida -= 30; 50 Onde, pontos_de_vida -= 3; é equivalente a pontos_de_vida = pontos_de_vida - 3; > pontos_de_vida *= 2; 100 > pontos_de_vida /= 5; 20 > pontos_de_vida %= 2; 0 Além dos operadores aritméticos e de atrubuição discutidos anteriormente, existem outros tipos de operadores. Veremos mais adiante. Boas práticas Sempre termine suas expressões com ponto e vírgula. O JavaScript possui um mecanismo de inserção de ponto e vírgula automático. No entanto, isso também pode ser uma fonte de erros, então é melhor certificar-se de sempre encerrar suas instruções com um ponto e virgula. Em outras palavras, ambas as expressões > 1 + 1 e > 1 + 1; funcionam. Tipos de dados primitivos Todo valor que você usa contém um tipo. Veja alguns dos tipos de dados primitivos do JavaScript: Number Este inclui números de ponto flutuante(decimais) e inteiros. Os seguintes valores são números: -1, 100, 3.1415. String Este consiste em um conjunto de caracteres, por exemplo 'a', 'um', 'um 2', 'dois 3'. Boolean Este pode ser true ou false Undefined Quando você tenta acessar uma variável que não existe, você obtém o valor especial undefined. O mesmo ocorre quando você tenta acessar uma variável sem valor atribuído. Nos bastidores do JavaScript as variáveis são inicializadas com o valor undefined. O tipo de dado undefined só pode ter um valor - o valor especial undefined. Null Este é outro tipo de dado que pode ter apenas um valor - o valor null. Isso indica que o valor é "vazio", ou "invalido". A diferença entre ele e o undefined é que, caso uma variável tenha o valor null, ela é definida; o que lhe falta é um valor. Veremos alguns exemplos em breve. Tenha calma. Qualquer valor que não pertence a um dos cinco tipos primitivos listados acima é um objeto. O valor null é considerado um objeto, eu sei que é estranho ter um objeto(algo) como sendo "nulo", "nada", "Tem, mas não tem". Aprenderemos mais sobre objetos adiante mas, por enquanto, lembre-se de que o JavaScript possui os seguinte tipos de dados: Primitivos(os cinco vistos anteriormente) Não primitivos(objetos) Obtendo o tipo do valor - o operador typeof Se você deseja obter o tipo da variável ou valor, você pode o usar o operador especial typeof. Este operador retorna uma string que representa o tipo do dado(do valor ou variável). Os valores retornados pelo typeof podem ser: number string boolean undefined object function Veja em seguida o typeof em ação para os cinco tipos de dados primitivos. Numbers Um inteiro é um número, simples não é? Se você atribuir 2 a uma variável e, em seguida usar o operador typeof, a seguinte string será mostrada: > var altura = 2; > typeof n; "number" > altura = 3; > typeof n; "number" Notou no exemplo anterior que você não precisa da instrução var novamente para reatribuir um valor a uma variável? Números de ponto flutuante(decimais) também são do tipo number > var altura = 1.89; > typeof n; "number" Podemos utilizar o typeof com um valor literal: typeof 1.89; "number" Números octais e hexadecimais Quando um número começa com um 0, este é considerado octal. Por exemplo, o octal 0377 é o decimal 255. > var n3 = 0377; > typeof n3; "number" > n3; 255 A última linha do exemplo anterior exibe a representação decimal do valor octal. O ES6 fornece o prefixo 0o (ou 0O, que fica bastante confuso em fontes monospace) para representar octais. Considere o exemplo: console.log( 0o776 ); // 510 Embora você não esteja intimamente familiarizado com os números octais, provavelmente você usou valores hexadecimais para definir cores em folhas de estilo CSS. Existem diversas maneiras de trabalhar com cores em CSS. Duas delas são: Usando valores decimais para especificar a quantidade de R ( red ), G ( green ) e B ( blue ), entre 0 e 255; Por exemplo, rgb( 0, 0, 0 ) é preto e rgb( 255, 0, 0 ) é vermelho(quantidade máxima de vermelho e nenhuma para verde e azul). Usando hexadecimais e especificando dois caracteres para cada valor R, G e B. Por exemplo, #000000 é preto e #ff0000 é vermelho. Isso ocorre porque ff é o valor hexadecimal para o decimal 255. Em JavaScript, você pode colocar 0x antes de um valor hexadecimal, vulgo hex, por exemplo: > var n4 = 0x00; > typeof n4; "number" > n4; 0 > var n5 = 0xff; > typeof n5; "number" > n5; 255 Literais Binários Antes do ES6, se você precisasse representar um binário em decimal, você precisaria passar uma string com o binário e a base 2, por exemplo: console.log( parseInt( '111', 2 ) ); // 7 Do ES6 em diante você pode usar o prefixo 0b(ou 0B) para representar binários como inteiros. Por exemplo: console.log( 0b111 ); // 7 Expoentes Literais 1e1 (também escrito como 1e+1 ou 1E1 ou 1E+1) representa o número 1 com n 0s após ele, ou seja, 10. Da forma forma, 2e+3 representa o número 2 com três 0s depois, ou 2000, por exemplo: > 1e1; 10 > 1e+1; 10 > 2e+3; 2000 > typeof 2e+3; "number" 2e+3 significa mover o ponto decimal três casas à direita do número 2. Enquanto que 2e-3 significa movê-lo para a esquerda. Observe o desenho a seguir: 2e+3 ⟼ 2.0.0.0. ⟼ 2000 ⤻ ⤻ ⤻ 1 2 3 2e-3 ⟼ 0.0.0.2. ⟼ 0.002 ⤾ ⤾ ⤾ 3 2 1 Veja o código: > 2e-3; 0.002 > 123.456E-3; 0.123456 > typeof 2e-3; "number" Infinity Existe um valor especial no JavaScript chamado Infinity. Este representa um número muito alto, um número que o JavaScript não consegue representar. O Infinity de fato é um número, confirme isso avaliando typeof Infinity no console. Você poderá comprovar que um número com 308 zeros é OK, com 309 não! é muito! Para ser preciso, o maior número que o JavaScript consegue representar é 1.7976931348623157e+308 e o menor 5e-324. Veja o seguinte exemplo: > Infinity; Infinity > typeof Infinity; 'number' > 1e309; Infinity > 1e308; 1e+308 Divisões por zero resultam em Infinity > var a = 6 / 0; > a; Infinity Infinity é o maior número (ou melhor, um pouco maior que o maior). Mas e o menor? É o Infinity com o sinal - na frente; -Infinity, por exemplo: > var i = -Infinity; > i; -Infinity > typeof i; 'number' Legal! Isso significa que o Infinity possui dois valores? Um extremo ao outro? Um infinito positivo e outro negativo? É isso mesmo!? Bem, na verdade não. Quando você soma Infinity e -Infinity, você não obtém 0, mas algo que é chamado de Not a Number(NaN). Por exemplo: > Infinity - Infinity; NaN > -Infinity + Infinity; NaN Qualquer operação aritmética que tenha Infinity resultará em Infinity, por exemplo: > Infinity - 20; Infinity > Infinity * 3; Infinity > -Infinity / 2; -Infinity > Infinity - 99999999999999; Infinity Existe um método global menos conhecido, o isFinite(), que informa se dado número é infinity ou não. O ES6 adiciona o método Number.isFinite() que faz a exatamente a mesma coisa. Você deve estar se perguntando o porque de um método repetido. A variante global de isFinite() tenta converter o tipo do valor com Number(value), enquanto que, Number.isFinite() não tenta, e portanto, é mais preciso. NaN O que é aquele NaN visto anteriormente? Acontece que, apesar do seu nome, Not a Number, NaN é um tipo especial e também um número: > typeof NaN; 'number' > var a = NaN; > a; NaN Você recebe NaN quando tenta executar uma operação que assume números, mas a operação falha. Por exemplo, se você tentar multiplicar o natural 10 pelo caractere 'f', o resultado será NaN, porque 'f' obviamente não é um operando válido para uma multiplicação: > var a = 10 * 'f'; > a; NaN NaN é contagioso, se você possuir qualquer operação aritmética que contenha o NaN mesmo que no final, tudo será 'drenado', por exemplo: > 1 + 2 + NaN; NaN Number.isNaN O ES5 possui um método global - o isNaN(). Ele determina se um valor é NaN ou não. Já o ES6 fornece um método bastante similar - o Number.isNaN()(Observe que este método não é global). A diferença entre o global isNaN() e Number.isNaN() é que o global isNaN() faz o casting(converte o tipo) de valores não-númericos antes de avaliá-los como NaN. Vejamos o exemplo a seguir. Estou utilizando o método ES6 Number.isNaN() para testar se algo é NaN ou não: > console.log(Number.isNaN('teste')); // String nao eh NaN false > console.log(Number.isNaN(123)); // Inteiros nao sao NaN false > console.log(Number.isNaN(NaN)); // NaNs sao NaNs true > console.log(Number.isNaN(123 / 'abc')); // 123 / 'abc' resulta em NaN true Vimos que o método global isNaN() do ES5 primeiro faz o casting do tipo não-númerico para só depois fazer a comparação; o resultado a seguir será diferente da sua contrapartida ES6: > console.log(isNaN('teste')); true Se por acaso me perguntarem: "E ai? isNaN() ou Number.isNaN()?" eu responderei: "Number.isNaN()". No entanto, nenhum deles pode ser usado para verificar se algo é um número ou não - eles apenas respondem se um número é ou não NaN. E como eu faço para ver se algo é um número ou não? A Mozilla sugere o seguinte método polyfill para tal tarefa: var isNumber = function( value ) { return typeof value === 'number' && !Number.isNaN( value ); } Number.isInteger Este é um novo método do ES6. Ele retorna true se o número é finito e não contém casas decimais(um número inteiro): > console.log( Number.isInteger( 'teste' ) ); false > console.log( Number.isInteger( Infinity ) ); false > console.log( Number.isInteger( NaN ) ); false > console.log( Number.isInteger( 123 ) ); true > console.log( Number.isInteger( 1.23 ) ); false Strings Uma string é uma sequência de caracteres usada para representar texto. Em JavaScript, todo valor colocado entre aspas simples ou duplas é considerado uma string. Isso significa que 1 é um número, mas '1' é uma string. Quando usado com strings, typeof retorna a string "string", por exemplo: > var s = 'O rato roeu a roupa do rei de roma'; > typeof s; 'string' > var s = '1 tigre, 2 tigres, 3 tigres'; > typeof s; 'string' Veja um exemplo em que o número '1' é usado no contexto de string: > var s = '1'; > typeof s; 'string' Se você não coloca nada entre aspas, o tipo ainda é string (uma string vazia), por exemplo: > var s = ""; typeof s; 'string' Já é de seu conhecimento que, usar o sinal + com números caracteriza a operação de adição aritmética. No entato, se você usar o sinal de mais com strings, a operação resultará na união das strings(concatenação). Veja o exemplo: > var nome = 'Gandalf'; > var sobreNome = 'Lopes'; > var nomeCompleto = nome + ' ' + sobreNome; > nomeCompleto 'Gandalf Lopes' > typeof nomeCompleto 'string' O dúbio sentido do operador + causa confuções e por consequencia, ERROR! Portanto, se você pretende concatenar strings, tenha certeza de que todos os operandos sejam do tipo string. O mesmo se aplica à adição; se você pretende adicionar números, então certifique-se de que os operandos sejam do tipo number. Adiante veremos mais exemplos. Conversão de Strings Quando você usa um número entre "", por exemplo, "1", como operando de uma operação aritmética, a string é coercitivamente transformada em um número. Isso ocorre em todas as operações aritméticas exceto na adição, devido aquela ambiguidade vista anteriormente. Considere o seguinte exemplo: > var s = '1'; > s = 3 * s; > typeof s; 'number' > s; 3 > var s = '1'; > s++; > typeof s; 'number' > s; 2 Uma maneira prequiçosa de converter um número entre "" para um número "de verdade" é multiplicá-lo por 1 (outra maneira é usar a função parseInt()): > var s = '100'; typeof s; 'string' > s = s * 1; 100 > typeof s; 'number' Se a conversão coercitiva falhar, NaN será retornado: > var filme = '101 vira-latas'; > filme * 1; NaN Eu sei que, multiplicar uma string por 1 a converte em number. E o contrário? E se por acaso eu precisar converter qualquer valor em uma string? Bem, você pode concatená-lo a uma string vazia. Veja o exemplo a seguir: > var n = 1; typeof n; 'number' > n = "" + n; typeof n; 'string' Strings Especiais Há também strings com significados especiais, conforme listado na tabela a seguir: String | Significado | Exemplo -------------+--------------------------+----------------------------------------------------------- `\\` | O `\` é o caractere de | > `var s = 'I don't know'`: Error! O JavaScript pensa que `'` | scape. Quando você quer | a string é somente `I don` e o por consequência, o restan- `"` | ter aspas dentro de sua | te do código se torna inválido. As seguintes expressões | string, você deve 'esca- | estão corretas: | par' delas para que o Ja-| > `var s = 'I don"t know';` | vaScript não pense que | > `var s = "I don't know";` | elas indicam o fim da | > `var s = 'I don\'t know';` | string. Caso precise de | > `var s = ""Hello", he said.";` | uma barra invertida na | > `var s = '"Hello", he said.';` | sua string, escape-a com | | outra barra invertida(\\)| -------------+--------------------------+----------------------------------------------------------- `\n` | Final da linha | > `var s = '\n1\n2\n3\n';` | | > `s;` | | > | | " | | 1 | | 2 | | 3 | | " -------------+--------------------------+----------------------------------------------------------- `\r` | [Carriage return](wikiped| Considere as seguintes instruções | ia.org/wiki/Carriage_ret | > `var s = '1\r2';` | urn.com) | > `var s = '1\n\r2';` | | > `var s = '1\r\n2';` | | Ao executar todas as instruções o resultado será: | | > s; | | "1 | | 2" -------------+--------------------------+----------------------------------------------------------- `\t` | Tab | > `var s = "1\t2";` | | > s; | | "1 2" -------------+--------------------------+----------------------------------------------------------- `\u` | O `\u` permite que você | Veja meu nome escrito em Búlgaro com caracteres Cyrillic: | represente caracteres | > "\u0421\u0442\u043E\u044F\u043D"; | com código Unicode. | "Стoян" -------------+--------------------------+----------------------------------------------------------- Existem outros caracteres especiais mas, esses são raramente usados: \b(backspace), \v(vertical tab) e \f(form feed). Template Literals O ES6 implementou uma nova funcionalidade chamada template literals. Outras linguagens de programação como Python e Perl já suportam tal funcionalidade. Templates literals permitem que expressões sejam incorporadas à strings. O ES6 possui dois tipos de literais: template literals e tagged literals. Template literals são strings de uma ou mais linhas contendo expressões incorporadas a elas. Creio eu que você já tenha feito algo parecido com: var [ monster, monsteHp, monsteDefense, myCriticalDamage ] = [ 'Balrog', 35000, 498, 1500 ]; var damage = Math.round( myCriticalDamage - Math.random() * monsterDefense ); monsteHp -= damage; // Mutable data ? var messageAtack = " Você causou " + damage + " de dano no " + monster + ". Agora ele possui " + monsterHp + " de vida"; console.log( messageAtack ); Agora veja o mesmo resultado usando template literals: var messageAtack = `Você causou ${ damage } de dano no ${ monster }. Agora ele possui ${ monsterHp } de vida`; console.log( messageAtack ); As template literals são representadas pelo caractere back-tick(``)(acento grave) em vez de aspas simples ou duplas. Os placeholders são representados pelo sinal de dólar e chaves (${ expressão }). Por padrão eles são concatenados para formar uma única string. Observe o exemplo com atenção: var attack = 125, strongPoints = 14, attackBonus = Math.round( ( Math.random() * strongPoints ) * attack / 100 ), defenseEnemy = 16; console.log( `Você causou ${ Math.round( attack + attackBonus - Math.random() * defenseEnemy ) } de dano no inimigo` ); E que tal invocar uma função? const calcMyAttack = function( attack, strongPoints ) { return attack + Math.round( ( Math.random() * strongPoints ) * attack / 100 ); }; const calcDefenseEnemy = function( defense, vitalityPoints ) { return defense + Math.round( ( ( Math.random( ) * defense + vitalityPoints ) * 2 ) ); }; console.log( `O seu guerreiro causou ${ calcMyAttack( 150, 19 ) - calcDefenseEnemy( 16, 7 ) } de dano no inimigo` ); As templates literals também nos ajuda a escrever múltiplas linhas sem a necessidade do uso de \n. Em vez de escrevermos: console.log("This is line one \n" + "and this is line two"); podemos escrever: console.log(` This is line one and this is line two `); O ES6 possui outro tipo de template literal interessante, chamada Tagged Template Literal. Uma Tagged template permite que você modifique a saída de uma template literal mediante o uso de uma função. Se você prefixar uma expressão para uma template literal, tal é considerada uma função a ser invocada. A função precisa ser definida antes da execução da tagged template literal. Por exemplo, a seguinte expressão: transforma`Meu nome eh ${ sobreNome }, ${ firstname } ${ sobreNome }` equivale a seguinte chamada de função: transforma( [ 'Meu nome eh ', ', ', ' ', '' ], nome, sobreNome ); A tag function (função rótulo) transforma, recebe 2 paramêtros - a template string que em nosso caso inicia com Meu nome eh e as substituições definidas por ${}. As substituições são reconhecíveis apenas em tempo de execução. Observe o exemplo com atenção: function transforma( strings, ...substitutes ) { console.log( strings ); // [ 'Meu nome eh ', ', ', ' ', '' ] console.log( substitutes ); // [ 'Bond', 'James', 'Bond' ] console.log(`${strings[ 0 ] }${ substitutes[ 0 ] }`); // 'Meu nome eh Bond' } var nome = "James"; var sobreNome = "Bond"; transforma`Meu nome eh ${ sobreNome }, ${ nome } ${ sobreNome }`; Quando uma template literal ( Meu nome eh ) é passada para uma tag function, existem duas formas de lidarmos com a sua avaliação, que são as seguintes: De forma crua(raw) onde as barras invertidas não são interpretadas De forma cozida('cooked') onde as barras invertidas possuem significado especial Caso queira avaliar sua template literal de forma crua, use a propriedade raw, conforme mostra o exemplo a seguir: function rawTag( strings, ...substitutes ) { console.log( strings.raw[ 0 ] ); } rawTag`This is a raw text and \n are not treated differently`; Booleans Existem apenas dois valores possíveis para o tipo de dado Boolean - true ou false(sem aspas): > var b = true; > typeof b; "boolean" > var b = false; > typeof b; "boolean" Se por acaso você coloque true ou false entre " ", é claro, o valor será do tipo String: > var b = "true"; > typeof b; 'string' Operadores Lógicos Existem três operadores, chamados de operadores lógicos, que lidam com valores Booleanos. Estes valores são: ! - logical NOT ( NEGAÇÃO ) && - logical AND ( E ) || - logical OR ( OU ) Você sabe que, quando algo NÃO é verdade( true ), lógico, é mentira( false ). Veja como isso pode ser expresso usando o operador lógico ! > var b = !true; > b; false Se você usar operador NOT duas vezes, você obterá o valor original, que é o seguinte: > var b = !!true; > b; true Se você usar o operador lógico em um valor não booleano, este será convertido em booleano implícitamente, da seguinte forma: > var b = "one"; > !b; false No caso anterior, a string "one" é convertida em um booleano true, e depois negada. O resultado de negar true é false. No exemplo a seguir, há uma negação dupla, então o resultado é true > var b = "one"; > !!b; true Você pode transformar qualquer tipo de dado em seu equivalente Booleano. Compreender como cada valor é convertido para booleano é de muita importância. Todos os valores são convertidos para true, exceto os seguintes valores: Uma string vazia "" null undefined O número 0 O número NaN O Booleano false Estes seis valores são tidos como falsos, enquanto todos ou outros são verdadeiros(incluindo, por exemplo, as strings "0", " "(ATENÇÃO! observe o espaço) e "false"). Vamos ver alguns exemplos dos outros dois operadores: o AND lógico (&&) e o OR lógico (||). Quando você usa o &&, o resultado é true somente se todos os operandos forem true. Quando você usa || , o resultado é true se pelo menos um dos operandos for true > var b1 = true, b2 = false; > b1 || b2; true > b1 && b2; false Veja abaixo uma lista das possíveis operações e seus resultados: Operation | Result ---------------+----------- true && true | true true && false | false false && true | false false && false | false true || true | true true || false | true false || true | true false || false | false Você pode compor expressões lógicas da seguinte maneira: > true && true && false && true; false > false || true || false true Você também pode misturar && e || na mesma expressão. Nesses casos, você deve usar parênteses para esclarecer como você pretende que a operação funcione. Considere o seguinte exemplo: > false && false || true && true; true > false && (false || true) && true; false Precedência de Operadores Você deve estar se perguntando: "Por qual motivo a expressão (false && false || true && true) retornou true?" Assim como na matemática, a avaliação de expressões possui uma ordem de precedência: > 1 + 2 * 3; 7 Isso ocorre porque a multiplicação possui precedência sobre a adição, então 2 * 3 é avaliado primeiro, é como se você digitasse: 1 + ( 2 * 3 ); 7 O mesmo vale para as operações lógicas, ! possui precedente e é executado primeiro, assumindo que não há parênteses que exijam o contrário. Então, na ordem de precedência, vem && e, finalmente, ||. Em outras palavras, os dois trechos de código a seguir são os mesmos. No primeiro temos: > false && true || true && false; true E no segundo: > ( false && true ) || ( true && false ); true O padrão ECMAScript define a precedência dos operadores. Um exercício para a memorização cairia muito bem mas, este livro não o oferece. Em primeiro lugar, você vai esquecer, e em segundo lugar, mesmo que você não o esqueça, você não deve confiar nisso. A pessoa que lê e mantém seu código provavelmente será confundida. Avaliação Preguiçosa Se você possui uma expressão contendo várias operações lógicas onde, o seu retorno é dado antes de sua completa avaliação, as operações finais não serão executadas porque elas não afetam o resultado final. Observe o exemplo: > true || false || true || false || true; true Como todas as operações são OR(possuem mesma precedência), o resultado será true se, pelo menos, um dos operandos for true. Após o primeiro operando ser avaliado, fica claro que o resultado será true, independentemente dos valores seguintes. Assim, o mecanismo do JavaScript decide ser preguiçoso(OK, eficiente) evitando processamento desnessário; avaliando código que não afeta o resultado final. Você pode verificar esse comportamento de "curto-circuito" com um pequeno experimento no console, como mostrado no seguinte bloco de código: > var b = 5; > true || ( b = 6 ); true > b; 5 > true && ( b = 6 ); 6 > b; 6 O exemplo a seguir mostra outro comportamento interessante - se o JavaScript encontrar uma expressão não booleana como um operando em uma operação lógica, o valor não booleano é retornado como resultado: > true || "String"; true > true && "String"; "String" > true && "String" && true; true O comportamento do exemplo a seguir não é algo sobre o qual você deve confiar, pois torna o código mais difícil de entender. É comum usar esse comportamento para definir variáveis quando você não tem certeza se elas foram previamente definidas. Se a variável myNumber for definida, seu valor será mantido; Caso contrário, é inicializado com o valor 10 > var myNumber = myNumber || 10; > myNumber; 10 Simples e elegante, não é mesmo? Mas... esteja ciente de que não é completamente infalível. Se myNumber for definida e inicializada tendo 0 como valor, ou tendo qualquer um dos 6 valores falsy, seu código pode não funcionar de maneira previsível, conforme mostra o exemplo: > var myNumber = 0; > var myNumber = myNumber || 10; > myNumber; 10 Comparações Há outro conjunto de operadores que retornam um valor booleano como resultado da operação. Estes são os operadores de comparação. A tabela a seguir os relaciona com exemplos de uso: Símbolo do Operador | Descrição | Exemplo ---------------------+-----------------------------------------------+--------------------- `==` | **Comparação de igualdade:** retorna `true` | > `1 == 1;` | quando ambos os operandos são iguais. Os ope- | true | randos são convertidos para o mesmo tipo tipo | > `1 == 2;` | antes de serem comparados. Também conhecida | false | por **loose comparison**(comparação frouxa) | > `1 == '1';` | | true ---------------------+-----------------------------------------------+--------------------- `===` | **Comparação de tipo e igualdade:** retorna | > `1 === '1';` | `true` se ambos os operandos forem iguais e do| false | mesmo tipo. Essa maneira de comparar é melhor | > `1 === 1;` | e mais segura porque não há conversão de tipos| true | coercitiva. Também conhecida por | | **strict comparison**(comparação rigorosa). | ---------------------+-----------------------------------------------+--------------------- `!=` | **Comparação de desigualdade:** retorna `true`| > `1 != 1;` | se os operandos **não** forem iguais(após uma | false | conversão de tipo). | > `1 != '1';` | | false | | > `1 != '2';` | | true ---------------------+-----------------------------------------------+--------------------- `!==` | **Comparação de igualdade sem conversão de** | > 1 !== 1; | **tipo:** retorna `true` se o tipo ou valor | false | dos operandos não forem iguais. | > 1 !== '1'; | | true ---------------------+-----------------------------------------------+--------------------- `>` | Retorna `true` se operando da esquerda for | > `1 > 1;` | maior que o da direita. | false | | > `33 > 22;` | | true ---------------------+-----------------------------------------------+--------------------- `>=` | Retorna `true` se operando da esquerda for | > `1 >= 1;` | maior ou igual que o da direita. | true ---------------------+-----------------------------------------------+--------------------- `<` | Retorna `true` se operando da esquerda for | > `1 < 1;` | menor que o da direita. | false | | > `1 < 2;` | | true ---------------------+-----------------------------------------------+--------------------- `<=` | Retorna `true` se operando da esquerda for | > `1 <= 1;` | menor ou igual que o da direita. | true | | > `1 <= 2;` | | true ---------------------+-----------------------------------------------+--------------------- Note que NaN não é equivalente a nada, nem a si mesmo. Dê uma olhada na seguinte linha de código: > NaN == NaN; false Undefined e Null Se você tentar usar uma variável não existente, você receberá o seguinte erro: > bla; ReferenceError: bla is not defined Usar o operador typeof em uma variável não existente não ocasiona em erro. Você obterá a string 'undefined' > typeof bla; 'undefined' Se você declarar uma variável sem dar-lhe um valor, isso não é, é claro, um erro. Mas, o typeof ainda retorna 'undefined' > var blu; > blu; > typeof somevar; 'undefined' Isso ocorre porque, quando você declara uma variável sem iniciá-la, o JavaScript a inicializa automaticamente com o valor undefined, conforme mostra o exemplo a seguir: > var blu; > blu === undefined; true O valor null, por outro lado, não é atribuído pelo JavaScript coercitivamente; é você programador o responsável por atribuí-lo, observe o seguinte exemplo: > var blu = null; undefined > blu; null > typeof blu; 'object' Embora a diferença entre null e undefined seja pequena, cuidado, isso pode ser bastante perigoso. Por exemplo, numa operação aritmética você obterá resultados diferentes: > var i = 1 + undefined; > i; NaN > var i = 1 + null; > i 1 Isso é devido ao fato de, null e undefined possuírem diferentes maneiras de serem convertidos para outros tipos de dados primitivos. Os exemplos a seguir mostram as possíveis conversões: Conversão para NaN: > 1 * undefined; NaN Conversão para um número: > 1 * null; 0 Conversão para um Booleano: > !!undefined; false > !!null; false Conversão para uma string: > "value: " + null; "value: null" > "value: " + undefined; "value: undefined" Symbols O ES6 introduziu um novo tipo primitivo de dado - Symbols. Várias linguagens já possuem ideia similiar. Symbols possuem aspecto similar à strings regulares, mas são muito diferentes. Vejamos como criar Symbols: var atom = Symbol() Observe que não usamos o operador new para criar um symbol. Você receberá um erro ao usá-lo: var atom = new Symbol() // TypeError: Symbol is not a constructor Você pode descrever um Symbol var atom = Symbol('atomic symbol') Descrever Symbols ajuda e muito na depuração de grandes programas contento diversos Symbols dispersos. A propriedade mais importante dos Symbols é (e, portanto, o motivo da sua existência) que eles são únicos e imutáveis: console.log(Symbol() === Symbol()) // false console.log(Symbol('atom') === Symbol('atom')) // false Sobre Symbols, por enquanto basta. Os Symbols são usados como chaves de propriedade e lugares onde você precisa de identificadores exclusivos. Vamos discutir Symbols posteriormente. Recaptulando tipos de dados primitivos Vamos resumir alguns dos principais pontos já descutidos anteriormente: Existem cinco tipos de dados primitivos no JavaScript: Number String Boolean Undefined Null Tudo que não é primitivo é objeto. O tipo de dado primitivo Number pode armazenar números inteiros/decimais, negativos/positivos, números hexadecimais, números octais, expoentes e os números especiais: NaN, Infinity, e -Infinity. O tipo de dado String contém caracteres entre aspas. Template literals permitem a incorporação de expressões dentro de uma string. Os booleanos só podem assumir dois valores: True ou False. O único valor possível para o tipo de dado null é o próprio Null. O único valor possível para o tipo de dado undefined é o próprio Undefined. Todos os valores se tornam verdadeiros quando convertidos em um Booleano, com exceção dos seguintes seis valores falsy "" null undefined 0 NaN false Arrays Agora que você já conhece os tipos de dados básicos em Javascript, é hora de passar para uma estrutura de dados mais poderosa - O Array. E o que é um Array? é simplesmente uma lista(uma sequencia) de valores. Em vez de usar uma variável para armazenar um valor, você pode usar uma variável do tipo Array para armazenar qualquer número de valores como elementos do Array. Para declarar uma variável que contém um Array vazio, você poder usar par de colchetes sem nada entre eles, conforme o exemplo a seguir: > var vetor = []; Para definir um Array que possui três elementos, você pode escrever a seguinte linha de código: var vetor = [ 1, 2, 3 ]; Para obter o conteúdo do Array, apenas digite o seu nome no console: > vetor; [1,2,3] Agora, a questão é como acessar os valores armazenados nesses elementos do Array. Os elementos contidos em um Array são indexados com números consecutivos, começando a partir do zero. O primeiro elemento possui índice (ou posição) 0, o segundo possui índice 1 e assim por diante. Observe com atenção a estrutura do Array que criamos anteriormente: +-------+-------+ | Index | Value | +-------+-------+ | 0 | 1 | +-------+-------+ | 1 | 2 | +-------+-------+ | 2 | 3 | +-------+-------+ Para acessar um elemento do Array, você pode especificar o índice desse elemento entre colchetes. Então, vetor[0] lhe dá o primeiro elemento do Array, vetor[1] lhe dá o segundo, e assim por diante. Observe o exemplo a seguir: > vetor[0]; 1 > vetor[1]; 2 Adicionando/Atualizando Elementos em Arrays Usando o índice, você também pode atualizar os valores dos elementos do Array. O próximo exemplo atualiza o terceiro elemento(índice 2) e imprime o counteúdo atualizado do Array, da seguinte maneira: > vetor[2] = "três"; > vetor; [1,2,"três"] Você pode adicionar mais elementos os endereçando a um índice que ainda não existe, conforme mostrado no exemplo a seguir: > vetor[3] = 'quatro'; "quatro" > vetor; [1, 2, "três", "quatro"] Se você adicionar um novo elemento deixando espaços no Array, esses índices irão ser preenchidos com o valor undefined. Confira o seguinte exemplo: > var vetor = [ 1, 2, 3 ]; > vetor[6] = 'new'; > vetor; [1, 2, 3, , , , "new"] > vetor[5]; undefined Deletando Elementos Para deletar um elemento, você pode usar o operador delete. No entanto, após a exclusão, o comprimento do Array não muda. Em certo sentido, você faz um buraco no Array > var vetor = [ 1, 2, 3 ]; > delete vetor[1]; true > vetor; [ 1, , 3 ] > typeof vetor[1]; "undefined" Arrays de Arrays Arrays podem conter todos os tipos de valores, incluindo outros arrays: > var vetor = [ 1, "dois", false, null, undefined ]; > vetor; [ 1, "dois", false, null, undefined ] > vetor[5] = [ 1, 2, 3 ]; [1,2,3] > vetor; [1, "two", false, null, undefined, Array[3]] O Array[3] no resultado é clicável no console ele expande os valores do Array. Vejamos um exemplo em que você possui um Array de dois elementos, ambos sendo outros Arrays: > var vetores = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]; > vetor; [Array[3], Array[3]] O primeiro elemento do Array é [0], e também é um Array: > vetores[0] [1,2,3] Para acessar um elemento no Array 'aninhado', você pode consultar o índice de elementos em outro par de colchetes, da seguinte forma: > vetores[0][0]; 1 > a[1][2]; 6 Observe que você usar a notação de Array para acessar caracteres individuais dentro de Strings, como mostrado no exemplo a seguir: > var palavra = "Portugues"; > palavra[0]; "P" > palavra[1]; "o" > palavra[0]; "r" Há ainda muito mais sobre arrays, mas vamos parar aqui por enquanto, lembrando os seguintes pontos: Um Array é uma 'loja' de dados Um Array contém elementos indexados Os índices começam de zero e aumentam em um para cada elemento no Array Para acessar um elemento no Array, use o seu índice entre colchetes Um Array pode conter qualquer tipo de dado, incluindo outros arrays Condições e Repetições As condições fornecem uma maneira simples, mas poderosa, de controlar o fluxo de execução do código. Loops permitem que você execute operações repetitivas com menos código. Vamos dar uma olhada em: condição if declarações switch laços while, do...while, for, e for...in Blocos de Código Nos exemplos anteior, você viu o uso de blocos de código. Vamos usar um pouco do nosso tempo para esclarecer o que é um bloco de código, porque você usará blocos extensivamente ao criar condições e loops. { var a = 1; var b = 3; } Você pode aninhar blocos indefinidamente, como mostrado no exemplo a seguir: { var a = 1; var b = 3; var c, d; { c = a + b; { d = a - b; } } } A condição if Obseve a seguir um caso de uso simples da condição if var resultado = '', a = 3; if ( a > 2 ) { resultado = 'a é maior que 2'; } A condição if é composta pelas seguintes partes: A declaração if A condição entre ( ) - a > (maior que) 2 ? O bloco de código no interior das { } que é executado caso a condição seja satisfeita A condição( o trecho entre parênteses ) sempre retorna um valor booleano e também pode conter: Uma operação lógica = !, && ou || Uma comparação, como ===, !=, > e assim por diante Qualquer valor ou variável que possa ser convertida em um booleano Uma combinação dos itens e comportamentos citados acima A cláusula else Há também a declaração opcional else que depende da condição if. A declaração else é seguida por um bloco de código que é executado caso a condição for avaliada como false if ( a > 2 ) { resultado = 'a é maior que 2'; } else { resultado = 'a NÃO é maior que 2'; } Entre as declarações if e else, também pode haver um número ilimitado de condições else...if. Observe o seguinte exemplo: if ( a > 2 || a < -2 ) { resultado = 'a não está entre -2 e 2'; } else if ( a === 0 && b === 0 ) { resultado = 'tanto a quanto b possuem o valor 0'; } else if ( a === b ) { resultado = 'a e b são iguais'; } else { result = 'Tudo ocorreu bem!'; } Você também pode aninhar as condições, colocando novas condições dentro de qualquer um dos blocos, conforme mostrado no seguinte código: if ( a === 1 ) { if ( b === 2 ) { resultado = 'a é 1 e b é 2'; } else { resultado = 'a é 1 mas b definitivamente não é 2'; } } else { resultado = 'a não é 1, e eu não faço ideia de quem seja b'; } Checando se uma Variável Existe Vamos aplicar o conhecimento que acabamos de adquirir em algo prático. Muitas vezes, é necessário verificar se uma variável existe. A maneira mais preguiçosa de fazer isso é simplesmente colocar a variável na condição parte da instrução if, por exemplo, if ( blu ) {...}. Mas, este não é necessariamente o melhor método. Vamos dar uma olhada em um exemplo que testa se uma variável chamada blu existe e, em caso afirmativo, definir a variável resultado como sim var resultado = ''; if ( blu ) { resultado = 'sim'; } // ReferenceError: blu is not defined resultado; // "" O código obviamente deveria funcionar mesmo não tendo o valor sim como resultado. Mas, em primeiro lugar, o código gerou um erro - blu is not defined, e você não deseja que seu código se comporte desse jeito. Em segundo lugar, apenas porque if ( blu ) retorna false, isso não significa que blu não esteja definida. Pode ser que blu esteja definida e inicializada, mas contém um valor falsy como false ou 0. Uma boa maneira de verificar se um valor existe é usando o operador typeof var resultado = ''; if ( typeof blu !== 'undefined' ) { resultado = 'sim'; } resultado; // "" O operador typeof sempre retorna uma String, e você pode compará-la com a string "undefined". Observe que a variável blu pode ter sido declarada, porém não inicializada(atribuído um valor a ela) ocasionando assim o mesmo erro. Com o uso do operador typeof, você realmente testa se a variável possui um valor diferente de undefined var blu; if ( typeof blu !== 'undefined' ) { resultado = 'sim'; } resultado; // "" blu = undefined; if ( typeof blu !== 'undefined' ) { resultado = 'sim'; } resultado; // "" Se uma variável for definida e inicializada com qualquer valor diferente de undefined, o tipo retornado por typeof não é mais "undefined", como mostra o seguinte exemplo: var blu = 23; if ( typeof blu !== 'undefined' ) { resultado = 'sim'; } resultado; // "sim" Alternativa à Sintaxe if Quando você precisar de uma condição simples, você pode considerar usar uma alternativa à sintaxe if. Observe a seguinte condição: var a = 1 resultado = ''; if ( a === 1 ) { resultado = 'a é um'; } else { resultado = 'a não é um'; } O exemplo acima pode ser escrito da seguinte maneira: var a = 1; var resultado = ( a === 1 ) ? 'a é um' : 'a não é um'; Você deve usar essa sintaxe apenas para condições simples. Tenha cuidado para não usar e abusar, pois é algo que facilmente pode deixar seu código ilegível. Observemos o exemplo adiante. Digamos que você quer ter certeza de que um número esteja dentro de um determinado intervalo, digamos entre 50 e 100: > var a = 123; > a = a > 100 ? 100 : a < 50 ? 50 : a; > a; 100 Pode não ficar claro como esse código funciona exatamente por causa dos múltiplos ?. Adicionar parênteses torna um pouco mais claro, como mostrado no seuinte exemplo: > var a = 123; > a = ( a > 100 ? 100 : a < 50 ) ? 50 : a; > a; 50 > var a = 123; > a = a > 100 ? 100 : ( a < 50 ? 50 : a ); > a; 100 A expressão que contém os símbolos ? e é chamada de operador ternário porque leva três operandos. Switch Se você se encontra usando a condição if com diversos else...if, você pode considerar trocar o if pelo switch, da seguinte maneira: var a = '1', resultado = ''; switch ( a ) { case 1: resultado = 'Number 1'; break; case '1': resultado = 'String 1'; break; default: resultado = "I don't know"; break; } O resultado após a execução é 'String 1'. A condição switch é composta pelas seguintes partes: A declaração switch. Uma expressão entre parênteses. Na maioria das vezes a expressão contém uma variável, mas pode ser qualquer coisa que retorne um valor. Uma série de case(casos) no interior das { }. Cada declaração case é seguida por uma expressão. O resultado da expressão é comparado com a expressão econtrada após a instrução switch. Se o resultado da comparação for true, o código seguinte aos dois pontos do case é executado. Existe a declaração opcional break que emite um sinal de parada para o bloco case. Se essa declaração break for acionada, a declaração switch e tida como finalizada. Caso contrário, se o break não existir, a execução do programa entra no próximo bloco case. Podemos usar a declaração opcional default para indicar um caso padrão e, esta declaração é seguida de um bloco de código. O caso padrão é executado caso todos os anteriores não retornem true. Em outras palavras, a declaração switch é executada da seguinte maneira: Avalie a expressão switch entre os parênteses; retenha o resultado. Mova para o primeiro case e compare seu valor com o resultado retido do Passo 1. Se a comparação do Passo 2 retornar true, execute o código do bloco case. Depois que o bloco case é executado, se houver a intrução break no final, termine a execução do switch. Se não houver break ou o passo 2 retornar false, mova para o próximo bloco case. Repita os passo de 2 até 5. Se a execução chegou até aqui(não foi encerrada no passo 4), execute o código que se encontra na instrução default. Não Esqueça do break Às vezes pode se fazer necessário omitir a instrução break intencionalmente, mas isso é raro. Isso é chamado de fall-through e deve ser sempre documentado porque pode parecer uma omissão acidental. Por outro lado, às vezes você pode necessitar omitir todo o bloco de código após um caso e ter dois casos compartilhando o mesmo código. Tudo bem, ok, mas isso não altera a regra de que, se houver um código logo após uma instrução case, este código vai acabar sendo encerrado com a intrução break. Use o case default. Isso o ajuda a garantir a obtenção de um resultado significativo após a intrução switch, mesmo que nenhum dos cases corresponda ao valor que está sendo switched( trocado =| ). Laços - Loops As instruções if...else e switch permitem que seu código tome caminhos diferentes, é como se você estivesse em uma encruzilhada a decidir qual caminho seguir mediante o resultado de uma condição. Loops, por outro lado, permitem que seu código faça algumas voltas antes de voltar a entrar na estrada principal. Quantas voltas? Isso depende do resultado da avaliação de uma condição antes(ou depois) de cada iteração(volta). Digamos que você esteja(a execução do seu programa esteja) viajando de A para B. Em algum momento, você alcançará um lugar onde você deve avaliar uma condição, C. O resultado da avaliação de C diz se você deve entrar em um loop, L. Você faz uma iteração e chega em C novamente. Então, você avalia a condição mais uma vez para verificar se outra iteração será necessária. E eventualmente, você segue o caminho para B Um loop infinito é aquele que a condição é sempre verdadeira, fazendo com que seu código fique preso no loop para sempre. Este é, claro, um erro lógico, e você deve estar super atento a tais cenários. Em JavaScript, temos quatro tipos de loops: while loops do-while loops for loops for-in loops Laços While O mais simples dos laços é o while. Veja o exemplo a seguir: var i = 0; while( i < 10 ) { console.log( i++ ); } A instrução while é seguida de uma condição entre parênteses e de um bloco de código entre chaves({ }). O bloco de código é executado sempre que a condição for avaliada como true. Laços Do-while O loop do...while é uma pequena variante do loop while. Observe o exemplo a seguir: var i = 0; do { console.log( i++ ); } while ( i < 10 ); No exemplo acima, a declaração do é seguida de um bloco de código e uma condição após o bloco. Isso significa que o bloco de código sempre será executado pelo menos uma vez, antes da condição ser avaliada. Inicialize o i como sendo 11 em vez de 0 como eu o fiz no último exemplo, e verás que o primeiro exemplo(o loop while) não sera executado, e o i ainda terá 11 no final da execução. Já no segundo(o loop do...while), o bloco de código será executado uma vez e o i terá 12 como valor no final da execução. Laços For O loop for é de longe o mais utilizado e, você deve se certificar de que está confortável com ele. Ele requer apenas um pouco mais em termos de sintaxe: Além da condição C e do bloco de código L, você possui o seguinte: Inicialização este é o código executado antes de entrar no loop( é o 0 do diagrama acima ) Incremento este é o código executado a cada iteração (é o ++ do diagrama acima) Veja a seguir como funciona o padrão de loop mais usado, for Na inicilização, você pode definir uma variável(ou setar o valor inicial com uma variável já existente), mais frequentemente chamda de i Na condição, você pode comparar i com algum valor, por exemplo, i < 100 No incremento, você pode acrescer i em 1, por exemplo, com a instrução i++ Veja o exemplo a seguir: var castigo = ''; for ( var i = 0; i < 100; i++ ) { castigo += 'Eu nunca mais farei isso novamente, '; } Os três fragmentos( inicialização, condição e incremento ) podem conter várias expressões separadas por vírgulas. Digamos que você deseja reescrever o exemplo anterior e definir a variável castigo dentro do fragmento de inicialização do loop: for ( var i = 0, castigo = ''; i < 100; i++ ) { castigo += 'Eu nunca farei isso novamente, '; } Posso eu mover o corpo do loop para dentro do fragmento de incrementação? Sim, podemos, isso é conhecido como "expressão one-liner". Isso fará com que o loop fique estranho, pois não terá corpo. Tenha em mente que, o que eu acabo de dizer é apenas um exercício intelectual; não recomendo em hipótese alguma a escrita de código estranho for ( var i = 0; castigo = ''; i < 100; i++, castigo += 'Eu nunca farei isso novamente, ' ) { /* O corpo está vazio! Estranho pra c... */ } Todos estes três fragmentos são opcionais. Veja abaixo outra maneira de escrever um loop for var i = 0, castigo = ''; for (;;) { castigo += 'Eu nunca farei isso novamente, '; if (++i == 100) { break; } } O código do exemplo anterior funciona da mesma maneira que o clássico(o primeiro exemplo), porém, ele se torna longo e de díficil leitura. Sem contar que, podemos obter o mesmo resultado usando um loop while. Mas, os loops for tornam nosso código mais "sucinto" e mais robusto porque a sua sitaxe simples faz você pensar sobre os três fragmentos de uma só vez(inicialização, condição e incremento) e, assim, o ajuda a pensar melhor na lógica, evitando situações como a de ficar preso em loops infinitos. Podemos aninhar loops. Vejamos um exemplo de loop aninhado que monta uma string contendo dez linhas e dez colunas de asteriscos. Pense em i sendo a linha e j sendo a coluna de uma imagem: var squareWithStar = '\n'; for ( var i = 0; i < 10; i++ ) { for ( var j = 0; j < 10; j++ ) { squareWithStar += '* '; } squareWithStar += '\n' } Veja abaixo a string resultante: Veja a seguir outro exemplo de loop aninhado que tem como resultado uma string semelhante a um floco de neve: var snowflake = '\n', i, j; for ( i = 1; i <= 7; i++ ) { for ( j = 1; j <= 15; j++ ) { snowflake += ( i * j ) % 8 ? ' ' : '*'; } snowflake += '\n'; } Veja o resultado: Laços For...in O loop for...in é usado para iterar sobre os elementos de uma matriz ou objeto, como você verá depois. Esta é a sua única utilidade; ele não funciona como os mecanismo de repetições clássicos como for e while. Veja a seguir um exemplo de uso do for-in para percorrer os elementos de uma array. Mas, tenha em mente que tal exemplo é apenas para fins informativos, pois o for...in é indicado para objetos e, caso queira percorrer arryas, use o loop for padrão. Neste exemplo, eu estou iterando sobre todos elementos de um Array e mostrando o índice(a chave) e o valor de cada elemento: // exemplo para fins informativos // for-in loops são adequados para objetos // use o for padrão para iterar sobre arrays var vogais = [ 'a', 'e', 'i', 'o', 'u' ]; var resultado = '\n'; for ( var vogal in vogais ) { resultado += `indice: ${ vogal }, valor: ${ vogais[ vogal ] } \n`; } Comentários Preciso falar sobre comentários. Você pode colocar comentários dentro do seu código JavaScript. Estes são ignorados pelo mecanismo do JavaScript e não tem nenhum efeito sobre o funcionamento do programa. Mas eles são de grande importância quando você revisita seu código após alguns meses, ou quando outra o pega para alguma manutenção ou extensão. Existem dois tipos de comentário: Comentário de uma linha: começam com // e terminal no final da linha Comentário multi-linha: começam com /* e terminam com */ na mesma linha ou em qualquer linha subsequente. Tenha em mente que qualquer código entre a marcação de um comentário é ignorado. Veja alguns exemplos: // Apenas um comentário let a = 1; // Outro comentário /* Um comentário multi-linha de uma linha só */ /* * Veja mais sobre a instrução let em: * - https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/let * - https://pt.stackoverflow.com/questions/47165/qual-%C3%A9-a-diferen%C3%A7a-entre-declara%C3%A7%C3%A3o-de-vari%C3%A1veis-usando-let-e-var * - http://blog.alura.com.br/entenda-diferenca-entre-var-let-e-const-no-javascript/ */ Existe ainda utilitários, como JSDoc e YUIDoc, que podem analisar seu código e gerar uma documentação significativa com base em seus comentáios! Vale a pena conferir!
  3. ---_ ......._-_--. (|\ / / /| \ \ / / .' -=-' `. / / .' ) _/ / .' _.) / / o o _.-' / .' \ _.-' / .'*| \______.-'// .'.' \*| \| \ | // .'.' _ |*| ` \|// .'.'_ _ _|*| . .// .'.' | _ _ \*| \`-|\_/ / \ _ _ \*\ `/'\__/ \ _ _ \*\ /^| \ _ _ \* ' ` \ _ _ \ ASH (+VK) \_ fonte: http://ascii.co.uk/art/snake = Python with Penetration Testing and Networking Penetration(pen) tester e hacker são termos semelhantes. A diferença é que pen-testers trabalham em uma organização para evitar às investidas de hackers, enquanto que um hacker geralmente trabalha para obter fama, vender vulnerabilidades por dinheiro, ou para explorar vulnerabilidades a fim de adiquirir algumas inimizades. Hackers bem treinados trabalham no campo de segurança da informação, invadindo um sistema e informando a vítima dos bugs de segurança para que eles possam ser corrigidos. Um hacker chamado de pentester é aquele que trabalha em uma empresa a fim de protejer seu sistema. Um pentester só inicia suas tentativas de quebrar a rede depois de obter a aprovação legal do cliente e, em seguida, apresenta um relatório de suas descobertas. Para se tornar um especialista em pentesting, uma pessoa deve ter profundo conhecimento dos conceitos de sua tecnologia. Neste capítulo, abordaremos os seguintes tópicos: • O escopo do pentesting • A necessidade do pentesting • Componentes a testar • Qualidades de um bom pentester • Abordagens do pentesting • Compreendendo os testes e ferramentas que você precisará • Network sockets • Métodos server socket • Métodos client socket • Métodos general socket • Exemplos práticos com socket • Socket exceptions • Métodos úteis do socket == Introducing the scope of pentesting Em simples palavras, penetration testing é o teste que avalia as políticas de segurança da informação de uma empresa. As medidas de segurança da informação envolvem a rede da empresa, banco de dados, site, servidores públicos e tudo o mais especificado pelo cliente. No final do dia, um pentester deve apresentar um relatório detalhado de suas descobertas, tais como instabilidades, vulnerabilidade na infra-estrutura da empresa, e o nível de risco da vulnerabilidade em particular, e fornecer soluções, se possível. === The need for pentesting Existem vários pontos que descrevem o significado do pentesting: * O pentesting identifica as ameaças que podem expor a confidencialidade de uma organização * Um Expert em pentesting fornece garantia para a organização com uma avaliação completa e detalhada da segurança organizacional * O pentesting avalia a eficiência da rede, produzindo uma enorme quantidade de tráfego e examina a segurança de dispositivos como firewalls, roteadores e switches * Alterar ou atualizar a infra-estrutura de software existente, hardware ou design de rede pode levar a vulnerabilidades que podem ser detectadas por pentesting * No mundo de hoje, as ameaças potenciais estão aumentando significativamente; Pentesting é um exercício proativo para minimizar a chance de ser "exploitado" * O Pentesting garante se as políticas de segurança adequadas estão sendo seguidas ou não Considere um exemplo de um e-commerce de nome que que gera sua renda com um serviço on-line. Um hacker ou grupo de hackers black hat encontram uma vulnerabilidade no site da empresa e a explora. Pode ser devastador a quantidade de prejuízo que eles podem causar. === Components to be tested Uma organização deve realizar uma operação de avaliação de risco antes do pentesting; isso ajudará a identificar as principais ameaças, como a má configuração ou vulnerabilidades em: * Roteadores, switches, ou gateways * Sistemas voltados para o público; Sites, DMZ, servidores de e-mail e sistemas remotos * DNS, firewalls, servidores proxy, FTP, e servidores web O teste de segurança deve ser executado em todos os componentes de hardware e software de um sistema em rede. === Qualities of a good pentester Os pontos a seguir descrevem as qualidades de um bom pentester. Ele deve: * Escolher um conjunto adequado de testes e ferramentas que equilibram custos e benefícios * Seguir procedimentos adequados com planejamento e documentação adequada * Estabelecer o escopo de cada teste, tais como objetivos, limitações e justificativa de procedimentos * Estar pronto para mostrar como explorar as vulnerabilidades * Indicar claramente os potenciais riscos achados no relatório final e fornecer métodos para a correção dos mesmos, se possível * Manter-se sempre atualizado porque a tecnologia está em constante evolução Um pentester testa a rede usando técnicas manuais ou ferramentas relevantes. Existem muitas ferramentas disponíveis no mercado. Algumas delas são de código aberto; outras muito caras. Com a ajuda da programação, você pode fazer suas próprias ferramentas. Ao criar suas próprias ferramentas, você pode esclarecer seus conceitos e também executar mais "R&D". Se você está interessado em pentesting e quer fazer suas próprias ferramentas, então a linguagem de programação Python é a melhor, pois conta com uma ampla variedade de pacotes para pentesting e é muito simples programar com ela. Esta simplicidade, juntamente com as bibliotecas de terceiros, tais como scapy e mechanize, reduz o tamanho do código. Em Python, para fazer um programa, você não precisa definir classes "gordas" como no Java. É mais produtivo escrever código em Python do que em C, e existem bibliotecas de alto nível para fazer praticamente qualquer tarefa imaginável. Se você já sabe um pouco sobre programação com Python e está interessado em pentesting este livro é ideal para você. === Deining the scope of pentesting Antes de entrar na fase do pentesting, o escopo do pentesting deve ser concebido. Os seguintes pontos devem ser considerados enquanto definimos o escopo: * Você deve desenvolver o escopo do projeto em consulta com o cliente. Por exemplo, se Bob (o cliente) quiser testar toda a infra-estrutura de rede da organização, então a pentester Alyosha definirá o escopo do pentesting levando essa rede em consideração. Alyosha consultará Bob quando houver alguma área a mais que precisa ser analizada * Você deve levar em conta o tempo, as pessoas e o dinheiro. * Você deve perfilar os limites do teste com base em um contrato assinado pelo pentester e pelo cliente. * As mudanças nas práticas da empresa podem afetar o escopo. Por exemplo, a adição de uma sub-rede, novas instalações de componentes, a adição ou modificação de um servidor web, e assim por diante, pode alterar o escopo do pentesting. O escopo do pentesting é definido em dois tipos de testes: * **Teste não destrutivo**: Este teste é limitado a encontrar e executar os testes sem riscos potenciais. Ele executa as seguintes ações: ** Escaneia e identifica potenciais vulnerabilidades no sistema remoto ** Investiga e verifica as descobertas ** Mapeia as vulnerabilidades com exploits apropriados ** Exploita o sistema remoto com o devido cuidado para evitar interrupções ** Fornece uma prova de conceito ** Não efetua um ataque de negação de serviço(DoS) * **Teste destrutivo**: Este teste pode produzir riscos. Ele executa as seguintes ações: ** Efetua ataques DoS e buffer overflows, que tem o potêncial de derrubar o sistema == Approaches to pentesting Existem três tipos de abordagens para pentesting: * **Black-box pentesting** segue a abordagem não-determinística de testes ** Você receberá apenas um nome de empresa ** Você deve agir - entre aspas - como um black hat(sem conhecimento prévio da infra) ** Não há necessidade de qualquer conhecimento prévio do sistema ** É demorado * **White-box pentesting** segue a abordagem determinista de testes ** Você receberá o conhecimento completo da infra-estrutura que precisa ser testada ** Serão fornecidas informações sobre a infra-estrutura da empresa, o tipo de rede, as políticas da empresa, os tópicos a fazer e a não fazer, o endereço IP e o firewall IPS/IDS * **Grey-box pentesting** segue uma abordagem híbrida de ambas as abordagens citadas acima: ** O pentester geralmente tem informações limitadas sobre a rede/sistema que é fornecido pelo cliente para reduzir os custos e diminuir tentativa e erro por parte do pentester ** Ele realiza a avaliação de segurança e os testes internamente == Introducing Python scripting Antes de começar a ler este livro, você deve conhecer os conceitos básicos da programação em Python, como a sintaxe básica, os tipos de variáveis, a estrutura de dado tupla, o dicionário, as funções, as strings, os métodos e assim por diante. Duas versões, 3.4 e 2.7.8, estão disponíveis em python.org/downloads/. Neste livro, todas as experiências e demonstrações foram feitas em Python versão 2.7.8. Se você usa o sistema operacional Linux como Kali ou BackTrack, então não haverá nenhum problema, porque muitos programas, como snifing wireless, não funcionam na plataforma Windows. O Kali Linux também usa a versão 2.7. Se você gosta de trabalhar no Red Hat ou CentOS, então esta versão é adequada para você A maioria dos hackers escolhem esta profissão porque eles não querem programar. Eles querem usar ferramentas. No entanto, sem programação, um hacker não pode melhorar suas habilidades. E de tempos em tempos eles procuram novas ferramentas na internet. Acredite em mim, depois de ver a sua simplicidade, você vai adorar esta linguagem. == Understanding the tests and tools you'll need Como você deve ter visto, este livro é dividido em sete capítulos. Para realizar varreduras e snifing pentesting, você precisará de uma pequena rede de dispositivos conectados. Se você não tem um laboratório, você pode fazer máquinas virtuais em seu computador. Para análise de tráfego sem fio, você deve ter uma rede sem fio. Para realizar um ataque na web, você precisará de um servidor Apache rodando na plataforma Linux. Será uma boa idéia usar CentOS ou Red Hat versão 5 ou 6 para o servidor web, pois contém os RPM's do Apache e PHP. Para o script Python, vamos usar a ferramenta Wireshark, que é de código aberto e pode ser executado no Windows, bem como plataformas Linux. == Learning the common testing platforms with Python Agora vamos começar a falar de pentesting; Espero que você esteja bem familiarizado com fundamentos de rede, como endereços IP, sub-redes classful, sub-redes sem classes, o significado das portas, endereços de rede e endereços de broadcast. Um pentester deve ser perfeito em fundamentos de redes, ou pelo menos entender bem um sistema operacional; Se você está pensando em usar o Linux, então você está no caminho certo. Neste livro, vamos executar nossos programas no Windows, bem como Linux. Neste livro, Windows, CentOS e Kali Linux serão usados. Um hacker sempre gosta de trabalhar em um sistema Linux. Como é livre e de código aberto, Kali Linux marca o renascimento do BackTrack e vem com um arsenal de ferramentas de hacking. Kali Linux NetHunter é a primeira plataforma de teste de penetração Android de código aberto para dispositivos Nexus. Algumas ferramentas funcionam no Linux e no Windows, mas no Windows, geralmente essas ferramentas não funcionam. Espero que você tenha conhecimento do Linux. Agora, é hora de trabalhar com redes em Python. == Network sockets Um network socket adress contém um endereço IP e um número de porta. De uma forma muito simples, um socket é uma maneira de falar com outros computadores. Por meio de um socket, um processo pode se comunicar com outro processo através da rede. Para criar um socket, use a função `socket.socket()` que está disponível no módulo socket. A sintaxe geral de uma função socket é a seguinte: [source,python] ---- s = socket.socket (socket_family, socket_type, protocol=0) ---- Aqui está a descrição dos parâmetros: `socket_family: socket.AF_INET, PF_PACKET` **AF_INET** é a família de endereços para **IPv4**. **PF_PACKET** opera na camada de driver de dispositivo. A biblioteca pcap para Linux usa **PF_PACKET**. Você verá mais detalhes sobre **PF_PACKET** no Capítulo 3, Snifing e Penetration Testing. Esses argumentos representam as famílias de endereços eo protocolo da camada de transporte: `Socket_type : socket.SOCK_DGRAM, socket.SOCK_RAW,socket.SOCK_STREAM` O argumento **socket.SOCK_DGRAM** descreve que UDP é confiável e sem conexão, e **socket.SOCK_STREAM** descreve que o TCP é confiável e é um serviço de conexão bidirecional. Discutiremos **socket.SOCK_RAW** no Capítulo 3, Snifing e Penetration Testing. `protocol` Geralmente, deixamos esse argumento; recebe 0 se não for especificado. Veremos o uso deste argumento no Capítulo 3, Sniffing and Penetration Testing. == Server socket methods * `socket.bind(address)`: Este método é usado para conectar o endereço(endereço IP, número da porta) ao socket. O socket deve estar aberto antes de se conectar ao endereço. * `socket.listen(q)`: Este método inicia a esculta TCP. O argumento q define o número máximo de conexões line-up. * `socket.accept()`: Usado para aceitar a conexão do cliente. Antes de usar este método, os métodos `socket.bind(address)` e `socket.listen(q)` devem ser usados. O método `socket.accept()` retorna dois valores: `client_socket` e `address`, onde `client_socket` é um novo objeto socket usado para enviar e receber dados através da conexão e o address é o endereço do cliente. Você verá exemplos mais tarde. == Client socket methods O único método dedicado ao cliente é o: * `socket.connect(address)`: Este método conecta o cliente ao servidor. O argumento `address` é o endereço do servidor. == General socket methods * `socket.recv(bufsize)`: Esse método recebe uma mensagem TCP do socket. O argumento `bufsize` define o máximo de dados que ele pode receber. * `socket.recvfrom(bufsize)`: Esse método recebe dados do socket. O método retorna um par de valores: o primeiro valor são dados recebidos, e o segundo valor é o endereço so socket que envia os dados. * `socket.recv_into(buffer)`: Este método recebe dados menores ou iguais a `buffer`. O parâmetro `buffer` é criado pelo método `bytearray()`. Discutiremos isso em um exemplo mais adiante. * `socket.recvfrom_into(buffer)`: Esse método obtém dados do socket e os gravam no buffer. É retornado dois valores(nbytes, address), onde `nbytes` é o número de bytes recebidos e `address` é o endereço do socket que envia os dados * `socket.send(bytes)`: Este método é usado para enviar dados para o socket. Antes de enviar os dados, verifique se o socket está conectado a uma máquina remota. Retorna o número de bytes enviados. * `socket.sendto(data, address)`: Este método é utilizado para enviar dados(parâmetro data) para o socket. Geralmente, usamos esse método em UDP. UDP é um protocolo sem conexão; Portanto, o socket não deve ser conectado a uma máquina remota, e no argumento address especifique o endereço da máquina remota. O valor de retorno fornece o número de bytes enviados. * `socket.sendall(data)`: Como o nome indica, este método envia todos os dados para o socket. Antes de enviar os dados, verifique se o socket está conectado a uma máquina remota. Esse método transfere incessantemente dados até que um erro seja encontrado. Se um erro for encontrado, uma exceção é lançada, e `socket.close()` fecha o socket. Então vamos a prática! == Moving on to the practical Primeiro, vamos fazer um programa server-side que oferece uma conexão com o cliente e envia uma mensagem para o mesmo. Crie o arquivo `server1.py` e adicione o seguinte código: [source,python] ---- import socket host = "192.168.1.2" #Server address port = 12345 #Port of Server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host,port)) #bind server s.listen(2) conn, addr = s.accept() print(addr, "Now Connected") conn.send("BSD is Life") conn.close() ---- O código precedente é muito simples; É o código mínimo no lado do servidor. Primeiro, importe o módulo socket e defina o host e o número da porta: **192.168.1.2** é o endereço IP do servidor. `socket.AF_INET` define a família do protocolo IPv4. `Socket.SOCK_STREAM` define a conexão TCP. A instrução `s.bind((host, port))` leva apenas um argumento. Liga(bind) o socket ao host e número de porta. A instrução `s.listen(2)` escuta a conexão e aguarda o cliente. A instrução `conn, addr = s.accept()` retorna dois valores: **conn** e **addr**. O socket **conn** é o socket cliente, como discutimos anteriormente. A função `conn.send()` envia a mensagem para o cliente. Finalmente, `conn.close()` fecha o socket. A partir dos exemplos a seguir com capturas de tela, você entenderá melhor o `conn`. Saída do programa server1.py: `$ python server1.py ` Agora o servidor está em listening(modo de escuta) aguardando o cliente: Dê uma olhada no código do client-side. Crie o arquivo client1.py e adicione as seguintes linhas: [source,python] ---- import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "192.168.1.2" # server address port =12345 #server port s.connect((host,port)) print s.recv(1024) s.send("Hello Server") s.close() ---- No código anterior, vimos dois métodos novos: `s.connect((host, port))`, que conecta o cliente ao servidor, e `s.recv(1024)`, que recebe as strings enviadas pelo servidor. A saída do `client.py` e a resposta do servidor são mostradados na seguinte captura de tela: image::http://i.imgur.com/iUgdChG.png[img1] A saída mostra que o servidor aceitou a conexão de `192.168.1.5`. Não se confunda com a porta 42000; É a porta aleatória do cliente. Quando o servidor envia uma mensagem para o cliente, ele usa o socket conn, como mencionado anteriormente, e este socket contém o endereço IP do cliente e o número da porta. Como vimos na imagem acima, o servidor está no modo de escuta e o cliente se conecta ao servidor. Quando você executar o servidor/cliente novamente, a porta aleatória será alterada. Para o cliente, a porta do servidor **12345** é a porta de destino, e para o servidor, a porta aleatória do cliente **42000** é a porta de destino. Você pode estender a funcionalidade do servidor usando o loop while, conforme mostrado no seguinte programa. Execute o programa `server2.py`: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host,port)) s.listen(2) while True: conn, addr = s.accept() print(addr, "Conexão bem sucedida") conn.send("BSD é VIDA!") conn.close() ---- O código anterior é o mesmo que o primeiro que vimos, apenas adicionamos um loop `while` infinito. Execute o programa `server2.py` em um host(no meu caso um FreeBSD), e o `client1.py` em outro host(no meu caso o Fedora). Observe o output(saída) do server2.py: image::http://i.imgur.com/Uq2nSfH.png[img2] Um servidor pode responder muitos clientes. O loop while mantém o programa do servidor vivo e não permite que o código pare de rodar. Você pode definir um limite de conexão para o loop while; Por exemplo, defina `while i > 10` e incremente 1 em i(i += 1) a cada conexão. Antes de prosseguir para o próximo exemplo, o conceito de `bytearray` deve ser entendido. O array `bytearray` é uma sequência mutável de inteiros unsigned no intervalo de 0 a 255. Você pode excluir, inserir ou substituir valores ou pedaços isolados. Os objetos do array `bytearray` podem ser criados chamando a matriz interna `bytearray`. A sintaxe geral de bytearray é: `bytearray([source[, encoding[, errors]]])` Vejamos um exemplo: ``` >>> m = bytearray("Heise Berg") >>> m[1] 101 >>> m[0] 72 >>> m[:5] = "Ollar" >>> m bytearray(b'Ollar Berg') >>> ``` Isto é um exemplo de slicing no `bytearray()`. Agora, vejamos a operação splitting no bytearray(): ``` >>> m = bytearray("Hello Heise") >>> m bytearray(b'Hello Heise') >>> m.split() [bytearray(b'Hello'), bytearray(b'Heise')] ``` Em seguida temos a operação append no bytearray(): ``` >>> m.append(33) >>> m bytearray(b'Hello Heise!') >>> ``` No próximo exemplo utilizaremos `s.recv_into(buff)`. Neste exemplo, usaremos `bytearray()` para criar um buffer de armazenamento de dados. Primeiro, execute o código server-side. segue abaixo o código do `server3.py`: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host, port)) s.listen(1) conn, addr = s.accept() print("conectado em", addr) conn.send("Thanks") conn.close() ---- O programa anterior é o mesmo que vimos anteriormente. Neste programa, o servidor envia Thanks, seis caracteres. Agora execute o seguinte programa client-side: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) buf = bytearray("-" * 30) # buffer criado print("Numero de bytes ",s.recv_into(buf)) print(buf) s.close ---- No programa anterior, um parâmetro `buf` é criado usando `bytearray()`. A instrução `s.recv_into(buf)` fornece o número de bytes recebidos. O parâmetro `buf` nos dá a string recebida. Veja as imagens do exemplo apresentado: image::http://i.imgur.com/T0Fw6V8.png[img3] Nosso client recebeu com sucesso uma string de 6 bytes, `Thanks`. Agora, você já tem uma noção sobre `bytearray()`. Espero que você se lembre. Desta vez vou criar um socket UDP Execute `udp1.py`, e em seguida vamos discutir o código linha por linha: [source,python] ---- import socket host = "192.168.1.2" port = 12346 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((host,port)) data, addr = s.recvfrom(1024) print("received from ",addr) print("obtained ", data) s.close() ---- `socket.SOCK_DGRAM` cria um socket UDP, e `data, addr = s.recvfrom(1024)` retornam duas coisas: primeiro são os dados e a segunda é o endereço da fonte. Agora, veja o client-side. Execute `udp2.py`: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) print(s.sendto("hello all",(host,port))) s.close() ---- Aqui, eu usei o socket UDP e o método `s.sendto()`, como você pode ver na definição do `socket.sendto()`. Você sabe muito bem que UDP é um protocolo sem conexão, então não há necessidade de estabelecer uma conexão aqui. A seguinte captura de tela mostra a saída de udp1.py(o servidor UDP) e udp2.py(o cliente UDP): image::http://i.imgur.com/7gmjvoa.png[img4] O server recebeu os dados com êxito. Vamos supor que um servidor está em execução e não há nenhuma conexão inicial com o cliente, pelo menos que ele tenha ouvido. Para evitar essa situação, use o `socket.timeout(value)`. Geralmente, passamos um valor inteiro; Se eu passo 5 como o valor, significa que ele vai esperar por 5 segundos. Se a operação não for concluída dentro de 5 segundos, será lançada uma exceção de tempo limite. Você também pode fornecer um valor do tipo float não-negativo. Por exemplo, vamos olhar para o seguinte código: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((host,port)) s.settimeout(5) data, addr = s.recvfrom(1024) print("recevied from ",addr) print("obtained ", data) s.close() ---- Eu adicionei uma linha extra, ou seja, `s.settimeout(5)`. O programa espera por 5 segundos; caso não haja conexões nesse intervalo, ele lança um erro. Execute udptime1.py. A saída é mostrada na seguinte imagem: image::http://i.imgur.com/d52rKiK.png[img5] O programa mostra um erro; tudo indica que ele não é um bom programa, pois seu erro é obscuro. O programa deve ser capaz manipular as exceções. === Socket exceptions Para lidar com exceções, usaremos os blocos `try` e `except`. O próximo exemplo lhe dirá como lidar com as exceções. Execute `udptime2.py`: [source,python] ---- import socket host = "192.168.1.3" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.bind((host,port)) s.settimeout(5) data, addr = s.recvfrom(1024) print("recevied from ",addr) print("obtained ", data) s.close() except socket.timeout : print("Client not connected") s.close() ---- A saída é mostrada na seguinte imagem: image::http://i.imgur.com/EPyZ4zu.png[img6] No bloco try, eu coloco o meu código; no except, uma mensagem personalizada para ser exibida caso ocorra alguma exception. Diferentes tipos de exceções são definidos na biblioteca sockets do Python para diferentes erros. Vejamos algumas: * `exception socket.herror`: Este bloco detecta erros relacionados à endereços. * `exception socket.timeout`: Este bloco captura uma exceção quando o tempo limite de esculta do socket expirar. No exemplo anterior você pode ver que usamos `socket.timeout`. * `exception socket.gaierror`: Este bloco captura qualquer exceção que é gerada devido a `getaddrinfo()` e `getnameinfo()`. * `exception socket.error`: Este bloco detecta quaisquer erros relacionados a socket. Se você não tiver certeza sobre qualquer exceção lançar, você pode usá-lo. Em outras palavras, você pode dizer que é um bloco genérico que pega qualquer tipo de exceção. === Useful socket methods Você ganhou conhecimento sobre socket e sobre a arquitetura cliente-servidor. E você já é capaz de fazer um pequeno software networking. No entanto, o objetivo deste livro é testar e coletar informações em alguma rede. Python pode fazer isso de maneira elegante, ele possuí métodos úteis para coleta informações. Primeiro, `import socket` e use os seguintes métodos: * `socket.gethostbyname(hostname)`: Este método recebe um hostname como parâmetro e retorna um endereço IPv4. O endereço IPv4 é retornado na forma de string. Aqui está um exemplo: ``` >>> import socket >>> socket.gethostbyname('thapar.edu') '220.227.15.55' >>> >>> socket.gethostbyname('google.com') '173.194.126.64' >>> ``` Eu sei que você está pensando sobre o comando **nslookup**. Mais tarde, você verá mais bruxarias. * `socket.gethostbyname_ex (name)`: Este método converte um nome de host para um endereço IPv4 padrão. No entanto, a vantagem sobre o método anterior é que ele dá todos os endereços IP do nome de domínio. Ele retorna uma tupla (hostname, canonical name, and IP_addrlist) onde hostmane é dado por nós, canonical name é uma lista(possivelmente vazia) de nomes de host canônicos do servidor para o mesmo endereço e IP_addrlist é uma lista de todos os IPs disponíveis no mesmo hostname. Muitas vezes, um nome de domínio é hospedado em muitos endereços IP para equilibrar a carga do servidor. Infelizmente, este método não funciona para IPv6. Espero que você esteja bem familiarizado com tuplas, listas e dicionários. Vejamos um exemplo: ``` >>> import socket >>> socket.gethostbyname_ex('pt.org.br') ('pt.org.br', [], ['199.83.132.133', '199.83.128.133']) >>> >>> socket.gethostbyname_ex('google.com') ('google.com', [], ['172.217.29.78']) >>> >>> socket.gethostbyname_ex('facebook.com') ('facebook.com', [], ['31.13.85.36']) >>> ``` Ele retorna muitos endereços IP (ou não) de um único nome de domínio. Isso significa que um domínio como `pt.org.br` é executado em dois IPs. * `socket.gethostname()`: Isso retorna o hostname do sistema onde o interpretador Python está sendo executado atualmente: ``` >>> socket.gethostname() 'dhcppc0' >>> ``` Para obter o endereço IP da máquina atual usando o módulo socket, você pode usar o seguinte truque executando `socket.gethostbyname(socket.gethostname())`: ``` >>> socket.gethostbyname(socket.gethostname()) '200.148.457.114' >>> ``` Você sabe que o nosso computador tem muitas interfaces. Se você quiser saber o endereço IP de todas as interfaces, use a interface estendida: ``` >>> socket.gethostbyname_ex(socket.gethostname()) ('gvt.com.br', ['dhcppc0.gvt.com.br'], ['134.456.457.171']) >>> ``` Ele retorna uma tupla contendo três elementos: primeiro é o nome da máquina, segundo é uma lista de aliases para o nome do host e terceiro é a lista de endereços IP das interfaces(no meu caso só tenho uma). * `socket.getfqdn([name])`: Isso é usado para encontrar o fully qualified name, se estiver disponível. O fully qualified name consiste em um host e um nome de domínio; Por exemplo, beta pode ser o nome do host, e example.com pode ser o nome do domínio. O **fully qualified domain name(FQDN)** torna-se beta. example.com: ``` >>> socket.getfqdn('facebook.com') 'edge-star-mini6-shv-01-gru2.facebook.com' >>> ``` No exemplo anterior, edge-star-mini6-shv-01-gru2 é o nome do host, e facebook.com é o nome do domínio. No exemplo a seguir, FQDN não está disponível para thapar.edu: ``` >>> socket.getfqdn('thapar.com') 'thapar.com' >>> ``` Se o argumento name estiver em branco, ele retornará o nome da máquina atual: ``` >>> socket.getfqdn() 'gvt.com.br' ``` * `socket.gethostbyaddr(ip_address)`: Isso é como um "reverse" lookup para o nome. Ele retorna uma tupla (hostname, canonical name, and IP_addrlist) onde hostname é o nome do host que responde ao dado ip_address, o canonical name é uma lista (possivelmente vazia) de nomes canônicos do mesmo endereço e IP_addrlist é uma lista de endereços IP para a mesma interface de rede no mesmo host: ``` >>> socket.gethostbyaddr('31.13.85.36') ('edge-star-mini-shv-01-gru2.facebook.com', [], ['31.13.85.36']) >>> >>> socket.gethostbyaddr('119.18.50.66') Traceback (most recent call last): File "<stdin>", line 1, in <module> socket.herror: [Errno 1] Unknown host >>> ``` Ele mostra um erro na última consulta porque o DNS lookup não está presente. * `socket.getservbyname(servicename[, protocol_name])`: Isso converte qualquer protocol name para o número da porta correspondente. O nome do protocolo é opcional, TCP ou UDP. Por exemplo, o serviço DNS usa TCP, bem como conexões UDP. Se o nome do protocolo não for fornecido, qualquer protocolo pode corresponder: ``` >>> socket.getservbyname('http') 80 >>> socket.getservbyname('smtp','tcp') 25 >>> ``` * `socket.getservbyport(port[, protocol_name])`: Isso converte um número de porta de um protocolo para o nome do serviço correspondente. O nome do protocolo é opcional, TCP ou UDP: ``` >>> socket.getservbyport(80) 'http' >>> socket.getservbyport(23) 'telnet' >>> socket.getservbyport(445) 'microsoft-ds' >>> ``` * `socket.connect_ex(address)`: Esse método retorna um indicador de erro. Se obter êxito. Ele `retorna 0`; Caso contrário, ele retorna a variável `errno`. Você pode aproveitar esta função para escanear as portas. Execute o programa `connet_ex.py`: [source,python] ---- import socket rmip ='127.0.0.1' portlist = [22,23,80,912,135,445,20] for port in portlist: sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM) result = sock.connect_ex((rmip,port)) print(port,":", result) sock.close() ---- A saída é mostrada na seguinte imagem: image::http://i.imgur.com/jZgqJNH.png[img7] A saída do programa anterior mostra que somente a porta 22 está aberta. Este é um scanner de porta rudimentar. Caso não encontre portas abertas, execute isso em outro dispositivo com uma grande lista de portas. Desta vez você terá que usar `socket.settimeout(value)`: `socket.getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]])` Este método socket converte os argumentos host e port em uma seqüência de cinco tuplas. Dê uma olhada no exemplo: ``` >>> socket.getaddrinfo('www.thapar.edu', 'http') [(2, 1, 6, '', ('220.227.15.49', 80)), (2, 2, 17, '', ('220.227.15.49', 80))] >>> ``` O 2 representa a família, 1 representa o tipo de socket, 6 representa o protocolo, ' ' representa o canonical name e ('220.227.15.49', 80) representa o endereço dos dois sockets. No entanto, este número é difícil de compreender. Navegue para onde estão seus arquivos de exemplos. Use o código a seguir para obter um resultado mais legível. `getadd1.py`: ``` import socket def get_protnumber(prefix): return dict( (getattr(socket, a), a) for a in dir(socket) if a.startswith(prefix)) proto_fam = get_protnumber('AF_') types = get_protnumber('SOCK_') protocols = get_protnumber('IPPROTO_') for res in socket.getaddrinfo('www.thapar.edu', 'http'): family, socktype, proto, canonname, sockaddr = res print('Family :', proto_fam[family]) print('Type :', types[socktype]) print('Protocol :', protocols[proto]) print('Canonical name :', canonname) print('Socket address :', sockaddr) print('--------------------------------------------') ``` image::http://i.imgur.com/k5Uus10.png[img7] A primeira parte do código faz um dicionário usando `AF_, SOCK_` e `IPPROTO_prefixes` que mapeia o número do protocolo para seus respectivos nomes. Este dicionário é formado pela técnica he list comprehension. O código dentro da função(get_protnumber(prefix)) do código acima pode parecer um pouco confusa, mas podemos executá-la separadamente da seguinte maneira: ``` >>> dict(( getattr(socket,n),n) for n in dir(socket) if n.startswith('AF_')) {0: 'AF_UNSPEC', 2: 'AF_INET', 6: 'AF_IPX', 11: 'AF_SNA', 12: 'AF_ DECnet', 16: 'AF_APPLETALK', 23: 'AF_INET6', 26: 'AF_IRDA'} ``` Agora ficou mais simples de entender. Esse código é geralmente usado para obter o número do protocolo: `for res in socket.getaddrinfo('www.thapar.edu', 'http'):` A linha de código anterior retorna os cinco valores, conforme discutido na definição. Esses valores são então combinados com seu dicionário correspondente.
  4. Mals por ter postado no local errado! Falta de atenção a minha. Desde de já, peço-lhes desculpas.
  5. ---_ ......._-_--. (|\ / / /| \ \ / / .' -=-' `. / / .' ) _/ / .' _.) / / o o _.-' / .' \ _.-' / .'*| \______.-'// .'.' \*| \| \ | // .'.' _ |*| ` \|// .'.'_ _ _|*| . .// .'.' | _ _ \*| \`-|\_/ / \ _ _ \*\ `/'\__/ \ _ _ \*\ /^| \ _ _ \* ' ` \ _ _ \ ASH (+VK) \_ fonte: http://ascii.co.uk/art/snake = Python with Penetration Testing and Networking Penetration(pen) tester e hacker são termos semelhantes. A diferença é que pen-testers trabalham em uma organização para evitar às investidas de hackers, enquanto que um hacker geralmente trabalha para obter fama, vender vulnerabilidades por dinheiro, ou para explorar vulnerabilidades a fim de adiquirir algumas inimizades. Hackers bem treinados trabalham no campo de segurança da informação, invadindo um sistema e informando a vítima dos bugs de segurança para que eles possam ser corrigidos. Um hacker chamado de pentester é aquele que trabalha em uma empresa a fim de protejer seu sistema. Um pentester só inicia suas tentativas de quebrar a rede depois de obter a aprovação legal do cliente e, em seguida, apresenta um relatório de suas descobertas. Para se tornar um especialista em pentesting, uma pessoa deve ter profundo conhecimento dos conceitos de sua tecnologia. Neste capítulo, abordaremos os seguintes tópicos: • O escopo do pentesting • A necessidade do pentesting • Componentes a testar • Qualidades de um bom pentester • Abordagens do pentesting • Compreendendo os testes e ferramentas que você precisará • Network sockets • Métodos server socket • Métodos client socket • Métodos general socket • Exemplos práticos com socket • Socket exceptions • Métodos úteis do socket == Introducing the scope of pentesting Em simples palavras, penetration testing é o teste que avalia as políticas de segurança da informação de uma empresa. As medidas de segurança da informação envolvem a rede da empresa, banco de dados, site, servidores públicos e tudo o mais especificado pelo cliente. No final do dia, um pentester deve apresentar um relatório detalhado de suas descobertas, tais como instabilidades, vulnerabilidade na infra-estrutura da empresa, e o nível de risco da vulnerabilidade em particular, e fornecer soluções, se possível. === The need for pentesting Existem vários pontos que descrevem o significado do pentesting: * O pentesting identifica as ameaças que podem expor a confidencialidade de uma organização * Um Expert em pentesting fornece garantia para a organização com uma avaliação completa e detalhada da segurança organizacional * O pentesting avalia a eficiência da rede, produzindo uma enorme quantidade de tráfego e examina a segurança de dispositivos como firewalls, roteadores e switches * Alterar ou atualizar a infra-estrutura de software existente, hardware ou design de rede pode levar a vulnerabilidades que podem ser detectadas por pentesting * No mundo de hoje, as ameaças potenciais estão aumentando significativamente; Pentesting é um exercício proativo para minimizar a chance de ser "exploitado" * O Pentesting garante se as políticas de segurança adequadas estão sendo seguidas ou não Considere um exemplo de um e-commerce de nome que que gera sua renda com um serviço on-line. Um hacker ou grupo de hackers black hat encontram uma vulnerabilidade no site da empresa e a explora. Pode ser devastador a quantidade de prejuízo que eles podem causar. === Components to be tested Uma organização deve realizar uma operação de avaliação de risco antes do pentesting; isso ajudará a identificar as principais ameaças, como a má configuração ou vulnerabilidades em: * Roteadores, switches, ou gateways * Sistemas voltados para o público; Sites, DMZ, servidores de e-mail e sistemas remotos * DNS, firewalls, servidores proxy, FTP, e servidores web O teste de segurança deve ser executado em todos os componentes de hardware e software de um sistema em rede. === Qualities of a good pentester Os pontos a seguir descrevem as qualidades de um bom pentester. Ele deve: * Escolher um conjunto adequado de testes e ferramentas que equilibram custos e benefícios * Seguir procedimentos adequados com planejamento e documentação adequada * Estabelecer o escopo de cada teste, tais como objetivos, limitações e justificativa de procedimentos * Estar pronto para mostrar como explorar as vulnerabilidades * Indicar claramente os potenciais riscos achados no relatório final e fornecer métodos para a correção dos mesmos, se possível * Manter-se sempre atualizado porque a tecnologia está em constante evolução Um pentester testa a rede usando técnicas manuais ou ferramentas relevantes. Existem muitas ferramentas disponíveis no mercado. Algumas delas são de código aberto; outras muito caras. Com a ajuda da programação, você pode fazer suas próprias ferramentas. Ao criar suas próprias ferramentas, você pode esclarecer seus conceitos e também executar mais "R&D". Se você está interessado em pentesting e quer fazer suas próprias ferramentas, então a linguagem de programação Python é a melhor, pois conta com uma ampla variedade de pacotes para pentesting e é muito simples programar com ela. Esta simplicidade, juntamente com as bibliotecas de terceiros, tais como scapy e mechanize, reduz o tamanho do código. Em Python, para fazer um programa, você não precisa definir classes "gordas" como no Java. É mais produtivo escrever código em Python do que em C, e existem bibliotecas de alto nível para fazer praticamente qualquer tarefa imaginável. Se você já sabe um pouco sobre programação com Python e está interessado em pentesting este livro é ideal para você. === Deining the scope of pentesting Antes de entrar na fase do pentesting, o escopo do pentesting deve ser concebido. Os seguintes pontos devem ser considerados enquanto definimos o escopo: * Você deve desenvolver o escopo do projeto em consulta com o cliente. Por exemplo, se Bob (o cliente) quiser testar toda a infra-estrutura de rede da organização, então a pentester Alyosha definirá o escopo do pentesting levando essa rede em consideração. Alyosha consultará Bob quando houver alguma área a mais que precisa ser analizada * Você deve levar em conta o tempo, as pessoas e o dinheiro. * Você deve perfilar os limites do teste com base em um contrato assinado pelo pentester e pelo cliente. * As mudanças nas práticas da empresa podem afetar o escopo. Por exemplo, a adição de uma sub-rede, novas instalações de componentes, a adição ou modificação de um servidor web, e assim por diante, pode alterar o escopo do pentesting. O escopo do pentesting é definido em dois tipos de testes: * **Teste não destrutivo**: Este teste é limitado a encontrar e executar os testes sem riscos potenciais. Ele executa as seguintes ações: ** Escaneia e identifica potenciais vulnerabilidades no sistema remoto ** Investiga e verifica as descobertas ** Mapeia as vulnerabilidades com exploits apropriados ** Exploita o sistema remoto com o devido cuidado para evitar interrupções ** Fornece uma prova de conceito ** Não efetua um ataque de negação de serviço(DoS) * **Teste destrutivo**: Este teste pode produzir riscos. Ele executa as seguintes ações: ** Efetua ataques DoS e buffer overflows, que tem o potêncial de derrubar o sistema == Approaches to pentesting Existem três tipos de abordagens para pentesting: * **Black-box pentesting** segue a abordagem não-determinística de testes ** Você receberá apenas um nome de empresa ** Você deve agir - entre aspas - como um black hat(sem conhecimento prévio da infra) ** Não há necessidade de qualquer conhecimento prévio do sistema ** É demorado * **White-box pentesting** segue a abordagem determinista de testes ** Você receberá o conhecimento completo da infra-estrutura que precisa ser testada ** Serão fornecidas informações sobre a infra-estrutura da empresa, o tipo de rede, as políticas da empresa, os tópicos a fazer e a não fazer, o endereço IP e o firewall IPS/IDS * **Grey-box pentesting** segue uma abordagem híbrida de ambas as abordagens citadas acima: ** O pentester geralmente tem informações limitadas sobre a rede/sistema que é fornecido pelo cliente para reduzir os custos e diminuir tentativa e erro por parte do pentester ** Ele realiza a avaliação de segurança e os testes internamente == Introducing Python scripting Antes de começar a ler este livro, você deve conhecer os conceitos básicos da programação em Python, como a sintaxe básica, os tipos de variáveis, a estrutura de dado tupla, o dicionário, as funções, as strings, os métodos e assim por diante. Duas versões, 3.4 e 2.7.8, estão disponíveis em python.org/downloads/. Neste livro, todas as experiências e demonstrações foram feitas em Python versão 2.7.8. Se você usa o sistema operacional Linux como Kali ou BackTrack, então não haverá nenhum problema, porque muitos programas, como snifing wireless, não funcionam na plataforma Windows. O Kali Linux também usa a versão 2.7. Se você gosta de trabalhar no Red Hat ou CentOS, então esta versão é adequada para você A maioria dos hackers escolhem esta profissão porque eles não querem programar. Eles querem usar ferramentas. No entanto, sem programação, um hacker não pode melhorar suas habilidades. E de tempos em tempos eles procuram novas ferramentas na internet. Acredite em mim, depois de ver a sua simplicidade, você vai adorar esta linguagem. == Understanding the tests and tools you'll need Como você deve ter visto, este livro é dividido em sete capítulos. Para realizar varreduras e snifing pentesting, você precisará de uma pequena rede de dispositivos conectados. Se você não tem um laboratório, você pode fazer máquinas virtuais em seu computador. Para análise de tráfego sem fio, você deve ter uma rede sem fio. Para realizar um ataque na web, você precisará de um servidor Apache rodando na plataforma Linux. Será uma boa idéia usar CentOS ou Red Hat versão 5 ou 6 para o servidor web, pois contém os RPM's do Apache e PHP. Para o script Python, vamos usar a ferramenta Wireshark, que é de código aberto e pode ser executado no Windows, bem como plataformas Linux. == Learning the common testing platforms with Python Agora vamos começar a falar de pentesting; Espero que você esteja bem familiarizado com fundamentos de rede, como endereços IP, sub-redes classful, sub-redes sem classes, o significado das portas, endereços de rede e endereços de broadcast. Um pentester deve ser perfeito em fundamentos de redes, ou pelo menos entender bem um sistema operacional; Se você está pensando em usar o Linux, então você está no caminho certo. Neste livro, vamos executar nossos programas no Windows, bem como Linux. Neste livro, Windows, CentOS e Kali Linux serão usados. Um hacker sempre gosta de trabalhar em um sistema Linux. Como é livre e de código aberto, Kali Linux marca o renascimento do BackTrack e vem com um arsenal de ferramentas de hacking. Kali Linux NetHunter é a primeira plataforma de teste de penetração Android de código aberto para dispositivos Nexus. Algumas ferramentas funcionam no Linux e no Windows, mas no Windows, geralmente essas ferramentas não funcionam. Espero que você tenha conhecimento do Linux. Agora, é hora de trabalhar com redes em Python. == Network sockets Um network socket adress contém um endereço IP e um número de porta. De uma forma muito simples, um socket é uma maneira de falar com outros computadores. Por meio de um socket, um processo pode se comunicar com outro processo através da rede. Para criar um socket, use a função `socket.socket()` que está disponível no módulo socket. A sintaxe geral de uma função socket é a seguinte: [source,python] ---- s = socket.socket (socket_family, socket_type, protocol=0) ---- Aqui está a descrição dos parâmetros: `socket_family: socket.AF_INET, PF_PACKET` **AF_INET** é a família de endereços para **IPv4**. **PF_PACKET** opera na camada de driver de dispositivo. A biblioteca pcap para Linux usa **PF_PACKET**. Você verá mais detalhes sobre **PF_PACKET** no Capítulo 3, Snifing e Penetration Testing. Esses argumentos representam as famílias de endereços eo protocolo da camada de transporte: `Socket_type : socket.SOCK_DGRAM, socket.SOCK_RAW,socket.SOCK_STREAM` O argumento **socket.SOCK_DGRAM** descreve que UDP é confiável e sem conexão, e **socket.SOCK_STREAM** descreve que o TCP é confiável e é um serviço de conexão bidirecional. Discutiremos **socket.SOCK_RAW** no Capítulo 3, Snifing e Penetration Testing. `protocol` Geralmente, deixamos esse argumento; recebe 0 se não for especificado. Veremos o uso deste argumento no Capítulo 3, Sniffing and Penetration Testing. == Server socket methods * `socket.bind(address)`: Este método é usado para conectar o endereço(endereço IP, número da porta) ao socket. O socket deve estar aberto antes de se conectar ao endereço. * `socket.listen(q)`: Este método inicia a esculta TCP. O argumento q define o número máximo de conexões line-up. * `socket.accept()`: Usado para aceitar a conexão do cliente. Antes de usar este método, os métodos `socket.bind(address)` e `socket.listen(q)` devem ser usados. O método `socket.accept()` retorna dois valores: `client_socket` e `address`, onde `client_socket` é um novo objeto socket usado para enviar e receber dados através da conexão e o address é o endereço do cliente. Você verá exemplos mais tarde. == Client socket methods O único método dedicado ao cliente é o: * `socket.connect(address)`: Este método conecta o cliente ao servidor. O argumento `address` é o endereço do servidor. == General socket methods * `socket.recv(bufsize)`: Esse método recebe uma mensagem TCP do socket. O argumento `bufsize` define o máximo de dados que ele pode receber. * `socket.recvfrom(bufsize)`: Esse método recebe dados do socket. O método retorna um par de valores: o primeiro valor são dados recebidos, e o segundo valor é o endereço so socket que envia os dados. * `socket.recv_into(buffer)`: Este método recebe dados menores ou iguais a `buffer`. O parâmetro `buffer` é criado pelo método `bytearray()`. Discutiremos isso em um exemplo mais adiante. * `socket.recvfrom_into(buffer)`: Esse método obtém dados do socket e os gravam no buffer. É retornado dois valores(nbytes, address), onde `nbytes` é o número de bytes recebidos e `address` é o endereço do socket que envia os dados * `socket.send(bytes)`: Este método é usado para enviar dados para o socket. Antes de enviar os dados, verifique se o socket está conectado a uma máquina remota. Retorna o número de bytes enviados. * `socket.sendto(data, address)`: Este método é utilizado para enviar dados(parâmetro data) para o socket. Geralmente, usamos esse método em UDP. UDP é um protocolo sem conexão; Portanto, o socket não deve ser conectado a uma máquina remota, e no argumento address especifique o endereço da máquina remota. O valor de retorno fornece o número de bytes enviados. * `socket.sendall(data)`: Como o nome indica, este método envia todos os dados para o socket. Antes de enviar os dados, verifique se o socket está conectado a uma máquina remota. Esse método transfere incessantemente dados até que um erro seja encontrado. Se um erro for encontrado, uma exceção é lançada, e `socket.close()` fecha o socket. Então vamos a prática! == Moving on to the practical Primeiro, vamos fazer um programa server-side que oferece uma conexão com o cliente e envia uma mensagem para o mesmo. Crie o arquivo `server1.py` e adicione o seguinte código: [source,python] ---- import socket host = "192.168.1.2" #Server address port = 12345 #Port of Server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host,port)) #bind server s.listen(2) conn, addr = s.accept() print(addr, "Now Connected") conn.send("BSD is Life") conn.close() ---- O código precedente é muito simples; É o código mínimo no lado do servidor. Primeiro, importe o módulo socket e defina o host e o número da porta: **192.168.1.2** é o endereço IP do servidor. `socket.AF_INET` define a família do protocolo IPv4. `Socket.SOCK_STREAM` define a conexão TCP. A instrução `s.bind((host, port))` leva apenas um argumento. Liga(bind) o socket ao host e número de porta. A instrução `s.listen(2)` escuta a conexão e aguarda o cliente. A instrução `conn, addr = s.accept()` retorna dois valores: **conn** e **addr**. O socket **conn** é o socket cliente, como discutimos anteriormente. A função `conn.send()` envia a mensagem para o cliente. Finalmente, `conn.close()` fecha o socket. A partir dos exemplos a seguir com capturas de tela, você entenderá melhor o `conn`. Saída do programa server1.py: `$ python server1.py ` Agora o servidor está em listening(modo de escuta) aguardando o cliente: Dê uma olhada no código do client-side. Crie o arquivo client1.py e adicione as seguintes linhas: [source,python] ---- import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "192.168.1.2" # server address port =12345 #server port s.connect((host,port)) print s.recv(1024) s.send("Hello Server") s.close() ---- No código anterior, vimos dois métodos novos: `s.connect((host, port))`, que conecta o cliente ao servidor, e `s.recv(1024)`, que recebe as strings enviadas pelo servidor. A saída do `client.py` e a resposta do servidor são mostradados na seguinte captura de tela: image::http://i.imgur.com/iUgdChG.png[img1] A saída mostra que o servidor aceitou a conexão de `192.168.1.5`. Não se confunda com a porta 42000; É a porta aleatória do cliente. Quando o servidor envia uma mensagem para o cliente, ele usa o socket conn, como mencionado anteriormente, e este socket contém o endereço IP do cliente e o número da porta. Como vimos na imagem acima, o servidor está no modo de escuta e o cliente se conecta ao servidor. Quando você executar o servidor/cliente novamente, a porta aleatória será alterada. Para o cliente, a porta do servidor **12345** é a porta de destino, e para o servidor, a porta aleatória do cliente **42000** é a porta de destino. Você pode estender a funcionalidade do servidor usando o loop while, conforme mostrado no seguinte programa. Execute o programa `server2.py`: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host,port)) s.listen(2) while True: conn, addr = s.accept() print(addr, "Conexão bem sucedida") conn.send("BSD é VIDA!") conn.close() ---- O código anterior é o mesmo que o primeiro que vimos, apenas adicionamos um loop `while` infinito. Execute o programa `server2.py` em um host(no meu caso um FreeBSD), e o `client1.py` em outro host(no meu caso o Fedora). Observe o output(saída) do server2.py: image::http://i.imgur.com/Uq2nSfH.png[img2] Um servidor pode responder muitos clientes. O loop while mantém o programa do servidor vivo e não permite que o código pare de rodar. Você pode definir um limite de conexão para o loop while; Por exemplo, defina `while i > 10` e incremente 1 em i(i += 1) a cada conexão. Antes de prosseguir para o próximo exemplo, o conceito de `bytearray` deve ser entendido. O array `bytearray` é uma sequência mutável de inteiros unsigned no intervalo de 0 a 255. Você pode excluir, inserir ou substituir valores ou pedaços isolados. Os objetos do array `bytearray` podem ser criados chamando a matriz interna `bytearray`. A sintaxe geral de bytearray é: `bytearray([source[, encoding[, errors]]])` Vejamos um exemplo: ``` >>> m = bytearray("Heise Berg") >>> m[1] 101 >>> m[0] 72 >>> m[:5] = "Ollar" >>> m bytearray(b'Ollar Berg') >>> ``` Isto é um exemplo de slicing no `bytearray()`. Agora, vejamos a operação splitting no bytearray(): ``` >>> m = bytearray("Hello Heise") >>> m bytearray(b'Hello Heise') >>> m.split() [bytearray(b'Hello'), bytearray(b'Heise')] ``` Em seguida temos a operação append no bytearray(): ``` >>> m.append(33) >>> m bytearray(b'Hello Heise!') >>> ``` No próximo exemplo utilizaremos `s.recv_into(buff)`. Neste exemplo, usaremos `bytearray()` para criar um buffer de armazenamento de dados. Primeiro, execute o código server-side. segue abaixo o código do `server3.py`: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host, port)) s.listen(1) conn, addr = s.accept() print("conectado em", addr) conn.send("Thanks") conn.close() ---- O programa anterior é o mesmo que vimos anteriormente. Neste programa, o servidor envia Thanks, seis caracteres. Agora execute o seguinte programa client-side: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) buf = bytearray("-" * 30) # buffer criado print("Numero de bytes ",s.recv_into(buf)) print(buf) s.close ---- No programa anterior, um parâmetro `buf` é criado usando `bytearray()`. A instrução `s.recv_into(buf)` fornece o número de bytes recebidos. O parâmetro `buf` nos dá a string recebida. Veja as imagens do exemplo apresentado: image::http://i.imgur.com/T0Fw6V8.png[img3] Nosso client recebeu com sucesso uma string de 6 bytes, `Thanks`. Agora, você já tem uma noção sobre `bytearray()`. Espero que você se lembre. Desta vez vou criar um socket UDP Execute `udp1.py`, e em seguida vamos discutir o código linha por linha: [source,python] ---- import socket host = "192.168.1.2" port = 12346 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((host,port)) data, addr = s.recvfrom(1024) print("received from ",addr) print("obtained ", data) s.close() ---- `socket.SOCK_DGRAM` cria um socket UDP, e `data, addr = s.recvfrom(1024)` retornam duas coisas: primeiro são os dados e a segunda é o endereço da fonte. Agora, veja o client-side. Execute `udp2.py`: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) print(s.sendto("hello all",(host,port))) s.close() ---- Aqui, eu usei o socket UDP e o método `s.sendto()`, como você pode ver na definição do `socket.sendto()`. Você sabe muito bem que UDP é um protocolo sem conexão, então não há necessidade de estabelecer uma conexão aqui. A seguinte captura de tela mostra a saída de udp1.py(o servidor UDP) e udp2.py(o cliente UDP): image::http://i.imgur.com/7gmjvoa.png[img4] O server recebeu os dados com êxito. Vamos supor que um servidor está em execução e não há nenhuma conexão inicial com o cliente, pelo menos que ele tenha ouvido. Para evitar essa situação, use o `socket.timeout(value)`. Geralmente, passamos um valor inteiro; Se eu passo 5 como o valor, significa que ele vai esperar por 5 segundos. Se a operação não for concluída dentro de 5 segundos, será lançada uma exceção de tempo limite. Você também pode fornecer um valor do tipo float não-negativo. Por exemplo, vamos olhar para o seguinte código: [source,python] ---- import socket host = "192.168.1.2" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((host,port)) s.settimeout(5) data, addr = s.recvfrom(1024) print("recevied from ",addr) print("obtained ", data) s.close() ---- Eu adicionei uma linha extra, ou seja, `s.settimeout(5)`. O programa espera por 5 segundos; caso não haja conexões nesse intervalo, ele lança um erro. Execute udptime1.py. A saída é mostrada na seguinte imagem: image::http://i.imgur.com/d52rKiK.png[img5] O programa mostra um erro; tudo indica que ele não é um bom programa, pois seu erro é obscuro. O programa deve ser capaz manipular as exceções. === Socket exceptions Para lidar com exceções, usaremos os blocos `try` e `except`. O próximo exemplo lhe dirá como lidar com as exceções. Execute `udptime2.py`: [source,python] ---- import socket host = "192.168.1.3" port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.bind((host,port)) s.settimeout(5) data, addr = s.recvfrom(1024) print("recevied from ",addr) print("obtained ", data) s.close() except socket.timeout : print("Client not connected") s.close() ---- A saída é mostrada na seguinte imagem: image::http://i.imgur.com/EPyZ4zu.png[img6] No bloco try, eu coloco o meu código; no except, uma mensagem personalizada para ser exibida caso ocorra alguma exception. Diferentes tipos de exceções são definidos na biblioteca sockets do Python para diferentes erros. Vejamos algumas: * `exception socket.herror`: Este bloco detecta erros relacionados à endereços. * `exception socket.timeout`: Este bloco captura uma exceção quando o tempo limite de esculta do socket expirar. No exemplo anterior você pode ver que usamos `socket.timeout`. * `exception socket.gaierror`: Este bloco captura qualquer exceção que é gerada devido a `getaddrinfo()` e `getnameinfo()`. * `exception socket.error`: Este bloco detecta quaisquer erros relacionados a socket. Se você não tiver certeza sobre qualquer exceção lançar, você pode usá-lo. Em outras palavras, você pode dizer que é um bloco genérico que pega qualquer tipo de exceção. === Useful socket methods Você ganhou conhecimento sobre socket e sobre a arquitetura cliente-servidor. E você já é capaz de fazer um pequeno software networking. No entanto, o objetivo deste livro é testar e coletar informações em alguma rede. Python pode fazer isso de maneira elegante, ele possuí métodos úteis para coleta informações. Primeiro, `import socket` e use os seguintes métodos: * `socket.gethostbyname(hostname)`: Este método recebe um hostname como parâmetro e retorna um endereço IPv4. O endereço IPv4 é retornado na forma de string. Aqui está um exemplo: ``` >>> import socket >>> socket.gethostbyname('thapar.edu') '220.227.15.55' >>> >>> socket.gethostbyname('google.com') '173.194.126.64' >>> ``` Eu sei que você está pensando sobre o comando **nslookup**. Mais tarde, você verá mais bruxarias. * `socket.gethostbyname_ex (name)`: Este método converte um nome de host para um endereço IPv4 padrão. No entanto, a vantagem sobre o método anterior é que ele dá todos os endereços IP do nome de domínio. Ele retorna uma tupla (hostname, canonical name, and IP_addrlist) onde hostmane é dado por nós, canonical name é uma lista(possivelmente vazia) de nomes de host canônicos do servidor para o mesmo endereço e IP_addrlist é uma lista de todos os IPs disponíveis no mesmo hostname. Muitas vezes, um nome de domínio é hospedado em muitos endereços IP para equilibrar a carga do servidor. Infelizmente, este método não funciona para IPv6. Espero que você esteja bem familiarizado com tuplas, listas e dicionários. Vejamos um exemplo: ``` >>> import socket >>> socket.gethostbyname_ex('pt.org.br') ('pt.org.br', [], ['199.83.132.133', '199.83.128.133']) >>> >>> socket.gethostbyname_ex('google.com') ('google.com', [], ['172.217.29.78']) >>> >>> socket.gethostbyname_ex('facebook.com') ('facebook.com', [], ['31.13.85.36']) >>> ``` Ele retorna muitos endereços IP (ou não) de um único nome de domínio. Isso significa que um domínio como `pt.org.br` é executado em dois IPs. * `socket.gethostname()`: Isso retorna o hostname do sistema onde o interpretador Python está sendo executado atualmente: ``` >>> socket.gethostname() 'dhcppc0' >>> ``` Para obter o endereço IP da máquina atual usando o módulo socket, você pode usar o seguinte truque executando `socket.gethostbyname(socket.gethostname())`: ``` >>> socket.gethostbyname(socket.gethostname()) '200.148.457.114' >>> ``` Você sabe que o nosso computador tem muitas interfaces. Se você quiser saber o endereço IP de todas as interfaces, use a interface estendida: ``` >>> socket.gethostbyname_ex(socket.gethostname()) ('gvt.com.br', ['dhcppc0.gvt.com.br'], ['134.456.457.171']) >>> ``` Ele retorna uma tupla contendo três elementos: primeiro é o nome da máquina, segundo é uma lista de aliases para o nome do host e terceiro é a lista de endereços IP das interfaces(no meu caso só tenho uma). * `socket.getfqdn([name])`: Isso é usado para encontrar o fully qualified name, se estiver disponível. O fully qualified name consiste em um host e um nome de domínio; Por exemplo, beta pode ser o nome do host, e example.com pode ser o nome do domínio. O **fully qualified domain name(FQDN)** torna-se beta. example.com: ``` >>> socket.getfqdn('facebook.com') 'edge-star-mini6-shv-01-gru2.facebook.com' >>> ``` No exemplo anterior, edge-star-mini6-shv-01-gru2 é o nome do host, e facebook.com é o nome do domínio. No exemplo a seguir, FQDN não está disponível para thapar.edu: ``` >>> socket.getfqdn('thapar.com') 'thapar.com' >>> ``` Se o argumento name estiver em branco, ele retornará o nome da máquina atual: ``` >>> socket.getfqdn() 'gvt.com.br' ``` * `socket.gethostbyaddr(ip_address)`: Isso é como um "reverse" lookup para o nome. Ele retorna uma tupla (hostname, canonical name, and IP_addrlist) onde hostname é o nome do host que responde ao dado ip_address, o canonical name é uma lista (possivelmente vazia) de nomes canônicos do mesmo endereço e IP_addrlist é uma lista de endereços IP para a mesma interface de rede no mesmo host: ``` >>> socket.gethostbyaddr('31.13.85.36') ('edge-star-mini-shv-01-gru2.facebook.com', [], ['31.13.85.36']) >>> >>> socket.gethostbyaddr('119.18.50.66') Traceback (most recent call last): File "<stdin>", line 1, in <module> socket.herror: [Errno 1] Unknown host >>> ``` Ele mostra um erro na última consulta porque o DNS lookup não está presente. * `socket.getservbyname(servicename[, protocol_name])`: Isso converte qualquer protocol name para o número da porta correspondente. O nome do protocolo é opcional, TCP ou UDP. Por exemplo, o serviço DNS usa TCP, bem como conexões UDP. Se o nome do protocolo não for fornecido, qualquer protocolo pode corresponder: ``` >>> socket.getservbyname('http') 80 >>> socket.getservbyname('smtp','tcp') 25 >>> ``` * `socket.getservbyport(port[, protocol_name])`: Isso converte um número de porta de um protocolo para o nome do serviço correspondente. O nome do protocolo é opcional, TCP ou UDP: ``` >>> socket.getservbyport(80) 'http' >>> socket.getservbyport(23) 'telnet' >>> socket.getservbyport(445) 'microsoft-ds' >>> ``` * `socket.connect_ex(address)`: Esse método retorna um indicador de erro. Se obter êxito. Ele `retorna 0`; Caso contrário, ele retorna a variável `errno`. Você pode aproveitar esta função para escanear as portas. Execute o programa `connet_ex.py`: [source,python] ---- import socket rmip ='127.0.0.1' portlist = [22,23,80,912,135,445,20] for port in portlist: sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM) result = sock.connect_ex((rmip,port)) print(port,":", result) sock.close() ---- A saída é mostrada na seguinte imagem: image::http://i.imgur.com/jZgqJNH.png[img7] A saída do programa anterior mostra que somente a porta 22 está aberta. Este é um scanner de porta rudimentar. Caso não encontre portas abertas, execute isso em outro dispositivo com uma grande lista de portas. Desta vez você terá que usar `socket.settimeout(value)`: `socket.getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]])` Este método socket converte os argumentos host e port em uma seqüência de cinco tuplas. Dê uma olhada no exemplo: ``` >>> socket.getaddrinfo('www.thapar.edu', 'http') [(2, 1, 6, '', ('220.227.15.49', 80)), (2, 2, 17, '', ('220.227.15.49', 80))] >>> ``` O 2 representa a família, 1 representa o tipo de socket, 6 representa o protocolo, ' ' representa o canonical name e ('220.227.15.49', 80) representa o endereço dos dois sockets. No entanto, este número é difícil de compreender. Navegue para onde estão seus arquivos de exemplos. Use o código a seguir para obter um resultado mais legível. `getadd1.py`: ``` import socket def get_protnumber(prefix): return dict( (getattr(socket, a), a) for a in dir(socket) if a.startswith(prefix)) proto_fam = get_protnumber('AF_') types = get_protnumber('SOCK_') protocols = get_protnumber('IPPROTO_') for res in socket.getaddrinfo('www.thapar.edu', 'http'): family, socktype, proto, canonname, sockaddr = res print('Family :', proto_fam[family]) print('Type :', types[socktype]) print('Protocol :', protocols[proto]) print('Canonical name :', canonname) print('Socket address :', sockaddr) print('--------------------------------------------') ``` image::http://i.imgur.com/k5Uus10.png[img7] A primeira parte do código faz um dicionário usando `AF_, SOCK_` e `IPPROTO_prefixes` que mapeia o número do protocolo para seus respectivos nomes. Este dicionário é formado pela técnica he list comprehension. O código dentro da função(get_protnumber(prefix)) do código acima pode parecer um pouco confusa, mas podemos executá-la separadamente da seguinte maneira: ``` >>> dict(( getattr(socket,n),n) for n in dir(socket) if n.startswith('AF_')) {0: 'AF_UNSPEC', 2: 'AF_INET', 6: 'AF_IPX', 11: 'AF_SNA', 12: 'AF_ DECnet', 16: 'AF_APPLETALK', 23: 'AF_INET6', 26: 'AF_IRDA'} ``` Agora ficou mais simples de entender. Esse código é geralmente usado para obter o número do protocolo: `for res in socket.getaddrinfo('www.thapar.edu', 'http'):` A linha de código anterior retorna os cinco valores, conforme discutido na definição. Esses valores são então combinados com seu dicionário correspondente.
  6. 2018/01/02 13:07:10 [info] 15686#0: the configuration file /etc/fsbr/fsbr.conf syntax is ok 2018/01/02 13:07:10 [info] 15686#0: the configuration file /etc/fsbr/fsbr.conf was tested successfully USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 2213 0.0 0.0 6784 2036 ? Ss 03:01 0:00 fsbr: master process /usr/sbin/fsbr -c /etc/fsbr/fsbr0.conf
×

Informação Importante

Ao usar este site, você concorda com nossos Termos de Uso.