Jump to content

Search the Community

Showing results for tags 'programação'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Supporter area
    • Tools of the Trade
    • Finance transparency
  • MBConf
    • MBConf v1
    • MBConf v2
    • MBConf v3
  • Mente Binária
    • General
    • Computer Architecture
    • Certifications
    • Quantum computing
    • Cryptography
    • Challenges and CTF
    • Hardware Hacking
    • Electronics
    • Conferences
    • Forensics
    • Games
    • Data privacy and laws
    • Code breaking
    • Networking
    • Pentest
    • Speak to us!
    • Software releases
  • Career
    • Study and profession
    • Jobs
  • Reverse Engineering
    • General
    • Malware Analysis
    • Firmware
    • Linux and UNIX-like
    • Windows
  • Programming
    • Assembly
    • C/C++
    • Python
    • Other languages
  • Operating Systems
    • GNU/Linux and UNIX-like
    • Windows
  • Segurança na Internet's Discussão

Categories

  • Portal Mente Binária
  • Specials

Categories

  • Tech basics
    • Text comprehension
    • English
    • Mathematics
  • Computing Basics
    • Lógica de Programação
    • Computers Architecture
    • Cryptography
    • Data Structures
    • Network
    • Operating Systems
  • Specifics
    • SO Internals
    • Web
    • Python
    • Javascript
    • Infrastructure
    • Go
    • Reverse Engineering
    • DevOps
    • C/C++
    • Log Analysis

Categories

  • Crackmes
  • Documentation
  • Debuggers
  • PE tools
  • Books
  • Util
  • Packers
  • Unpackers
  • Virtual Machines

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


GitHub


Twitter


LinkedIn


Website

  1. O Relatório de Ciências da Organização das Nações Unidas para Educação, Ciência e Cultura (Unesco) publicado em fevereiro deste ano apontou que, em todo o mundo, as mulheres ainda representam apenas 40% dos graduados em ciência da computação e informática. Em relação à área de engenharia, o percentual cai para 28%, enquanto em áreas altamente qualificadas, como inteligência artificial, apenas 22% dos profissionais são mulheres. Ao se deparar com o relatório, o Secretário-Geral da ONU António Guterres alertou que “promover a igualdade de gênero no mundo científico e tecnológico é essencial para a construção de um futuro melhor”. Segundo ele, “mulheres e meninas pertencem à ciência”, mas os estereótipos as afastaram de campos relacionados à área, e “é hora de reconhecer que mais diversidade promove mais inovação”. Para o Secretário-Geral, sem mais mulheres nas ciências, “o mundo continuará a ser projetado por e para homens, e o potencial de meninas e mulheres permanecerá inexplorado". Para que esse potencial seja cada vez mais explorado, precisamos incentivar o público feminino a entrar na área de ciência, tecnologia, engenharia e matemática (também conhecida pelo termo STEM, em inglês), sem medo de ser menos capaz que os homens nesse quesito – porque de fato não é. É por isso que escolhemos este dia 12 de outubro para tratar do assunto. Toda segunda terça-feira do mês de outubro comemora-se mundialmente o Ada Lovelace Day, celebração internacional que representa a conquista das mulheres na STEM. Augusta Ada Byron King – conhecida como Ada Lovelace – é considerada a primeira programadora do mundo. A matemática e escritora inglesa é reconhecida principalmente por ter escrito o primeiro algoritmo a ser processado por uma máquina nos anos 1800. Ada Lovelace deixou um legado, séculos depois, sendo seu nome usado para dar destaque às mulheres que atuam na ciência, tecnologia, engenharia e matemática, com o objetivo de aumentar a representação do público feminino nessas áreas. E já temos exemplos de meninas que conquistaram e vêm conquistando cada vez mais espaço e podem inspirar outras tantas a estudarem e se engajarem nas profissionais relacionadas a STEM. É o caso de Lais Bento, que é Java Software Engineer na eDreams ODIGEO. Sua trajetória na área de Tecnologia da Informação (TI) vem de uma longa história. Ela conta que sempre se interessou pelo tema, mas na hora de escolher um curso na universidade se deparou com algumas barreiras sociais e acabou optando por cursar administração. "Depois de um tempo, isso passou a ser um peso na minha vida, porque eu não gostava da área. Mas eu tinha receio de ir para TI, porque não era muito moda, tinha poucas mulheres na área", diz Lais em entrevista ao Mente Binária. Até aceitar definitivamente seu gosto pela tecnologia, Lais passou um tempo trabalhando como administradora. Em determinado momento, decidiu fazer um curso técnico em informática, para depois cursar Análise e Desenvolvimento de Sistemas no Instituto Federal de São Paulo. "No segundo ano da universidade consegui minha primeira oportunidade na área de TI, em uma grande empresa, com 26 anos", conta. "No começo eu era uma no meio de vários homens, mas isso mudou muito rápido" - Lais Bento Depois de alguns meses, Lais já foi para sua segunda oportunidade na carreira, no iFood, que considera ter mudado sua vida. "Entrei lá como estagiária e virei uma desenvolvedora de software. Aprendi o que precisava e graças a toda a experiência que conquistei, hoje estou morando em Portugal, trabalhando na maior agência de viagens online da Europa". Lais conta que sua experiência no iFood marcou sua vida profissional especialmente por ter sido uma das primeiras estagiárias de tecnologia mulher de toda a história da empresa. "Isso me impactou bastante". Após 2 anos trabalhando lá, ela conta que a inclusão já mudou bastante, e muitas mulheres entraram na área de programação, entre elas desenvolvedoras e cientistas de dados. "No começo eu era uma no meio de vários homens, mas isso mudou muito rápido". Não ter medo de começar – Lais relata ter um único arrependimento nessa história toda: não ter entrado antes na área de TI. "Deveria ter começado com 18 anos, aceitando que mulheres podem programar também", diz. Segundo ela, começar mais cedo poderia ter trazido mais experiência do que tem hoje. "As coisas em tecnologia mudam muito rápido, então você sempre tem que estar pronto para aprender coisas novas. Sei que não tem idade para começar, mas se eu tivesse entrado antes, vejo que eu estaria mais avançada", diz. Já a história de Tainah Bernardo é um pouco diferente. Ao contrário da Lais, que sempre soube que gostava de tecnologia, Tainah nunca achou que essa área fosse pra ela. Formada em Artes Visuais, recentemente, aos 26 anos, ela descobriu que queria mudar de carreira e foi atrás de um curso de programação. "Com a pandemia, eu percebi que estava cansada de atuar em uma profissão que, por mais que gostasse, não era valorizada. Um dia encontrei um curso que ensinava programação a partir do zero e que garantia inserção no mercado. Foi aí que comecei a conhecer o mercado da tecnologia, porque até então era uma coisa distante, tinha uma imagem bem estereotipada de que para entrar na área precisaria ser um gênio ou alguém enclausurado em um quarto escuro de capuz com várias telas em volta", conta. "Tudo sempre foi muito atrelado à imagem masculina, mas isso era muito inconsciente" – Tainah Bernardo Segundo ela, ainda é muito enraizada a visão de que somente homens são capazes de participar do desenvolvimento tecnológico. "Quem mexia com tecnologia era o meu irmão, o computador ficava no quarto dele. Tudo sempre foi muito atrelado à imagem masculina, mas isso era muito inconsciente", conta Tainah, que deu o primeiro passo em meados do ano passado para investir em uma área totalmente nova. "Fui fazer o curso, que tinha duração de 6 meses, e achei que o mais importante é que eles trabalham com inclusão, tratam bem as pessoas, fazem elas se sentirem acolhidas, parte da comunidade, e eu vi que tinha espaço pra mim", conta sobre sua experiência no curso oferecido pela Labenu. Tainah conta que acabou estudando muitas matérias práticas, e no final realizou treinamentos relacionados a entrevistas de emprego e challenges que a prepararam para o mercado de trabalho. "Terminei o curso e comecei a trabalhar no Olist". Hoje, ela é desenvolvedora backend de e-commerce no Olist, trabalhando com NodeJs dentro do time de Shops, que desenvolve um produto novo para pessoas físicas criarem uma loja virtual e integrá-la às redes sociais. O time da Tainah é responsável por essa integração e cotidianamente busca melhorias e novas features para quem utiliza o app. No dia a dia de trabalho, ela utiliza Javascript. Superando desafios – "Se eu tivesse pensado um pouco mais, talvez eu não tivesse coragem, porque cheguei a ver relatos de mulheres que desistiram da área por conta de pessoas que as desrespeitaram", conta Tainah. Segundo ela, o grande desafio de iniciar uma carreira na tecnologia é a comparação em relação aos homens, pois causa estranheza ver tantos homens numa mesma área enquanto há poucas mulheres. "Quando cheguei, fiquei meio bloqueada, achando que seria tratada com diferença, mas eu tive sorte, porque as pessoas foram super legais comigo", reforça. Superando o receio inicial, Tainah percebeu que essa é uma área que está crescendo, e assim ela decidiu crescer junto. "Eu não sabia que TI tem uma comunidade tão ativa e cooperativa, o que me deixou bem feliz. Mas comecei a entender isso melhor durante o curso, pois vi que tinha mulheres trabalhando, que mudaram de área e conseguiram se adequar. Eu vi que eu consigo também, se tem outras pessoas lá, eu também consigo", destaca. "Eu achava que sempre dava sorte de entrar nos lugares, mas aos poucos percebi que não foi por sorte, e sim porque mereci, me esforcei, estudei", relata Lais. Para Lais, um grande desafio que principalmente as mulheres devem superar é de sempre se acharem inferiores tecnicamente ou menos capazes. "Eu achava que sempre dava sorte de entrar nos lugares, mas aos poucos percebi que não foi por sorte, e sim porque mereci, me esforcei, estudei. Se isso acontecer com alguma menina, o ideal é fazer uma listinha de tudo que teve que aprender para estar onde está hoje. Com isso, consigo perceber que foi uma questão de mérito", conta. Como TI é uma área predominantemente masculina, Lais destaca que há algumas situações em que meninos fazem piadas machistas, perpetuando o estigma de que a área de tecnologia não é para mulheres. "Mas as pessoas hoje estão muito mais abertas. No começo eu sentia mais medo de homens me julgarem, de sofrer algum tipo de machismo. Mas é sempre bom estarmos preparados psicologicamente, pois essas coisas podem acontecer". Como entrar na área – Lais diz que a melhor maneira para meninas que querem atuar com tecnologia iniciarem sua carreira na área é saber exatamente o que querem fazer. "É muito fácil falar que quer entrar em TI, mas é uma área muito abrangente. Você quer ser programadora? De aplicativo ou de site? Quer participar da parte lógica, ou ir para a parte bonita do site, que todo mundo vê? Eu sugiro que elas entendam o que cada parte faz, e como trabalham, e assim fazer um curso básico de programação de um app, ou como construir um site, para entender o que acha mais legal. O ideal é saber o que quer fazer e depois disso começar a estudar", indica. Segundo ela, o próximo passo é encarar as oportunidades de frente. "Já vi pesquisas que dizem que se uma mulher achar que não atende a um requisito da vaga, não se candidata. E eu diria para se candidatar sim, porque o homem se candidata e nem sempre ele tem todos os requisitos. Encarem as oportunidades de frente, se atualizem e estudem. Se você recebeu uma proposta, participe do processo. A chance de conseguir a oportunidade é muito grande, e sobre o requisito que faltou, você acaba aprendendo depois" diz. "Há muitas oportunidades, só faltam mulheres corajosas para conseguirem ocupar essas vagas", destaca Lais. "Vai dar medo, vai ser difícil, mas vai dar. É possível sim, não existe limitação física para isso", diz Tainah. Tainah também acredita que mais do que capacidade intelectual, o principal para entrar na área de TI é a vontade. "Vai dar medo, vai ser difícil, mas vai dar. É possível sim, não existe limitação física para isso. Eu vi muitas mulheres que eram mães de primeira viagem ou de segunda viagem fazendo o mesmo curso que eu. Pessoas que não tinham condições financeiras, com dificuldade de aprendizado, e todas elas conseguiram. É possível, mas não vai ser fácil. Aí que entra o querer", relata. Ela também não precisou abandonar sua vocação com as artes para seguir a carreira na tecnologia. Hoje, as artes visuais hoje são um hobby (vem dar uma olhada não trabalho dela!), o que, segundo Tainah, acaba sendo muito melhor do que antes. "Faço quando quero, como quero, não dependo disso, e agora conheci um novo universo que vai de acordo com minha personalidade, porque gosto de trabalhar em grupo, isso não era tão comum nas artes, nos lugares em que trabalhei", conta. "Cada um tem sua história, e é bom não desistir nos momentos difíceis, que é quando a gente tem mais vontade. Nessa hora respire, tire o pé do acelerador, mas não para e não volta. Vai devagar, mas vai".
  2. 413 downloads

    Livro em Português sobre Assembly em constante desenvolvimento. É de autoria do @Felipe.Silva, membro da comunidade aqui! 🤗
  3. O livro Programação Shell Linux é uma referência completa sobre programação Shell nos sistemas operacionais Unix/Linux, e chegou na sua 12ª edição depois de mais de 22 anos que o autor, Julio Cezar Neves, veio adaptando essa obra. Apresentando o assunto de forma didática e descontraída, Julio se utiliza de exemplos e dicas de fácil compreensão para explicar para seu público como programar em Shell. Quem pensa que esse é um livro de cabeceira está enganado. É um material prático para ser usado do lado do computador, sendo uma referência completa da linguagem Shell. "Esse livro não é para estudar, é um guia de referência, porque ele está completo", diz o próprio autor, Julio Neves. Ele conta para o Mente Binária como foi seu processo de construção desse rico material. "Tudo começou como uma brincadeira. Durante muitos anos eu fui gestor e de repente eu estava de saco cheio do meu departamento, não queria ficar administrando pessoas. Então resolvi abandonar essa área e voltar para a área técnica". Foi assim que Julio passou a atuar no suporte técnico. No início dos anos 1980, ele já tinha trabalhado desenvolvendo um Unix na Cobra Computadores e Sistemas Brasileiros, hoje BB Tecnologia e Serviços (BBTS). "Eu continuei usando o Unix, porque quando apareceu o primeiro sistema operacional da Microsoft, o DOS, ele saiu da costela do Unix. Se eu já tinha o Shell, para que eu iria usar o DOS?", diz. Assim, Julio decidiu fazer um sistema para protocolo, transferência e check de integridade de arquivos via FTP para a empresa na qual trabalhava. "Eu fiz tudo em Shell". Ele passou a fazer treinamentos com seus colegas na empresa, e acabou escrevendo um manual no qual conta que tinha muita piada e conteúdo descontraído. "A cada treinamento que eu dava eu ia melhorando. Um belo dia minha esposa falou que isso tudo daria um livro", conta Julio. Foi assim que ele publicou a primeira edição do livro Programação Shell Linux, em 2000. Motivação Os treinamentos internos que Julio fazia em sua empresa sempre davam muita audiência. Ele conta que quando trabalhou na Cobra Computadores, foi feito um convênio com a Universidade Estácio de Sá, do Rio de Janeiro, que cedia as instalações para o pessoal da Cobra dar treinamentos. Em contrapartida, a Cobra dava suporte para a Estácio de graça. "Foi minha primeira experiência dando aula. Eu fiz um curso de didática com técnicas de apresentação e quando fui dar os treinamentos na empresa, anos depois, já tinha essa prática de didática. É uma coisa que gosto de fazer", conta. Hoje, Julio oferece treinamentos sobre Shell Script junto a Rubens Queiroz De Almeida (saiba mais sobre os treinamentos). Além do gosto por dar aulas, Julio conta que o livro é uma maneira de disseminar o conteúdo sobre Shell em uma linguagem fácil e acessível, o que não existe em outros materiais. Ele cita o Linux man pages, um manual escrito em inglês rebuscado, sem exemplos. "Praticamente tudo que tem no man pages tem no meu livro, só que o livro tem um monte de exemplo e bom humor. O man page não te ensina a programar, mas só a usar uma instrução. O meu livro mostra as instruções, de forma ordenada, e o funcionamento do Shell. Eu mesmo aprendi Shell pelo man pages, mas é muito chato!", avalia. "Meu livro mostra as instruções, de forma ordenada, e o funcionamento do Shell" – Julio Neves Por que um profissional precisa ter conhecimento de Shell? "Uma vez eu dei uma palestra sobre Shell e na hora das perguntas um cara falou que não gostava da linguagem. E eu falei que sem o Shell, o Linux não existe. Quando você dá boot na máquina, ela executa centenas de scripts em Shell; quando você loga, ela roda dezenas de scripts em Shell. Tudo que é feito na máquina está em C ou em Shell. O administrador de sistemas antigamente era obrigado a conhecer profundamente Shell", explica. Na área de segurança, a necessidade desse conhecimento é igualmente importante, conforme explica Fernando Mercês, que é pesquisador na Trend Micro e fundador do Mente Binária. "O Linux é um sistema operacional obrigatório na área de segurança. Quem não conhece Linux, não consegue andar nessa área. E o Shell é o coração do Linux, é por onde um usuário controla o sistema inteiro e usa todos os recursos. Programar em Shell é obrigatório para automatizar o que precisa ser automatizado no Linux", explica Mercês. Ele conta que no lado dos ataques, por exemplo, do ponto de vista de segurança ofensiva, e também para criar defesas e ações de proteção de um servidor Linux diante de algum ataque, é preciso usar programação em Shell. "Se seu sistema Linux está sob ataque, você detectou isso e vai bloquear a comunicação do atacante para com o seu servidor, e isso vai ser um comando em Shell do Linux. Além do Linux ser um sistema operacional que precisa ser conhecido, programá-lo bem e saber operá-lo em nível de programação via Shell é essencial para um bom profissional, e um grande diferencial para profissionais de tecnologia em geral", destaca Mercês. Para Julio Neves, não saber programar em Shell pode ser inclusive um risco de segurança. "A pessoa tem que saber Shell, porque a interface gráfica não sabe tudo sobre a digitação, e aí se ele tiver alguma dúvida, vai recorrer à Internet. Se ela fizer isso, pode colocar dentro do computador algo que pode ser ruim, um malware", diz. "Meu nível de Shell depois desse livro ficou muito acima da média", diz Fernando Mercês Na experiência de Mercês, de fato o livro Programação Shell Linux é o material mais completo que se tem em língua portuguesa sobre o assunto. "Quando comecei a estudar Linux, em 2008, vi que os materiais que tratam do assunto introduzem o Shell, mas não vão muito além disso. Aí comprei a 6ª edição desse livro do Julio e me impressionei, porque além de ser muito mais profundo que as introduções que eu tinha lido, a didática é impecável. O livro realmente ensina a programar com alguém que sabe muito", conta. "Meu nível de Shell depois desse livro ficou muito acima da média, mesmo no meio do mundo Linux, porque esse conhecimento veio de alguém que não simplesmente estudou, aprendeu e escreveu um livro. O Julio fez parte do time de desenvolvimento de um Unix. Ele foi capaz de criar um Shell. É uma pérola no Brasil". 12ª edição Julio Neves conta que ao longo do tempo, as edições do livro foram "engordando", cada vez contendo mais material. Mas nessa 12ª edição, ele acabou publicando o livro em uma nova editora, a Novatec, muito motivado a baixar o custo. "Eu estava achando o preço do livro um absurdo e resolvi pegar o livro, que tinha duas partes, um Shell básico e um Shell programação, e tirei o Shell básico para diminuir o custo do livro". Ainda assim, o material continua com 600 páginas, já que ao longo do tempo Julio foi agregando mais conhecimento. "Na primeira edição eu disse que o intuito do livro não era ser um compêndio sobre Shell. Hoje, coloco ele como uma referência sobre Shell", destaca. O livro pode ser comprado online nesse link, e tem um cupom de 25% de desconto válido até o dia 30 de julho. Para utilizar, basta digitar PROGSHELL na hora de realizar a compra. Capa do livro Programação Shell Linux – 12ª Edição Falando em referência, o Julio também é uma inspiração para nós do Mente Binária, afinal foi o primeiro entrevistado no programa Papo Binário, em janeiro de 2016. Assista na íntegra:
  4. Virar um desenvolvedor de jogos pode ser o grande sonho dos apaixonados por games. São esses os profissionais que projetam e criam jogos para computadores, celulares e consoles, se envolvendo desde a concepção até a execução do projeto junto a uma equipe composta por produtores, designers, artistas, engenheiros de som e testadores. Essa galera trabalha para levar os melhores produtos a uma indústria que hoje é composta por 2,8 bilhões de jogadores em todo o mundo, gerando receitas de US$ 189,3 bilhões, segundo dados da empresa de pesquisa Newzoo. No Brasil, a Newzoo aponta que o mercado de jogos terá uma receita de US$ 2,3 bilhões em 2021. Ainda que aqui a indústria seja menor, as oportunidades para trabalhar na área estão crescendo mesmo para quem não é um aficionado pela profissão, como é o caso de Rodrigo Duarte Louro. Ao procurar estágio enquanto cursava a faculdade de Ciência da Computação, ele acabou se deparando com uma vaga em uma empresa de jogos pequena que tinha acabado de começar. Na época, Rodrigo tinha 21 anos e confessa que esse não era seu sonho, mas acabou encarando o desafio. "Quando entrei na faculdade, eu não tinha muita ideia para onde ir. Nunca quis muito uma carreira específica, mas as possibilidades de mercado para quem é programador são grandes", diz Rodrigo ao Mente Binária. "Eu não manjava nada de games, mas estagiei nessa empresa por um ano, e foi onde eu comecei a gostar e aprender sobre desenvolvimento de games", conta. Rodrigo saiu desse estágio para conseguir concluir a faculdade, mas no último semestre voltou a estagiar em outra empresa de games, a Tapps, onde está até hoje trabalhando com desenvolvimento de jogos para mobile. "Quando comecei a estagiar com jogos, eu gostei, e a menos que aparecesse uma oportunidade muito boa, eu decidi que não ia mais sair da área", relata. "A menos que aparecesse uma oportunidade muito boa, eu decidi que não ia mais sair da área" - Rodrigo Duarte Louro Já o caso de Murilo Costa é o mais tradicional para quem trabalha com desenvolvimento de jogos: ser apaixonado pela área. "Desde dos 12 anos de idade eu já estava certo da vida que queria trabalhar com jogos, e nessa idade já tinha começado a programar", conta Murilo ao Mente Binária. Foi assim que ele acabou fazendo um curso técnico em informática aos 15 anos. "Eu já queria entrar na área de jogos, mas no Brasil era difícil", destaca. Por certa falta de oportunidade, Murilo acabou entrando no mercado de TI como desenvolvedor de software, atuando nessa área por cerca de 7 anos. Enquanto isso, ele também cursou a faculdade de Ciência da Computação. "Eu tinha 22 anos quando consegui meu primeiro emprego como estagiário em jogos. Eu ia para essa profissão de qualquer jeito, mesmo que por conta própria. Mandei muito currículo, porque o mais importante era começar de algum jeito, e depois ir encontrando meu espaço", ressalta. "Eu me permiti voltar para a estaca zero quando entrei na indústria de games. Quando decidi sair do emprego de desenvolvedor de software e ir pra jogos, eu já era CLT e estava quase virando um profissional pleno dentro da empresa, mas decidi voltar a ser estagiário para começar na área", conta. "Eu me permiti voltar para a estaca zero quando entrei na indústria de games" - Murilo Costa Murilo ficou durante 5 anos e meio nessa empresa, começando como programador de jogos mobile, e aos poucos foi crescendo internamente, até virar coordenador. "Passei a trabalhar como gestor, participava da contratação e desenvolvimento de outros programadores", diz. Mas no ano passado, Murilo decidiu que queria voltar a programar, e foi aí que começou a trabalhar no estúdio Rogue Snail como programador sênior. "O que eu gosto é da área de programação", pontua. O que precisa para ser um desenvolvedor de jogos A complexidade da profissão pode variar dependendo do tipo de jogo que será desenvolvido, mas para quem quer começar, é preciso saber o básico de programação. "Na faculdade não tem nada específico para o mercado de trabalho. Se você tem uma base de programação forte, está preparado para tudo, mas não é especialista em nada", diz Rodrigo. Ele conta que apesar disso, há cursos mais focados em jogos. "Na Tapps muita gente que trabalha comigo fez um curso de design de jogos na Fatec e na Anhembi. No meu caso, não fiz nenhum curso específico. Agora, com 7 anos de experiência, tenho uma noção das outras áreas, mas o background de programação dá total liberdade para fazer os jogos", diz. A dica é saber duas linguagens de programação, que são as mais adotadas em desenvolvimento de games: C# e C++. A primeira é utilizada pelo motor de jogos (game engine) chamado Unity, enquanto a segunda é utilizada pela engine Unreal. Apesar de essas serem as duas linguagens mais utilizadas, muitas empresas querem fazer suas próprias engines. "Na Unity você consegue fazer e exportar o jogo para cada plataforma específica, mas na Tapps a gente exporta só mobile, então usamos uma engine própria", diz Rodrigo. O mais recomendável é estudar não somente a linguagem, mas aprender como a engine funciona e pode ser manipulada para se obter resultados. "Eu já praticava isso sozinho", diz Murilo. "Normalmente, quando as pessoas vão trabalhar com jogos, já tiveram contato com engines ou produção de algum jogo". Ele destaca que o ideal para treinar é participar de game jams, que são encontros de desenvolvedores de jogos com a proposta de planejar e criar um ou mais jogos em pouco tempo, geralmente variando entre 24 e 72 horas. "Isso ajuda as pessoas a terem contato com as engines e tecnologias. Mas precisa de um conhecimento básico em programação. Essa é uma recomendação para desenvolvedores de software no geral", destaca. Matemática e inglês são pré-requisitos Além de saber o básico de programação, é preciso ter uma noção tanto de matemática quanto de inglês para quem quer evoluir na carreira de desenvolvedor de games. "Os dois são bem importantes. Não é um impeditivo total não saber isso, mas vai facilitar muito sua vida", destaca Rodrigo. Tanto ele quanto Murilo recomendam no mínimo o conhecimento de leitura em inglês para que a atuação na área seja mais fácil. Isso porque muitos dos materiais de estudo em programação são nesse idioma. "Isso é uma barreira que pode atrapalhar", diz Rodrigo. Os conhecimentos de matemática também são importantes para programação, na visão de Rodrigo. "Dependendo do jogo que você fizer, a matemática é utilizada mais ou menos na prática, mas ter esse conhecimento mais forte te faz um programador melhor sempre", afirma. Ele diz ainda que em alguns casos a geometria analítica é utilizada. "Se estou fazendo um jogo de tiro, preciso saber com qual força a bala sai da arma", explica. Mobile x console Tendo navegado no mundo mobile e agora no de jogos para computadores, Murilo conta um pouco sobre a diferença entre programar em um e para outro universo. "Jogos mobile gratuitos normalmente têm compras dentro, e os usuários podem assistir anúncios para obter recompensas. Também é preciso pensar que essa indústria é gigantesca, tanto em número de devices quanto de usuários. Você atinge o público de maneira mais global e tem que se preparar para lidar com menos recursos, porque o celular é menos potente que o console", diz. Ele destaca ainda que a usabilidade é bem diferente no celular e no PC. "No mobile, precisamos pensar em como integrar compras e propagandas em tempo de execução do jogo, e games de console normalmente não têm isso. No PC, o jogo tende a ser mais fácil, por outro lado, que os demais jogos de console, por ser mais flexível em relação aos inputs de teclado, mouse, ou controle", relata Murilo. Mercado de trabalho O mercado de trabalho brasileiro para a indústria de games está crescendo, apesar das oportunidades ainda serem maiores em outros países. Na visão de Murilo, esse ainda é um setor de poucas empresas no Brasil, com algumas companhias grandes dominando, mas praticamente todas para desenvolvimento mobile, ressaltando que os jogos para celulares compõem a maior parte do mercado consolidado no país. Murilo diz ainda que há alguns estúdios pequenos se desenvolvendo localmente, mas muitos ainda dependem de investimento externo ou de projetos institucionais que estimulem seu funcionamento. "Ainda temos poucas oportunidades e não temos tanta escolha". Ele adiciona que na área de programação, ainda há uma diferenciação para quem trabalha com desenvolvimento para bancos ou para web, e quem trabalha com jogos. "Tem uma procura muito alta de pessoas querendo trabalhar na área, mas a remuneração não é competitiva, e temos poucos estúdios. Mas tem crescido", avalia. "Não ter medo e não desistir da área" Para Rodrigo, o gargalo também aparece do lado da mão de obra especializada. "Sempre vejo vaga aberta, mas é difícil preencher. As empresas estrangeiras acabam tendo maior competitividade. Também é o sonho das pessoas trabalharem em grandes companhias como Blizzard, King, etc. O mercado de jogos mexe com o sonho das pessoas, por isso muitas delas já escolheram a faculdade porque queriam fazer games. Mas ainda vejo que faltam profissionais especializados, e isso é na área de programação em geral", pontua. Mesmo com esses desafios, não ter medo e não desistir da área é a dica de Rodrigo para quem quer atuar como desenvolvedor de games. "Se você tem um background de programação, já é um programador e quer migrar de área, pesquise o mercado, pois tem muita empresa sólida. E quem não é programador e quer fazer jogos, precisa começar a estudar programação em geral, ter um nível mediano. Não precisa focar em jogos inicialmente, mas depois comece a pegar tutoriais, estudar engines e fazer jogos simples. Explore a parte criativa, porque isso gera muito conhecimento", indica. "A participação em game jams ajuda muito" Murilo também destaca a importância de tentar construir um portfólio, reforçando que a participação em game jams ajuda muito nesse sentido. "É uma oportunidade de conhecer pessoas da área, estabelecer contatos e mostrar o trabalho. É preciso ter a experiência de fazer um jogo para entender quais são as partes que precisam ser melhor desenvolvidas", ressalta. 🎮
  5. Pessoal, disponibilizei no meu GitHub o meu primeiro projeto OpenSource, como forma de retornar para a comunidade o que tenho aprendido nestes últimos anos. O projeto se chama LIBCPF (C Plugin Framework - https://github.com/fabianofurtado/libcpf/) e se trata de uma biblioteca/framework pra gerenciamento de Plugins (".so") no Linux, feita em C. Para maiores detalhes, veja a documentação no arquivo README.md. Ela foi lançada utilizando-se a licença GPL v2, uma vez que não entendo muito sobre esse assunto e precisava de uma licença para o projeto. Espero que, de alguma forma, este projeto possa ser útil para você. Mesmo que não exista a necessidade de utilização desta lib em sua aplicação, a mesma pode ser usada para listar os nomes das funções e os offsets que existem dentro de uma shared library, algo que considero bem interessante. Veja no exemplo como isso funciona. Como qualquer programa em C, o desenvolvimento deu muito trabalho pois tentei desenvolvê-la com foco na segurança, tentando evitar possíveis "buffer overflows" ou qualquer outro tipo de vulnerabilidades. Sugestões e críticas serão sempre bem-vindas! Desde já, agradeço.
  6. 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!
  7. A presença de Security Champions nas equipes de desenvolvimento pode trazer uma visão mais estruturada acerca da segurança de aplicações. Além disso, ele pode ser um grande influenciador da cultura de segurança dentro da empresa. Mas você sabe qual é o papel deste profissional? Conversamos com o Rodrigo Maués, Tech Lead na Conviso Application Security, para entender melhor quem é e qual o papel do Security Champion dentro de um time de segurança. O que é o Security Champion De acordo com Maués, dentro de empresas que produzem softwares, é comum existir um atrito ocasional entre duas áreas. Para a área de desenvolvimento, as equipes de segurança são pontos de gargalo dentro de um processo já bem pressionado e com prazos apertados. Do outro lado, temos as equipes de segurança que, por vezes, entram em conflito ao buscar introduzir mais segurança nos produtos entregues pelos desenvolvedores. “Este conflito nem sempre é facilmente solucionado, pois vai contra algo que, do ponto de vista comercial, é bem mais forte que a validação de um código: as demandas de um mercado cada vez mais inovador e ágil”, contextualiza o Tech Lead. É aí que entra o papel do Security Champion. No mundo de Segurança de Aplicações, entendemos os Security Champions como sendo os membros das equipes de desenvolvimento que receberam treinamento específico para atuar como ponto focal de segurança de aplicações dentro do time. Quando um membro do time de desenvolvimento se torna um Security Champion, ele estabelece uma melhor comunicação com seus pares e muda a cultura do desenvolvimento de dentro para fora, já que acessa a mesma linguagem dos membros envolvidos na produção do software. “Desta forma, o time de Desenvolvimento consegue compreender muito melhor a informação passada, uma vez que recebe o conhecimento de um dos seus”, esclarece Maués. Ou seja: o Security Champion trabalha como uma ponte entre as duas áreas, de forma conciliadora, para garantir que a cultura da segurança seja mantida sem desgastar as equipes. Quais as responsabilidades de um Security Champion? Entre as principais responsabilidades dos Security Champions está a mudança cultural dos desenvolvedores, que devem passar a ter um olhar mais cuidadoso no trabalho de codificação, aplicando as melhores práticas de desenvolvimento seguro e buscando ter um olhar cada vez mais focado em segurança desde o início da criação de seus produtos. Um Security Champion, de forma geral, é um transformador cultural para as equipes de desenvolvimento, e atua também como uma ponte de ligação entre as áreas de desenvolvimento e de segurança. “É ele quem consegue manter um entendimento dos dois mundos, amenizando os conflitos e disputas”, esclarece Maués. Algumas atividades comuns do dia a dia de um Security Champion são: Ajudar na realização de revisões de segurança; Ajudar com a observação de melhores práticas de segurança; Desenvolver Modelagem de Ameaças para aplicativos novos e em evolução; Participar de movimentos de P&D – Pesquisa e Desenvolvimento; Orientar na identificação de requisitos de segurança; Avaliar e estudar bugs em código; Servir de elo de contato entre as demais equipes da área de segurança. No entanto, é muito importante ressaltar que o Security Champion não realiza essas tarefas sozinho. Tudo é feito em colaboração com o time! Afinal, o papel do Security Champion não é o de centralizar o conhecimento - e sim, de disseminá-lo nas equipes. Como se tornar um Security Champion? Existe um perfil específico? É comum que Security Champions sejam desenvolvedores treinados e devidamente capacitados para suportar as iniciativas de segurança. No entanto, isso não é regra - é possível que profissionais egressos de outras áreas, mas com algum conhecimento em desenvolvimento, recebam treinamento para atuar como Security Champions caso cumpram outros requisitos. De todo modo, é preciso ressaltar que Security Champions não são profissionais de segurança de aplicações que são focados exclusivamente em segurança. Essa confusão é muito comum, mas é uma concepção errada. A escolha dos Security Champions dentro de cada time gera uma noção de pertencimento e ajuda no trabalho com os desenvolvedores. É imprescindível um trabalho cauteloso, que começa por um mapeamento de times. Para isso, é preciso identificar e capacitar membros dos times de desenvolvimento que tenham esse perfil, para que atuem como facilitadores de segurança. E este papel exige características comportamentais, como iniciativa e autogestão. Mas caso você sinta afinidade com essa carreira, esse já é um ótimo indício! Vagas na Conviso A Conviso, mantenedora aqui do Mente Binária, está com muitas vagas abertas. São vagas para áreas variadas dentro da empresa - de Desenvolvedor a Analista de Segurança da Informação. Caso você sinta afinidade com a especialidade da Conviso - Segurança de Aplicações - não deixe de se inscrever, ou mesmo de se cadastrar em nosso banco de talentos. É só clicar no botão abaixo:
  8. Os compiladores são ferramentas muito úteis e importantes no mundo da programação e desenvolvimento. A função básica dos compiladores é pegar uma linguagem de "alto nível" (linguagem com maior nível de abstração do hardware) e produzir um código semanticamente equivalente em "baixo nível". A velocidade de execução do código compilado é uma vantagem que se destaca, tendo em vista que o compilador faz otimizações no processo de compilação. Verificações de erros sintáticos e semânticos são outras funcionalidades também executadas pelo compilador. Por que criar um compilador? Além dos motivos mencionados anteriormente, a forma mais simples e rápida de entender como os compiladores funcionam é criando um. Neste tutorial iremos criar um compilador simples, porém abordando os principais conceitos da compilação de forma teórica e prática. Para seguir esse tutorial será necessário o conhecimento de algoritmo e no mínimo uma linguagem de programação. Neste artigo estarei utilizando a linguagem C. Antes de começarmos a criação do projeto, vamos organizar o nosso projeto: Criaremos uma linguagem que trabalha com números inteiros e reais; Utilizaremos condições (if, else, etc); Utilizaremos expressões aritméticas e relacionais; Etapas da compilação As etapas que um compilador executa são: Análise léxica, Análise sintática, análise semântica, otimizador de código e gerador de código objeto. Alguns compiladores tem uma estrutura bem mais complexa, dependendo da linguagem a ser compilada: Nosso projeto terá as seguintes etapas: análise léxica, análise sintática, análise semântica e gerador de código. O gerador de código vai gerar um bytecode para uma máquina virtual que também vamos implementar. Bytecodes são instruções para uma máquina virtual, como mover um valor para a memória ou para um registrador, por exemplo. Abaixo podemos ver um trecho de código em Python e seus respectivos bytecodes: def soma(): print(10 + 10) 0 LOAD_GLOBAL 0 (print) 2 LOAD_CONST 1 (20) 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 0 (None) 10 RETURN_VALUE No final desta série estaremos executando o seguinte código: INIT VAR max := 10 VAR num INPUT num IF (num < max) INIT PRINT 0 END ELSE INIT PRINT 1 END END Análise Léxica A análise léxica consiste em pegar cada caractere de uma linguagem e identificar os padrões da linguagem. Exemplo: int a = 10 Aqui podemos identificar os seguintes padrões: int é uma palavra reservada do compilador; a é um identificador/variável; = é um sinal de atribuição; 10 é um número inteiro; Ao realizar esse processo estamos identificando os lexemas, que são pedaços de uma string (texto), reconhecidos pelo analisador léxico. Os tokens são um par constituído de um nome e um valor de atributo, sendo este último opcional: <tipo, valor> Onde: tipo como o nome já diz seria o tipo do token. valor é o valor de um token. Alguns tokens não utilizam este campo. Representação da análise léxica: Para uma entrada como VAR num := 100 + 10 obtemos os seguintes tokens: <PC_VAR> <ID, num> <OP_ATR> <T_INT, 100> <OP_MAIS> <T_INT, 10> Onde: <PC_VAR> representa a palavra chave VAR; <ID, num> representa um identificador (variável ou função) tendo o valor num; <OP_ART> representa o operador de atribuição =; <OP_MAIS> representa o operador aritmético mais (+); <T_INT, 100>, <T_INT, 10> representa um inteiro com o valor 100 e 10 respectivamente; Não se esqueça que os tipos de token são definidos por você! Usarei o gcc como compilador C e o vscode como editor. Iremos começar de uma forma simples, melhorando tudo aos poucos, vamos nessa! Essa é a estrutura de pastas do nosso projeto. Temos uma pasta para os headers, uma pasta src para o código fonte e a pasta exe, que terá o executável: Escreva o texto seguinte no arquivo teste.txt: INIT PRINT 1 + 2 * 3 END include/lex.h - Aqui simplesmente criamos um módulo para tratar da análise léxica e definimos a função que retorna um token: #ifndef art_lex_h #define art_lex_h void proximo_token(); #endif src/lex.c: Esta é nossa função inicial que lê cada caractere e mostra na console. Se o caractere for EOF, significa que não há mais caracteres no arquivo (fim de arquivo) e então paramos o loop: #include <string.h> #include <ctype.h> #include "glob.h" #include "lex.h" // variável que passará por cada caractere do arquivo static int c; void proximo_token() { while (1) { c = fgetc(file); if (c == EOF) break; else printf("%c", c); } } includes/glob.h: Este outro arquivo serve para algumas definições globais (que vamos usar em mais de um arquivo). Definimos os tipos dos tokens, um enum para representar o token e uma struct com os campos tipo e val: #ifndef art_glob_h #define art_glob_h #include <stdio.h> #include <stdlib.h> FILE *file; // linha atual static int linha = 1; // tipos de tokens enum { // palavras chave PC_INIT, PC_END, PC_PRINT, PC_INPUT, PC_VAR, PC_IF, PC_ELSE, // numeros T_INT, // operadores OP_MAIS, OP_MENOS, OP_MULT, OP_DIVI, // ( ) := < > <= >= = T_LPARENT, T_RPARENT, T_ATRIB, T_MENOR, T_MAIOR, T_MENOR_I, T_MAIOR_I, T_IGUAL, // identificador ID }; typedef struct { int tipo; int val; } Token; Token tok; #endif src/main.c: Na função main iremos tentar abrir um arquivo. Caso haja algum erro o programa sairá mostrando a mensagem de erro. Caso contrário, leremos todos os caracteres do arquivo teste.txt. Vamos ver se funciona: #include <stdlib.h> #include "lex.h" #include "glob.h" int main(int argc, char *argv[]) { // abrir o arquivo file = fopen(argv[1], "r"); if (file == NULL) { printf("Erro ao abrir o arquivo"); exit(EXIT_FAILURE); } proximo_token(); fclose(file); return EXIT_SUCCESS; // ou return 0 } Para facilitar o processo de compilação usaremos o seguinte Makefile: all: gcc -c src/lex.c -I includes -o exe/lex.o gcc src/main.c exe/*.o -I includes -o exe/main rm -r exe/*.o *Se você estiver em um ambiente Windows saiba que o comando rm -r exe/*.o não funcionará. Ao executar o Makefile teremos na pasta exe o arquivo compilado. Ao executarmos teremos a seguinte saída: INIT PRINT 1 + 2 * 3 END Perfeito! Por agora vamos ignorar espaços em branco, tabulação e quebra de linha. Criaremos agora uma função que vai criar um token. Por enquanto ela irá apenas mostrar na saída algo como <’+’, 0> <’INIT’, 0>, mas depois vamos mudar isso. lex.c: Aqui estamos somando 1 na variável linha para uso posterior em caso de nosso compilador ache um caractere que não existe em nossa linguagem (como um “$”, por exemplo): void makeToken(char *nome, int val) // mostrar o token { printf("<%s, %d>", nome, val); } void voltaPonteiro() // volta um caracter se necessário { if (c != EOF) fseek(file, ftell(file)-1, SEEK_SET); } void proximo_token() { // após o if else if (c == ' ' || c == '\t') continue; else if (c == '\n') { linha++; continue; } } No código acima temos uma função voltaPonteiro, que é responsável por voltar um caractere no arquivo. Em alguns casos vamos ter que ver o caractere a frente e depois voltar o caractere quando estivermos analisando uma palavra chave. Enquanto o caractere for alfanumérico o ponteiro avança. Para facilitar o entendimento vamos utilizar a imagem abaixo como exemplo. Aqui reconhecemos a palavra num e paramos no caractere =, ou seja, reconhecemos o token <ID, num>. Quando vamos continuar o processo iniciamos do =, isto é, o próximo caractere é o espaço, seguido do número 1 e assim por diante. Tendo em vista que = é um caractere diferente do que estaríamos esperando iremos esquece-lo e então voltaremos um caractere parando assim no m. lex.c: vamos reconhecer operadores aritméticos como mais (+), menos (-), multiplicação (*) e divisão (/): void proximo_token() { // codigo anterior else if (c == '+') makeToken("+", 0); else if (c == '-') makeToken("-", 0); else if (c == '*') makeToken("*", 0); else if (c == '/') makeToken("/", 0); // codigo else Ao compilar o código e executar teremos algo como: $ ./exe/main.exe teste.txt INITPRINT1<+, 0>2<*, 0>3END lex.c: Agora vamos reconhecer os demais números, palavras, parênteses, etc: else if (c == '+') { makeToken("+", 0); } else if (c == '-') { makeToken("-", 0); } else if (c == '*'){ makeToken("*", 0); } else if (c == '/') { makeToken("/", 0); } else if (c == '(') { makeToken("(", 0); } else if (c == ')') { makeToken(")", 0); } else if (c == ':') { c = fgetc(file); // pega o próximo caractere if (c == '=') // se for '=' sabemos que é o token ':=' makeToken(":=", 0); } else if (c == '<') { c = fgetc(file); // pega o próximo caractere if (c == '=') // se for '=' sabemos que é o token '<=' makeToken("<=", 0); else makeToken("<", 0); } else if (c == '>') { c = fgetc(file); if (c == '=') makeToken(">=", 0); else makeToken(">", 0); } else if (c == '=') { makeToken("=", 0); } else if (isdigit(c)) { numero(); } else if (isalpha(c)) { palavra(); } else { printf("O caracter '%c' na linha %d nao reconhecido.\n", c, linha); exit(EXIT_FAILURE); } lex.c: Temos duas novas funções, são elas palavra e numero: void palavra() { char palavra[100] = ""; int pos = 0; while (isalnum(c)) { palavra[pos++] = c; c = fgetc(file); } voltaPonteiro(); if (strcmp(palavra, "INIT") == 0) makeToken("INIT", 0); else if (strcmp(palavra, "PRINT") == 0) makeToken("PRINT", 0); else if (strcmp(palavra, "INPUT") == 0) makeToken("INPUT", 0); else if (strcmp(palavra, "VAR") == 0) makeToken("VAR", 0); else if (strcmp(palavra, "IF") == 0) makeToken("IF", 0); else if (strcmp(palavra, "ELSE") == 0) makeToken("ELSE", 0); else if (strcmp(palavra, "END") == 0) makeToken("END", 0); else makeToken("ID", 0); } Não é a função mais otimizada que você já viu, mas funciona: void numero() { int k = 0; while (isdigit(c)) { k = k * 10 + c - '0'; c = fgetc(file); } voltaPonteiro(); makeToken("T_INT", k); } Testamos o código agora: $ ./exe/main teste.txt <INIT, 0><PRINT, 0><T_INT, 1><+, 0><T_INT, 2><*, 0><T_INT, 3><END, 0> Olha só, reconhecemos a maior parte dos tokens de nossa linguagem! Agora que tal mais um teste utilizando outro teste.txt? INIT VAR max := 10 VAR num INPUT num IF (num < max) INIT PRINT 0 END ELSE INIT PRINT 1 END END $ ./exe/main teste.txt <INIT, 0><VAR, 0><END, 0><:=, 0><=, 0><T_INT, 10><VAR, 0><END, 0><INPUT, 0><END, 0><IF, 0> <(, 0><END, 0><<, 0><END, 0><), 0><INIT, 0><PRINT, 0><T_INT, 0><END, 0><ELSE, 0><INIT, 0> <PRINT, 0><T_INT, 1><END, 0><END, 0> Na próxima parte vamos fazer algumas alterações no analisador léxico e depois daremos início ao analisador sintático. Até lá. 🙂
  9. O professor, analista de sistemas e economista José Augusto N. G. Manzano lançou o livro "Algoritmos Funcionais - Introdução minimalista à lógica de programação funcional pura aplicada à teoria dos conjuntos". O livro busca fundamentar, dentro de seu escopo, diversas ações matemáticas e de programação importantes dentro do universo funcional. "Este livro foi escrito exclusivamente para uso em sala de aula. É um material voltado à apresentação e estudo da 'Lógica de Programação Funcional', que vem tomando grandes posições dentro do universo comercial no desenvolvimento de software", escreveu o autor em publicação no seu LinkedIn. Paradigma funcional – O paradigma funcional tem um jeito de programar específico, diferenciado do tradicional, com algumas vantagens para certas aplicações. Uma das áreas em que o paradigma funcional se destaca é a ciência de dados, que tem sido uma grande aposta das empresas, especialmente startups, na busca de profissionais. Dentro do paradigma funcional, há várias linguagens de programação. O Nubank, por exemplo, utiliza bastante uma linguagem chamada Clojure, e tem uma alta demanda por profissionais que programem nessa linguagem (saiba mais). Este livro, portanto, é uma introdução para quem quer adentrar nesse mundo das linguagens funcionais. Qualquer futuro programador em uma dessas linguagens funcionais vai se beneficiar deste conteúdo. Clique e veja como comprar O professor Manzano se destaca também por produzir muitos materiais da área, se mantendo sempre atualizado sobre o que o mercado está precisando no momento. Já divulgamos outros trabalhos de sua autoria aqui no Mente Binária, entre eles o livro 'Fundamentos em Programação Assembly': Inclusive, o canal do professor Manzano no YouTube possui mini aulas sobre programação de computadores e outros assuntos relacionados à área da computação e tecnologia da informação. Vale conferir!
  10. O Google está financiando um projeto do Internet Security Research Group para deixar toda a Internet mais segura. Trata-se de um módulo para o Apache HTTP web server (ou simplesmente httpd), que é um software livre, sendo o servidor web mais utilizado no mundo, escrito em linguagem C. A linguagem C, contudo, pode ser insegura, se tornando mais passível de bugs que representam falhas de segurança. De olho nisso e buscando tornar a Internet um local mais seguro, o Google decidiu financiar o projeto para desenvolver um novo módulo para substituir o mod_ssl do Apache. O módulo será responsável por suportar as operações criptográficas necessárias para estabelecer conexões HTTPS em um servidor web Apache. Assim, será utilizada uma alternativa mais segura de linguagem chamada Rust. O Internet Security Research Group afirma que planeja desenvolver um novo módulo chamado mod_tls que funcionará com o o mod_ssl, mas usará essa nova linguagem de programação Rust em vez de C. Segundo o ZDNet, a Rust é uma maneira simples e rápida de garantir que bilhões de usuários sejam mantidos em segurança nos próximos anos. Leia mais detalhes sobre o projeto (em inglês).
  11. O OEA Cyberwomen Challenge, evento realizado nos dias 10 e 11 de fevereiro, tem como missão promover a entrada do público feminino no mercado de cibersegurança. Este ano, o desafio será realizado em formato 100% online, organizado pela Trend Micro, Organização dos Estados Americanos (OEA) e o Governo do Reino Unido, com apoio da Womcy Latam. O evento é voltado para mulheres que estejam iniciando sua carreira em cibersegurança e topam o desafio de avaliar e mostrar suas capacidades e talentos. Nesta edição, na parte da manhã do dia 10 de fevereiro, a partir das 10h, será realizado um Painel de Tecnologia formado por executivas do mercado de TI e cibersegurança da América Latina e tratará de práticas de gestão e investigação. Estarão presentes no painel Adriana Shimabukuro, técnica do Núcleo Técnico de Combate aos Crimes Cibernéticos do Ministério Público Federal; Leticia Gammill, líder do time de Canais de Cybersecurity das Américas na Cisco e fundadora e presidente da Womcy; Claudia Anania, Head de TI do Instituto Butantan; Tamires Almeida, focada em pré-vendas de projetos de segurança da informação e líder do programa Womcy Mentoring para Mentorias Reversas; Rayanne Nunes, Coordenadora de Tecnologia na Trend Micro; e Barbara Marchiori de Assis, Consultora da OEA e outras empresas. Já no dia 11 de fevereiro, ocorrerá um workshop virtual com o desafio de defender um ambiente simulado com situações realistas de cibersegurança. O objetivo é proporcionar um aprendizado sobre situações como migração de aplicações, apps de orquestração e uso de Security as Code. O workshop terá a orientação de especialistas da Trend Micro e as participantes terão a oportunidade de explorar o networking com profissionais do mercado, além de receberem o kit de participação em sua casa. O desafio concederá ainda uma premiação para o grupo primeiro colocado, composta por um curso relacionado à área do desafio, a ser confirmado pela organização até a data de início do evento; licenças de Antivírus da Trend Micro por um ano; e mentoria de Carreira realizada pela equipe Womcy Mentoring. Para participar, as mulheres de áreas de segurança, arquitetas de segurança, infraestrutura, desenvolvimento ou operações precisam ter conhecimento prévio em Windows e Linux, redes, segurança; containers, pipelines e imagens; processo de DevOps; e conhecer as ferramentas DevOps (Github, Jenkins, ECR da Amazon, Kubernetes, Docker e APIs). Inscreva-se!
  12. Version 1a

    345 downloads

    Estruturas de dados lineares básica Abordagem prática, com implementações em C e Java. Valéria Maria Bezerra Cavalcanti Nadja da Nóbrega Rodrigues Sinopse Número de páginas: 296 ISBN: 9788563406613 Biografia do Autor Nadja da Nóbrega Rodrigues Nadja da Nobrega Rodrigues é mestre em Administração de Empresas (UFPB), especialista em Sistemas de Informação e Redes de Computadores (UFPB) e graduada em Ciência da Computação (UFPB). Trabalhou por 12 anos na indústria de TIC, fazendo parte de empresas públicas como SERPRO, DATAPREV e UFPB (NTI), além de ter atuado em várias empresas da iniciativa privada, dentre elas, a Confederação das Unimeds do Norte-Nordeste. Na área acadêmica, lecionou em faculdades nos estados da Paraíba e Pernambuco, e na UFPB. Atualmente é professora, pesquisadora e extensionista no IFPB, atuando nas áreas de Engenharia de Software, Sistemas de Informação e Inclusão Digital.
  13. 626 downloads

    Programação para leigos com Raspberry Pi / Elivelto Ebermam... [et al.]. – Vitória, ES : Edifes ; João Pessoa, PB : Editora IFPB, 2017. 290 p. : il. ; 21 cm. Inclui bibliografia. ISBN 97885________(broch.). ISBN 97885________(e-book). 1. Raspberry Pi (Computador) – Microcomputadores. I. Título. Autores: Elivelto Ebermam Guilherme Moraes Pesente Renan Osório Rios Igor Carlos Pulini
  14. Boa noite pessoal, estamos com vagas remotas para programador python fullstack com experiencia em AWS para atuar na area de arquitetura, nos micro serviços que se comunicam com nossos sdks de proteção de endpoint. Necessário: Mínimo de 3 anos de experiência em web services(fullstack) e 2 anos de experiência com ambiente aws Experiência com sanic ou fastapi na implementação de micro serviços Experiência com elasticsearch e kibana Experiência com bancos relacionais(postgree), key value(redis) e nosql(mongodb) Experiência com unit test, pylist ou outras ferramentas de qualidade Experiência com docker Experiência com elastic APM ou outros sistemas de monitoramento Difrenciais: Experiência com kafka Experiência com serviços de alta demanda(nossos endpoints chegama receber 5 mil requests por segundo) Experiência com front-end Interessados emilio.simoni@psafe.com
  15. Boa noite pessoal, estamos com vagas remotas para programador c++ para o time de ciber segurança da PSafe, a vaga é para atuar diretamente no nosso motor anti ransomware, criando novas features, acompanhando a evolução das ameaças e ajudando a criar formas comportamentais de proteção. Necessario: Mínimo de 3 anos em c/c++ com experiência em arquitetura de sistema operacional Experiência com Poco ou Boost, gtest ou outro framework de unit testing Conhecimento de funcionamento de malware, em especial ransomware Experiencia com windbg Diferenciais: Experiência com programação em kernel windows (mini filters, wfp, ...) ou mac Experiência com engenharia reversa Experiência com machine learning (scikit, tensorflow, xgb) Interessados emilio.simoni@psafe.com
  16. Version 2020

    222 downloads

    Introdução à Robótica Educacional com Arduino - HANDS ON! INICIANTE Autores Marcelo Eduardo de Oliveira Matheus Fernando Lima Zuccherelli Giovanni Polette Dalla Libera Renata Lima Zuccherelli de Oliveira Adriano Rogério Bruno Tech DOI: 10.11606/9786587023052 Pirassununga Faculdade de Zootecnia e Engenharia de Alimentos 2020
  17. 208 downloads

    Conci, Aura Javascript para construção de páginas de Web / Aura Conci; João Sérgio Assis - Niterói, RJ: Editora da UFF, 2012. p. : 23 cm. — (Coleção Didáticos EdUFF, 2004) Bibliografia. p. 229 ISBN 978-85-228-0535-8 1. Javascript. 2. Construção de páginas de Web. I. Conci, Aura. II. Assis, João Sérgio. III Universidade Federal Fluminense. IV. Título CDD 370” Excerpt From: Aura Conci e João Sérgio Assis. “Javascript para construção de páginas de Web.” Apple Books.
  18. Version 1.0.0

    597 downloads

    Livro do Julio Cezar Neves com dicas importantes (e raras de serem encontradas) sobre shell, incluindo sincronismo de processos, novidades do Bash 4.0, uso do ImageMagik e YAD (o melhor da categoria dos dialog da vida). Vale ler cada palavra. ? E se quiser ver se tem turma aberta do curso dele é só clicar aqui. ?
  19. Olá, neste artigo compartilharei um pouco da minha pesquisa no desenvolvimento de debuggers. No momento estou trabalhando em um protótipo de debugger para Linux, mas nada tão avançado quanto um gdb ou radare (muitas coisas são necessárias para chegar neste nível de maturidade de software). O desenvolvimento de debuggers é uma atividade muito interessante, já que, em sua forma mais básica, pode ser resumido em uma série de chamadas de sistema (syscalls) para que seja possível o controle do processo a ser depurado (muitas vezes chamado de debuggee) e de seus recursos, mas não vamos colocar a carroça na frente dos cavalos e vamos em partes. Antes de começarmos a discutir detalhes mais específicos acerca da depuração de processos, é necessário um entendimento básico de como os mesmos se comunicam na plataforma que vamos desenvolver o tal debugger, no nosso caso, UNIX-like. Inter-process communication (IPC) IPC é uma forma que processos podem utilizar para se comunicar dentro de um sistema operacional. Existem diversas maneiras de comunicação: via sinais (signals), sockets, etc, mas para a criação de um debugger é apenas necessário usar sinais para a execução. Sinais funcionam como uma notificação que pode ser enviada à um processo específico para avisar que algum evento ocorreu. É possível também programar um processo para reagir aos sinais de maneira não padrão. Se você já teve um uso razoável de Linux, você provavelmente já enviou sinais à um processo. Por exemplo, quando você aperta Ctrl+C para interromper a execução de um processo, é enviado um sinal do tipo SIGINT, que nada mais é que uma abreviação para Signal Interruption. Se o processo em questão não está preparado para reagir a este sinal, o mesmo é terminado. Por exemplo, considere o seguinte código: #include <stdio.h> int main(void) { while(1) printf("hi\n"); return 0; } Ao compilar e executar o código acima e apertar Ctrl+C, o mesmo encerra como esperado, porém podemos verificar que um SIGINT foi enviado usando a ferramenta ltrace, que além de listar chamadas a bibliotecas também mostra os sinais enviados ao processo: $ gcc -o hello hello.c $ ltrace ./hello Rode o comando acima e aperte Ctrl+C para verificar o sinal enviado! Programando reações a sinais A capacidade de enviar sinais a um processo nos dá a possibilidade de saber o que esta acontecendo com algum processo específico que estejamos depurando. Para programar reações a algum tipo de sinal, podemos incluir a biblioteca signal, para que possamos usar a função e estrutura (struct) sigaction: struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }; int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); A struct sigaction nos permite adicionar handlers (tratadores) para nossos sinais, enviando o endereço de nossa função que realiza algum tipo de ação baseada no sinal enviado para o campo sa_handler(sigaction handler). Um handler neste contexto nada mais é que uma função que sempre vai ser chamada quando um dado sinal for enviado, dessa maneira podemos executar alguma ação quando recebermos um sinal. Já a função sigaction recebe o número do sinal, porém uma série de macros já são pré-definidas e podemos passar como argumento apenas o nome do sinal, como SIGINT por exemplo. A função recebe também a referência da struct previamente definida (struct sigaction) e, caso precise trocar um handler por outro, também recebe no último argumento (oldact) o handler anterior, para que possa ser feita a troca pelo novo. Como não é o nosso caso, vamos passar NULL neste último argumento. O código abaixo simula um uso de handlers de sinais, que imprime uma mensagem quando um sinal é enviado: #include <stdio.h> #include <signal.h> #include <unistd.h> // sleep void simple_handler(int sig) { printf("Hello SIGINT\n"); } int main() { struct sigaction sig_handler = { simple_handler }; sigaction(SIGINT, &sig_handler, NULL); sleep(1000); return 0; } Ao executar o código acima, aperte Ctrl+C e veja que será imprimido a mensagem do nosso handler! O manual da signal contém uma tabela com todos os sinais usados por sistemas POSIX. Para enviarmos sinais facilmente em sistemas UNIX podemos usar o comando kill: $ kill -l O comando acima mostra todos os sinais e seus respectivos números, com isso podemos fazer algo interessante. Por exemplo, rode o código acima em um terminal separado e use o kill para se comunicar com o seu processo, assim: $ ps ax | grep simple_signal $ kill -2 <pid> Primeiro buscamos o PID do nosso processo então usamos o kill que espera como primeiro argumento numero do sinal (listado em kill -l) e o segundo o PID do processo alvo. Ao enviar o sinal, podemos ver que o nosso código reage aos sinais que foram associados a um handler especifico! Tente criar handlers para vários sinais e teste usando o comando kill. ? Abaixo um código para demonstrar um uso real de um software que escreve dados aleatórios nos arquivos temporários e antes de uma finalização abrupta, é deletado o que foi usado: #include <stdio.h> #include <signal.h> #include <unistd.h> // Log errors void fatal(const char* err_msg) { fprintf(stderr, "Error: %s\n", err_msg); } // Escreve algo random em um arquivo void random_work() { FILE* temp_files = fopen("/tmp/foo", "w"); if (!temp_files) { fatal("Cant open foo!"); } else { fprintf(temp_files, "%s", "Random random random!\n"); fclose(temp_files); } } // Handler para deleta arquivos criados void handler_termination(int sig) { // Verifica se existe usando a function access // Caso existe usa a syscall unlink para remover o arquivo if (access("/tmp/foo", R_OK) < 0) return; unlink("/tmp/foo"); printf("All clean! closing...\n"); } int main() { //struct sigaction que recebe a function handler_termination como valor do seu handler struct sigaction interruption_handler; interruption_handler.sa_handler = handler_termination; // Syscall sigaction que associa o nosso handler para um sinal especifico // O ultimo campo NULL, espera o handler anterior para que posso tornar o novo handler o default sigaction(SIGINT, &interruption_handler, NULL); random_work(); sleep(1000); handler_termination(0); return 0; } Dica: Dê uma olhada na tabela de sinais e crie handlers para o mesmo código acima! Para a construção do nosso debugger iremos focar mais no signal SIGTRAP, para que seja possível detectar se o nosso processo sofreu uma "trap" da CPU. Uma trap ocorre quando acontece alguma interrupção síncrona na execução, que faz o processo ficar parado até que o sistema operacional execute alguma ação. Isto será usado para implementar e interpretar breakpoints. Veremos tudo isso com mais detalhes em breve! Sinta-se livre para comentar e sugerir correções e melhorias. Até o próximo artigo! Links úteis: Syscall IPC CERO 11 – Linux Syscalls Syscalls, Kernel mode vs User mode Programação em C
  20. Olá! No artigo anterior falamos sobre Signals, que é de suma importância para a comunicação entre processos, mas para construir o nosso debugger precisamos muito mais do que apenas isso, precisamos de fato ter total controle sobre um dado processo e se possível controlar até o seu própio início. Neste artigo será explicado o que são forks e seu uso em desenvolvimento de aplicações em sistemas UNIX. Sem mais delongas, vamos prosseguir!!!? Resumidamente a syscall fork é usada para a duplicação e criação de um processo. Quando um dado processo chama a função fork(), é criada uma cópia idêntinca de seus dados. Note que apenas uma cópia é feita, o processo filho não compartilha o mesmo espaço de memória do pai. A syscall fork retorna um PID que é usado para indetificar em qual processos estamos e também dar acesso ao ID do processo filho. Caso o PID seja 0 estamos executando no filho, caso seja qualquer outro somos o processo pai, isso ocorre pois o pai precisa saber o PID do filho, mas o filho não necessariamente precisa saber o seu própio (da mesma maneira que o seu processo não sabe o própio PID ao menos que o mesmo peça). Algo interessante de se notar é que os Init System usados para subir e gerenciar serviços de sua máquina trabalham dessa mesma maneira, você pode checar sua árvore de processo usando comando pstree: $ pstree Dessa maneira você tem uma representação bem visual de como está dividida a sua estrutura de processos ?. Note que todos os processos são filhos do seu Init system (seja ele SystemV, Systemd, etc). Aconselho você explorar o comando pstree para uma visão bem mais detalhada do seu sistema! Outra abordagem é usar o própio comando ps: $ ps -ef Rode o comando acima (dependendo da quantidade de processos use um pipe para o less ?) e com ele teremos uma visão mais detalhada. A coluna PID representa o ID do processo em si e a coluna PPID representa o "Parent Process ID", que nada mais é que o ID do processo pai. Note que o PID 1 é o seu Init System e os seus processos rodam como filho dele! Vale notar que o processo Pai do própio init é o PID 0, que é conhecido como "swapper" ou "scheduler", que é o processo responsavel para realização de paging. Paging é o sistema de gerenciamento de memória que salva os dados da RAM em uma memória secundária (HD, SSD e etc) e recupera em formato de páginas (outros PID também são filhos do propio PID 0 como PID 2 que gerencia todas as threads que rodam em Kernel Land(KThread) etc). Programando Forks A syscall fork está na lib <unistd.h> (Unix Standard library) e tem a seguinte construção: #include <sys/types.h> #include <unistd.h> pid_t fork(void); Precisamos incluir a lib <sys/types.h> para que seja possivel acessar o tipo pid_t. A função fork não espera nenhum parâmetro para a sua construção e o código abaixo demonstra o quão simples é cria um fork. #include <stdio.h> // Acesso a syscall #include <unistd.h> // Acesso ao tipo variavel pid_t #include <sys/types.h> int main(void) { int x; printf("Processo normal...\n"); printf("Forking...\n"); sleep(5); pid_t pid = fork(); x = 40; if (pid == 0) { printf("Eu sou o processo filho meu PID: %d\n", pid); } else { printf("Eu sou o processo pai de %d\n", pid); } sleep(5); return 0; } Compile o código acima da seguinte forma: $ gcc -o fork fork.c $ ./fork Note que o código se "divide" a partir da chamada fork e um if é usado para saber se estamos executando no pai ou no filho, note também que o pai sabe o PID e o filho não. Para melhor visualização o código acima roda por 10 segundos (por conta da chamada ao sleep com esse tempo de espera). Abra um outro terminal e rode o comando: $ watch -n1 pstree O comando acima vai executar o pstree a cada 1 segundo, desta forma você verá o exato momento da criação do fork. Comunicando-se com o processo fork Agora imagine que um processo precisa esperar o seu filho terminar algum trabalho e dependendo do seu sinal o processo pai realiza alguma ação. A comunicação entre o processo pai e o filho se da por signals. O pai pode saber exatamente o estado do seu processo filho usando a syscall wait e waitpid, ambas na lib <sys/wait.h>: #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); A syscall wait espera que ao menos 1 de seus processos filho troque de estado, já a waitpid espera por um processo específico. Como sabemos exatamente qual processo queremos rastrear iremos usar esta call ?: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main(void) { printf("Spliting work...\n"); pid_t pid = fork(); if (!pid) { int a = 0; for(int i = 0; i < 100000000; i++ ) { a += i*2 + 10 *i; } return 9; } int status; int signal; printf("Waiting child finish work...\n"); waitpid(pid, &status, 0); if (WIFEXITED(status)) { signal = WEXITSTATUS(status); printf("Child exited, status = %s\n", strsignal(signal)); } return 1; } Compile o código acima e execute: $ gcc -o work work.c $ ./work Spliting work... Waiting child finish work... Child exited, status = Killed Veja que após a chamada de fork nosso processo filho executa várias iterações e realiza um cálculo (um cálculo totalmente randômico) e após isso retorna 9. Este retorno em questão é apenas por motivos educativos (no artigo anterior falamos de sinais e como eles funcionam). O processo pai usa a syscall waitpid para esperar que qualquer signal seja enviada do pid especificado. Após receber um status é verificado se o fork saiu (WIFEXITED) e se sim, pegamos o signal enviado usando WEXITSTATUS(status da saída) e usamos a chamada strsignal(provida pela string.h) para recuperar uma versão em texto do signal. Nesse caso iremos recuperar o signal "KILLED", pois colocamos 9 apenas por razões educativas. Normalmente se tudo ocorreu bem colocamos 0 (inclusive é dessa maneira que sua shell avalia se o programa rodou certo). $./work && echo "Filho saiu com 0, tudo certo..." || echo "Filho saiu com 1, algo errado..." No caso acima a nossa shell irá criar um fork do nosso work, executar o nosso programa (que por sua vez também executa um fork mas não entra em questão aqui) e se o signal retornado pelo fork for 0 ele imprime uma mensagem, caso contrario ele imprime uma mensagem de erro, dessa maneira você pode orquestrar um shell scripting usando o própio retorno do processo ? Tente mudar o retorno do fork acima e verifique seu status usando funções providas pela <sys/wait.h>. No exemplo acima usamos apenas a call WIFEXITED e WEXITSTATUS, mas existem várias outras. Forks são de extrema importância para criação e gerenciamento de processos e iremos usar forks para que seja possível executar o programa que queremos debugar, dessa maneira o software em questão vai ser filho do nosso debugger, o que nós da total controle sobre o mesmo. Comentarios são todos bem vindos e todos os códigos usados estão disponíveis no github! ? Links úteis: Process Control fork wait Process State Fork Bomb - Cuidado com isso
  21. Olá, já faz um bom tempo desde do ultimo artigo sobre a construção de debuggers mas, sem mais delongas, vamos dar continuidade a esta série! ? Neste artigo iremos falar um pouco sobre uma chamada de sistema que é capaz de controlar quase todos os aspectos de um processo: a syscall PTRACE (process trace). Antes de continuarmos, vale ressaltar que todo o código utilizado neste artigo está disponível no repositório do Github. De acordo com o manual do Linux (man ptrace), a syscall ptrace é definida assim: "A syscall ptrace provê meios para que um processo (denominado "tracer") possa observar, controlar a execução de um outro processo (denominado "tracee"), examinar e modificar a memória e registradores do "tracee". É primariamente utilizado para a implementação de 'breakpoint debugging' e para rastreamento de syscalls". Em outras palavras, podemos utilizar a ptrace para controlar um outro processo sobre o qual termos permissões sobre! Por exemplo, execute: strace /bin/ls O comando "strace" acima, é utilizado para que se possa rastrear todas as syscalls que um programa realiza. Vale lembrar que toda a técnica utilizada para o rastreamento de syscalls envolve o conteúdo abordado nos artigos anteriores, então é de suma importância que você tenha lido (ou saiba) o primeiro artigo sobre Sinais e o segundo sobre Forks. Antes de começar a rastrear um dado comando, o strace precisa ter controle total sobre a execução do processo alvo, para isso é feito um fork do processo em questão e o mesmo é "traceado". Voltaremos neste assunto em breve. A wrapper da ptrace é definida em <sys/ptrace.h> e tem o seguinte protótipo: #include <sys/ptrace.h> long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); Onde o primeiro argumento request é um enum onde cada valor define uma ação em cima do "tracee", tais como TRACEME, GETEREGS, SETREGS e etc. O segundo argumento, pid, é o PID (Process Identification) do processo que queremos "tracear", o terceiro argumento addr é um endereço para alguma interação que a ser realizada da memória do processo "traceado" e o quarto e último argumento data é algum tipo de dado passado para o processo. Agora que você ja conhece o formato desta syscall, vamos fazer um pequeno breakdown do comando "strace". Execute: strace strace /bin/ls 2>&1 | grep -A2 clone Por mais bizarro que o comando acima pareça, o que vamos fazer aqui é rastrear todas as syscalls que o strace faz usando o próprio strace! Como a saída padrão do strace não é o stdout (dê uma lida em standart streams, caso esteja confuso) então é primeiro redirecionar a saída de erro para a saída padrão, para que seja possível rodar o grep no que queremos. Estamos buscando aqui, alguma chamada a syscall clone, que é sempre chamada quando é feito um fork. A chamada à ptrace vem logo em seguida: clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c4aa8ea10) = 16203 ptrace(PTRACE_SEIZE, 16203, NULL, 0) = 0 Nesse caso, o strace cria um processo filho e em seguida usa o ptrace com o argumento SEIZE para iniciar o rastreamento (tracing) de um processo sem interrompê-lo, como analisaremos em seguida. Dessa maneira o strace é capaz de interceptar cada chamada de sistema feita pelo processo! Dê uma olhada no comando ltrace, que diferente do strace, rastreia todas as chamadas à bibliotecas (libraries trace) e tente fazer o mesmo que fizemos acima! Algumas ações notáveis que podemos fazer com a ptrace: PTRACE_PEEKTEXT, PTRACE_PEEKDATA Ler uma word em um dado endereço. PTRACE_POKETEXT, PTRACE_POKEDATA Copiar uma word para um determinado endereço (injete dados na memória). PTRACE_GETREGS Ler os registradores de um processo, que será guardado na struct user_regs_struct em <sys/user.h>. PTRACE_SETREGS Escrever nos registradores de um processo (também no formato da struct acima). Execute "man ptrace" para uma abordagem mais detalhadas de todos os valores disponíveis. ? Implementando um simples tracer Agora que já temos uma base de forks e uma ideia de como o ptrace funciona, podemos unificar os dois e tenho certeza que o ptrace irá ficar mais claro. A partir de agora ele é fundamental para a implementação do nosso debugger. O primeiro passo é definir o escopo de como será feito o nosso "tracer": vamos rastrear um processo que já esta sendo executado ou vamos criar um novo? Para o nosso debugger, iremos apenas criar um fork e trocar sua imagem de execução para a do programa que queremos debugar, usando uma das funções da família exec. Primeiro vamos usar a função execl, que faz parte do leque de funções exec (man 3 exec) que trocam a imagem do nosso processo por outra, ou seja, o nosso programa é realmente trocado por outro em uma execução. A função execl é definida como: #include <unistd.h> int execl(const char *pathname, const char *arg, ... /* (char *) NULL */); Onde o primeiro argumento pathname é caminho completo do nosso executável alvo e os demais argumentos, que podem ser vários, são os argumentos para o programa que será executado. Para seguir um padrão, o primeiro argumento que geralmente colocamos é o caminho do programa em questão (lembrem que no array argv a posição 0 guarda o nome do programa em si), o resto dos argumentos são opcionais e seguem no modelo de lista de argumentos que são delimitados por um argumento NULL, que geralmente usamos para finalizar a lista. Agora considere o seguinte exemplo: #include <unistd.h> #include <stdio.h> int main(int argc, char* const* argv) { if (argc < 3) { printf("Usage: %s <command> <args>\n", argv[0]); return 1; } const char* command = argv[1]; char* const* args = &argv[1]; printf("First arg => %s\n", args[0]); execv(command, args); puts("Continua?\n"); return 0; } Compile com $ gcc -o exec exec.c $ ./exec /bin/ls -lah Este programa bem simples demonstra como a exec funciona. O que acabamos de criar aqui foi uma espécie de wrapper para qualquer comando: ele irá pegar o nome do comando e os seus respectivos argumentos e trocar sua execução atual pela a que você especificou. Note também a string "Continue?" que deveria ser impressa na tela. Esta nunca será impressa pois o nosso programa virou de fato, outro. Interessante, não? Usando um pouco de criatividade, podemos criar novos processos filhos combinando forks + exec, ou seja, criamos um fork do nosso processo e trocamos sua imagem por outra! Dessa maneira, por exemplo, temos total controle sobre o comando ls. Modificando um pouco o código acima e seguindo a ideia de forks, temos: #include <stdio.h> #include <sys/types.h> #include <sys/ptrace.h> #include <unistd.h> int main(int argc, char* const* argv) { if (argc < 3) { printf("Usage: %s <command> <args>\n", argv[0]); return 1; } const char* command = argv[1]; char* const* args = &argv[1]; pid_t child_pid = fork(); // Neste ponto, todas as variaveis sao copiadas para o nosso fork // o fork NAO recebe as mesmas variaveis, apenas uma cópia ;) if (!child_pid) { // Hora de transformar nosso fork em outro programa ptrace(PTRACE_TRACEME, NULL, NULL, NULL); execv(command, args); } char in; do { puts("Iniciar processo ? [y/n]: "); in = getchar(); } while (in != 'y'); ptrace(PTRACE_CONT, child_pid, NULL, NULL); return 0; } Compile $ gcc -o fork_exec fork_exec. $ ./fork_exec /bin/ls O programa acima realiza os primeiros passos do nosso tracer: é passado o caminho de um programa e os argumentos para o mesmo. Com isso criamos um fork e usamos o ptrace no própio fork com o argumento TRACEME. Este parâmetro indica que o este processo será "traced" pelo seu processo pai. Em seguida trocamos a nossa execução para o nosso programa alvo. Neste momento temos total controle sobre a execução, no exemplo acima, do comando ls. Quando um processo inicia sua execução com TRACEME + exec, o mesmo recebe um sinal de interrupção (SIGTRAP) até que o seu processo pai indique que ele deve continuar sua execução. Por isso, o nosso processo pai, que retém o PID do processo filho, usa o ptrace com o argumento CONT para que seja enviado o signal para dar continuidade de execução. E depois? Agora toda a comunicação entre os processos pai e o filho se dará via sinais e usaremos a syscall wait constantemente. Lembra que definimos acima algumas funções que podemos usar em conjunto com a ptrace? Para já irmos adiantando alguns artigos, vamos fazer um programa que mostra o estado dos registradores para um processo, passo a passo. Vamos usar dois parâmetros para a ptrace: GETREGS e STEP. Segue o código: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/user.h> #include <sys/wait.h> void display_regs(struct user_regs_struct* regs) { printf("RIP: 0x%x\n", regs->rip); printf("RBP: 0x%x\n", regs->rbp); printf("RSP: 0x%x\n", regs->rsp); } int main(int argc, char* const* argv) { if (argc < 2) { fprintf(stderr, "Usage: %s <program_path>\n", argv[0]); return 1; } const char* progName = argv[1]; pid_t child = fork(); if (!child) { ptrace(PTRACE_TRACEME, NULL, NULL, NULL); execl(progName, progName, NULL); } int status; int options = 0; int signal; // Estrutura que mantem os registradores struct user_regs_struct regs; /// Capta primeiro sinal de parada do filho waitpid(child, &status, 0); signal = WSTOPSIG(status); if (signal == SIGTRAP) { printf("Processo alvo %s esperando pronto para iniciar\n\n", progName); } printf("Executando 10 instruções\n"); for (int i = 0; i < 10; ++i) { printf("Passo: %d\n", i+1); // Executa uma instrução ptrace(PTRACE_SINGLESTEP, child, NULL, NULL); // Espera sinal do filho waitpid(child, &status, 0); // Copia o estado atual dos registradores ptrace(PTRACE_GETREGS, child, NULL, &regs); // Função local para imprimir os principais registradores display_regs(&regs); puts("\n\n"); } puts("Continuando...\n"); /// Continua execução ptrace(PTRACE_CONT, child, NULL, NULL); waitpid(child, &status, 0); printf("Filho saiu com %d\n", WIFEXITED(status)); return 0; } Compile: $ gcc -o tracer tracer.c $ ./tracer /bin/ls O código acima, além de criar e rastrear o processo, executa as primeiras 10 instruções e copia os estados dos registradores em cada passo. Logo após, continua a execução do programa normalmente. A estrutura user_reg_struct, definida em <sys/user.h>, contém todos os registradores que estão disponíveis na sua arquitetura. O código foi escrito considerando um ambiente x86-64. Com o estudo da ptrace, fechamos toda a introdução para construirmos o nosso debugger de fato, que vamos começar a desenvolver no próximo artigo, incialmente com capacidade de por breakpoints, imprimir o atual estado dos registrados e executar instrução por instrução do processo. Qualquer dúvida ou correção sinta-se livre de por nos comentários! ? Links úteis: Process control Process relationship Code injection with ptrace Sinais Fork Até a próxima!
  22. Comecei a estudar a linguagem Go há alguns dias e fiquei muito impressionado com seus recursos. A facilidade para programação paralela, o fato de ter ponteiros, funções que retornam mais de um valor, código enxuto (se você declarar uma variável e não usar, o programa nem compila!) e outros realmente me encantaram. Recentemente precisei disassemblar um trecho de código de um binário PE para um projeto que está escrito em Go. Vi que existem algumas bibliotecas prontas para serem usadas, como gapstone (bindings da Capstone) e go-zydis (bindings da Zydis) mas não encontrei uma nativa. No entanto, vi que existe uma ferramenta nativa no toolset da linguagem similar ao objdump do GNU binutils: $ go doc cmd/objdump Objdump disassembles executable files. Usage: go tool objdump [-s symregexp] binary Objdump prints a disassembly of all text symbols (code) in the binary. If the -s option is present, objdump only disassembles symbols with names matching the regular expression. Compilei um "hello, world" em Go só pra ver: ~/hello $ cat main.go package main import "fmt" func main() { fmt.Println("menteb.in") } ~/hello $ go build E de fato o objdump da Go funciona: ~/hello $ go tool objdump hello | head TEXT go.buildid(SB) :-134217728 0x1001000 ff20 JMP 0(AX) :-134217728 0x1001002 476f OUTSD DS:0(SI), DX :-134217728 0x1001004 206275 ANDB AH, 0x75(DX) :-134217728 0x1001007 696c642049443a20 IMULL $0x203a4449, 0x20(SP), BP :-1 0x100100f 226d35 ANDB 0x35(BP), CH :-1 0x1001012 4c6f OUTSD DS:0(SI), DX :-1 0x1001014 6a52 PUSHL $0x52 :-1 0x1001016 436e OUTSB DS:0(SI), DX :-1 0x1001018 4a31794f XORQ DI, 0x4f(CX) Mas ao tentar com o um PE compilado pra 64-bits, descobri que só funciona com binários feito em Go. ? $ go tool objdump putty.exe objdump: disassemble putty.exe: no runtime.pclntab symbol found De qualquer forma, resolvi olhar o código-fonte deste objdump interno da linguagem pra ver qual é dessa mandinga. Na linha 43 do main.go do objdump tem um import pra uma biblioteca chamada objfile. Pensei: Wow, deve ser uma biblioteca de disassembly, talvez eu possa alterar ! E na hora já criei um projeto tentando usá-la mas fui surpreendido com um errão! kkkk ~hello $ cat main.go package main import "fmt" import "cmd/internal/objfile" func main() { fmt.Println("menteb.in") } ~hello $ go build main.go:4:8: use of internal package cmd/internal/objfile not allowed Não pesquisei muito sobre essa história sobre eu não poder usar um pacote interno (por quê o objdump pode e eu não posso?!), mas fui olhar esta objfile e terminei encontrando seu fonte. Para minha alegria, neste arquivos disasm.go vi os seguintes imports: "golang.org/x/arch/arm/armasm" "golang.org/x/arch/arm64/arm64asm" "golang.org/x/arch/ppc64/ppc64asm" "golang.org/x/arch/x86/x86asm" Agora sim, carái! É tudo público e posso usar. Desculpe o desabafo.. hehe o artigo na verdade começa aqui mas quis contar como cheguei porque né. ? Cada uma dessas bibliotecas possui uma função Decode() justamente pra decodificar uma instrução (tipo Inst). Testei com um NOP em 64-bits, só pra ver: package main import ( "fmt" "log" "golang.org/x/arch/x86/x86asm" ) func main() { dados := []byte{0x90} ins, err := x86asm.Decode(dados, 64) if err != nil { log.Fatalln(err) } fmt.Println(ins) } A saída foi exatamente a esperada: $ ./hello NOP Show. Agora é abrir um PE, ler de onde quero e daí disassemblar usado essa x86asm.Decode() num loop, mas vou deixar esse exercício aí pra quem quiser treinar Go. Ou se acharem útil posso postar um aqui mais tarde. Aqui já funcionou mas precisa de uma polida. ? Perceba também que há bibliotecas para ARM e PowerPC. Achei bem maneiro. Talvez em breve o time da Go adicione suporte a mais arquiteturas. Amém! ?
  23. https://bookauthority.org/books/new-networking-books E aí, concordam com a lista acima? Confesso que muitos títulos me chamaram a atenção, mas antes de fazer algum movimento imprudente ($$), gostaria de ouvir alguma opinião de alguém que possa ter tido a oportunidade de ter comprado, lido, analisado, etc., um ou mais dos títulos da lista. Se alguém puder fornecer algum pdf, mesmo que seja prévia, também serei grato. P.S: Os livros de C e Python particularmente me interessaram...
  24. until
    Dia 02/04/2019 (terça) tivemos o lançamento oficial do Visual Studio 2019, com o anúncio de inúmeras novidades envolvendo o desenvolvimento de soluções baseadas em tecnologias como Azure DevOps, .NET Core, ASP.NET Core, C# e PowerShell. Assim como aconteceu em outras ocasiões, a Microsoft novamente fará uma parceria com comunidades técnicas através da realização de eventos locais. O DevOps Professionals em conjunto com a FC Nuvem também participa desta iniciativa, com um EVENTO PRESENCIAL e GRATUITO Programação prevista (grade sujeita a alterações): - Novos Recursos para Debugging no Visual Studio 2019 + Suporte a Docker no .NET Core 3.0 - Renato Groffe (Microsoft MVP) - Dicas e truques com Azure e Azure DevOps no Visual Studio 2019 - Vinicius Moura (Microsoft MVP) - Colaboração Contínua com o Visual Studio Live Share - Milton Câmara Gomes (Microsoft MVP) - Indo além de ambientes Windows com PowerShell Core, Linux e Visual Studio Code - Ewerton Jordão (.NET SP, SampaDevs) Acompanhe e apoie esta iniciativa, divulgando e indicando o Visual Studio 2019 Launch para amigos e colegas de trabalho! Mais informações: https://www.sympla.com.br/visual-studio-2019---lancamento---devops-professionals--fc-nuvem__525409
  25. Alguém me indica um bom livro para aprender programação utilizando sockets em C? Pode ser em inglês. Desde já agradeço!
×
×
  • Create New...