Showing posts with label tutorial. Show all posts
Showing posts with label tutorial. Show all posts

Wednesday, July 19, 2017

ToolKit Swiss - Chrome Extension

Hello there!

Recently I have been working on a chrome extension that helps me on internet researching and navigation. So I coded in a way that makes easy to write new tools and I have only spend time on the tool itself, no needing to write the whole extension again for new tools that I may need.

The extension is still in developing, but only do it in my free time. I think it can be useful for someone that want the same functionality or want to code chrome extensions, there is a plenty of examples that can be useful.

This post I tried to cover the steps that I use to code a new tool, showing how my "ToolKit Swiss" works. I posted the version 0.1 that was pretty rustic and "boilerplating". The version 0.2 it is more easier to implement and have new functionalities. Soon as I work on it will be better and more flexible. If you have any ideas you can contact me I would like to help if needed.

Well, let's do it!

Design Overview




So in this diagram you can see how the classes interact with each other. ToolKit runs on background intercepting all actions with the context menus created and the rest is injected on page. ToolKit provide all the resource the tool need. Each tool have it's own JSON with the html creation logic that is provided to the FrontEnd that passes it to the ElementBuilder to build the html.

In the next example I will try to demonstrate the flowchart of the ToolKit Swiss. From the start to the tool execution. The tool inside the ToolKit has freedom with all DOM elements since it was injected on the page.



By the way the source is commented and I will export to better JSDOC documentation soon as I could. I will try to discuss about it in general through the new tool creation.

Some parts of the tool creation is still hard coded in some classes. My goal is to make it more flexible soon as I can, until there it's needed to hard code inside the class.

Base64 Encode-Decode

I will discuss about the hard coded part first, where it's needed to add code for make it functional on the ToolKit Swiss.

First of all lets make the hard coded part on the classes. First we need the ID for the new tool let's add it on the types.js:




Now we have to create the Context menu at toolkit.js toolContextCreate_:



And the function event to handle the context click:



Well, that's it. Now let's to the tool itself. First I begin with the element JSON (html logic). There are elements that need to be fixed on the JSON elements of the tool, I will improve it soon as I can.

Let's copy the template.json and alter to our purpose since the layout is what I need. Important thing is that all components used in the tool construction must be at class property of the component-html tag.



It's important to change the "toolkit-block-base64" (Line 12) for each new tool. I forget to comment in this line on the template.json.

To start a tool the code we need is:




I will just pass through the key parts of the Tool. Since I fully commented the Tool class you can have a very good base of what is capable of. And all my tools are commented as well. Any question just send it to me I would be glad to help.

Any doubts just see at the tool.js. Everything is there.


  • Line 11
    • init must be set in every tool. FrontEnd call this constructor when it loads the tool;
  • Lines 14-15:
    • This property is mandatory for internal purposes;
  • Line 26:
    • Like a said before, your components must be in the class property of the html TAG;
  • Line 31:
    • To have this functionally you must set a DIV on the JSON element with class 'error-raise';
  • Line 43:
    • Every new tool must to do this to inherit the properties of the Tool;
  • Line 50-51:
    • This is mandatory tool. ctxFront is a instance of FrontEnd;
    • initTool_ initializes the tool on FrontEnd for interaction;
    • getElements_ make the FrontEnd call for the elements of the tool invoked;


For a better view of all functionalities I recommend to see the translate tool. Translate tool it's more complete over the functionalities available. Anything just contact me.




Thanks!

Monday, July 10, 2017

Basicando o Assembly

Introdução ao Assembly

Fala galera, eu aqui de novo trazendo mais tópicos básicos de engenharia reversa, básicos por enquanto, viu ?! Quero trazer algo mais desafiador nos posts futuros, porém deve haver uma introdução primeiro. Desculpem qualquer bobeira que eu disser durante o post e se eu fizer algo por favor fiquem à vontade para me comunicar. Engenharia reversa só pode ser feita com conhecimento em Assembly, não tem outro jeito, ou aprendemos ou aprendemos.

Caso você não tenha lido o meu último post, eu recomendo. Eu tento abordar de uma forma mais dinâmica a computação. Então tudo no fim é assembly como você já deve saber, não importa que linguagem você utiliza ela vira assembly no fim ou no mínimo é interpretada através dela. De qualquer modo é bom saber assembly, fora que não é tão difícil entender assembly ainda mais se você já usou alguma linguagem funcional, como pascal por exemplo.

Assembly não é universal, diferentes assemblers compilam para assembly de diferentes formas e com base na arquitetura utilizada. Afinal alguns processadores interpretam as instruções de forma diferente, assim como é nas linguagens de alto nível. O que muda é a sintaxe, os conceitos permanecem.  Nas minhas postagens trarei a arquitetura IA-32 (que é mais comum pelo mundo afora). Lembrando que esse post não é uma aula completa, mas abordarei os conceitos para que possamos iniciar posts mais técnicos.

Como vimos no meu post passado, o computador trabalha com o conceito de Memória-Dados X Instruções. Ele armazena tudo na memória e depois interpreta os dados e instruções de acordo com a necessidade e a forma como comandamos ele. Se você passa determinado para a CPU dizendo que é pra ela executar, ela vai tentar executar. Mais pra frente veremos como isso acontece.

 
Computador / Memória, CPU, Dispositivos IO(Entrada-Saida) e BUS

O processador possui alguns ponteiros que são utilizados para ajudar a CPU durante toda a sua execução, uns apontam para as instruções e outros para os dados. Basicamente é o que eu já havia dito, faça determinada operação com os determinados dados. Então temos o ponteiro de instrução instruction pointer e o ponteiro de dados data pointer. Nós trabalharemos muito com eles nos posts futuros. O ponteiro de instrução como o nome diz, aponta para a memória que contém as instruções e o ponteiro de dados aponta para a memória que contém os dados, e com essas informações a CPU faz os procedimentos necessários.

 
Unidades da CPU X Memória

Não se assustem explicarei em mais detalhes. A CPU guarda os valores desses ponteiros em registradores (registers). Conforme o processador executa as instruções o ponteiro de instrução (instruction pointer) aponta para a próxima instrução, assim como o ponteiro de dados. Uma instrução possui entre 1 e 3 bytes e é chamada de opcode (operation code(código de operação)). Então basicamente o assembly possui 3 partes principais, opcodes (operações), data sections(seções de dados) e directives.

Opcode


Código mnemônico, é uma representação mais compreensível dos opcodes. É uma forma mais tranquila para se ler do que o código de máquina. No exemplo abaixo temos do lado esquerdo o código de máquina e do lado direito os mnemônicos. Temos por exemplo o opcode/instrução '89' que é o a instrução MOV. Diferentes "tipos" de assembly representam esses valores de formas diferentes.

 
OllyDbg / Direito Assembly, Esquerdo OpCodes

Data


As data sections são espaços utilizados para armazenar os dados que as instruções precisam buscar para executar. Então os dados podem estar em alguma seção da memória ou ele pode usar a stack (mais depois). Todos os dados são armazenados utilizando sua representação em hexadecimal, e são referenciados para serem utilizados por seus endereços em memória. Tudo que é armazenado na memória possui um endereço. Veremos que os dados ou endereços podem ser requisitados através de uma immediate constant que está explícito diretamente na linha de execução ou pode estar em alguma parte da memória, isso inclui o stack frame.


Directives


Directives são elementos utilizados em assembly para informar ao assembler (compilador do assembly) como esse determinado dado deve ser utilizado. Por exemplo se você precisa armazenar um número float o assembler precisa saber que tipo de dado é para armazená-lo de forma correta. Ou quando você quer utilizar um dado que está em um determinado endereço de memória, por exemplo. As directives criam seções na memória para armazenar os tipos de dados. Uma das directives mais importantes é a ".section" que cria seções em memória para os determinados tipos de dados.

Sections


Nós temos vários tipos de seções, podemos até criar outras se quisermos. Contudo as seções listadas abaixo são padrão:

  • .text:
    • Todos as instruções que o processador executará são armazenadas nessa seção. Dados não são permitidos aqui, a não ser os immediate constants que como eu disse antes são representados diretamente na linha de instrução. Como uma atribuição de valor a = 5, mas isso depende do programador assembly ou do compilador nas linguagens de alto-nível.
  • .data:
    • Esta seção é responsável por armazenar todos os dados que serão utilizados pela seção .text. Serão referenciados através de endereços de memória. Como program.ADDRESS, por exemplo.
  • .bss:
    • Esta seção é utilizada geralmente para dados não inicializados, que são armazenados durante a execução do programa. Acredito que o nome da seção mude de acordo com as linguagens.


Arquitetura IA-32


A arquitetura IA-32 foi desenvolvida para os processadores pentium pela Intel. Não tenho certeza se é realmente a mais utilizada atualmente, contudo é MUITO conhecida, possui bastante documentação e bem utilizada. Quando estamos aprendendo assembly a arquitetura é só a sintaxe de como escrever, para migrar para outras arquiteturas fica mais fácil depois que aprendemos uma bem, pois o conceito é sempre o mesmo o que muda é como escrever. Algumas arquiteturas possuem mais instruções ou instruções mais elaboradas e outras menos. A arquitetura IA-32 é dividida basicamente em 4 partes:

  • Control unit (Unidade de controle):
    • A unidade de controle é responsável por trazer da memória os dados e as instruções. Ela então traduz essas instruções em micro-operações e passa para unidade de execução. Após a execução o resultado retorna para a unidade de controle que armazena o resultado.
  • Execution unit (Unidade de execução):
    • Executa todas as micro-operações e retorna o resultado.
  • Registers (Registradores):
    • Os registradores são responsáveis por armazenar dados e endereços de memória. Esses registradores estão na memória interna do processador. O processador mantém os dados na memória interna para deixar a sua execução mais rápida, pois o processo de ir buscar na memória RAM do computador é muito mais lenta.
    • Para manter o post objetivo não escrevei extensamente sobre todos os registradores, contudo estou deixando as referências no final do post com um conteúdo mais acurado. Basicamente há 4 tipos de registradores (Tem mais, veja nas referências):
        • General Purposes (Propósitos gerais): 8 registradores de 32-bits. Como o próprio nome diz, são registradores gerais, podem armazenar dados e endereçõs de memória.
        • Segment (Segmentos): 6 registradores de 16-bits. Usados para acessos de memória.
        • Instruction Pointer (Ponteiro de instrução): 1 registrador de 32-bits. Aponta para a instrução que deve ser executada pelo processador.
        • Floating-point (Ponteiro de Float): 8 registradores de 80-bits. Para trabalhar com os números de ponto flutuantes.
  • Flags:
    • Flags são utilizadas para manter controle das operações executadas pelo processador. Através dela sabemos se as operações funcionaram ou não. Certas flags são marcadas de acordo com a operação executada. Veremos melhor sobre elas. 

Registers


General Purposes


Esse tipo de registradores são utilizados principalmente para trabalhar com os dados que as instruções/operações usam. Esses registradores possuem um tamanho de 32-bits, porém são subdivididos em 16-bits e 8-bits. 

  • EAX (32-bits):
    • AH (16-bits):
      • AL (8-bits)
  • EBX (32-bits):
    • BH (16-bits):
      • BL (8-bits)
  • ECX (32-bits):
    • CH (16-bits):
      • CL (8-bits)
  • EDX (32-bits):
    • DH (16-bits):
      • DL (8-bits)
  • EDI (32-bits):
    • DI (16-bits)
  • ESI (32-bits):
    • SI (16-bits)
  • EBP (32-bits):
    • BP (16-bits)
  • ESP (32-bits):
    • SP (16-bits)

OllyDbg / Registradores e Flags

Esses são registradores que trabalharemos na maior parte do tempo. É importante ressaltar que modificar um registrador da cadeia mais alta modifica também a cadeia mais baixa. Por exemplo se armazenarmos um valor em AL e então colocarmos outro valor em EAX o valor que havíamos colocado em AL foi modificado pelo novo valor colocado em EAX já que AL é EAX.

Alguns desses registradores são utilizados de forma padrão, como é o exemplo dos registradores EBP e ESP que são utilizados para controlar o stack frame. O stack frame é um bloco de memória utilizado para controlar o contexto de valores e dados de uma determinada function/method, por exemplo quando declaramos uma variável dentro de uma função, esse valor é armazenado na stack frame. Utilizamos a stack  também para passar valores via parâmetro na chamada de outras funções, utilizando a instrução push que armazena os valores na stack, veremos melhor na prática no decorrer dos posts.

Como eu sempre digo, é importante programar uma linguagem "baixo-nível" tipo C ou C++ que a curva de conversão para o assembly é menor, além do que você tem uma prática de manuseio diretamente com a memória. Com isso você tem uma visão melhor de como esse código fica em assembly. No decorrer das minhas postagens trarei bastante código C para analisarmos.

Segment


Antigamente podia-se escrever diretamente na memória física, pois o processador permitia esse tipo de ação, esse modo é chamado de real mode. Atualmente o real mode ainda funciona porém de forma limitada. O modo que é utilizado atualmente pelos sistemas operacionais (Windows NT em diante) é o protected mode com o conceito de paginação de memória, dessa forma fica explícito que em segmento de código não pode ser escrito nada, somente em segmento de dados. Pode-se também descrever blocos de memórias com determinados níveis de acesso. O assunto é extenso, vamos com calma ;p

Portanto os segmento de registros são utilizados para identificar onde os dados estão localizados em memória. Cada registro de segmento possui um ponteiro para a section (seção) onde ele irá pegar os dados necessários. Os segmentos são:

  • CS: Segmento de Código.
  • DS: Segmento de Dados
  • SS: Segmento da Stack
  • ES: Segmento Extra
  • FS: Segmento Extra
  • GS: Segmento Extra
Cada um deles é usado em casos específicos. Por exemplo, se você possui um endereço dentro do registrador EAX que é um endereço de memória dentro da seção de dados e você precisa guardar a informação que está em EBX, você verá algo como:

MOV dword ptr ds:[EAX], EBX

Com isso você está movendo a informação que EBX contém para dentro do endereço de memória que está localizado em EAX e dentro do segmento DS (segmento de dados), veja que você não está guardando dentro do registrador EAX, mas sim dentro do endereço que EAX aponta e na seção em que o segmento DS aponta.

 
OllyDbg / Immediate constant sendo movido para dentro da stack

Veremos muitos exemplos práticos. Que é o melhor meio de aprender!

Flags


As flags são mantidas em um único registrador chamado EFLAGS e cada flag é representada por um 1 bit dentro desse registrador. Como eu disse antes as flags são utilizadas para controlar o sucesso ou a falha das operações/instruções que o processador executa. Por exemplo os JUMPS (saltos) condicionais, eles usam algumas flags como referência para saber se o salto irá ser realizado ou não. Jump como o nome diz é um salto para algum endereço de memória, saltar para outro endereço de memória com outras instruções, ou seja continuar a execução a partir de outro endereço, outro ponto dentro do programa. Flags são divididas em 3 grupos:

  • Status Flags
  • Control Flags
  • System Flags

Vou falar basicamente sobre as status flags. Nas referências no fim do post tem bons livros com mais informações e de forma mais acurada, a ideia aqui é objetividade e macro-informação. As flags são um "sinal" ou status do resultado das operações matemáticas executadas pelo processador. As flags são :
  • CF: Carry Flag.
    • Carry flag é usado como suporte para as operações binárias salvando o carry ou o borrow. Utilizado para "carregar" o bit que sobra da operação. (Post introdutório de computação) Geralmente usado em operações unsigned.
  • PF: Parity Flag.
    • Quando a soma de 1s do valor resultante da operação é par.
  • AF: Adjust Flag.
    • Usada no Binary Coded Decimal (BCD) quando é um borrow ou carry. Como o próprio nome diz flag de ajuste do valor referente ao byte que representa a grandeza dentro do BCD. É um assunto complicadinho, indico leitura na referência (Richard Blum).
  • ZF: Zero Flag.
    • Usada quando o resultado de uma operação binária é 0.
  • SF: Sign Flag.
    • É usada em operações com números signed, é algo como números sinalizados (positivo ou negativo). Leia o post introdutório que tem um bom conteúdo lá. Esta flag é marcada quando a operação resulta em um número negativo.
  • OF: Overflow Flag.
    • Esta flag é utilizada em número signed quando a operação resulta em um número maior do que pode ser armazenada, ou seja deu erro. 

O assunto é complicadinho mesmo, mas com a prática a mais pesquisas chegaremos em um entendimento bacana. Qualquer dúvida estamos ai.

Stack


Stack é muito importante se tratando de engenharia reversa ela é utilizada o tempo todo durante a execução de qualquer programa. É utilizada para guardar dados de curto-prazo por assim dizer. Como eu disse antes, quando a execução entra dentro de uma função é criado uma nova stack frame  que seria um pedaço ou um bloco dentro da stack reservado para a função que está sendo executada. Stack geralmente é utilizada para:
  • Salvar os valores dos registradores:
    • Geralmente é utilizada para guardar valores de um registrador quando este mesmo registrador precisa ser utilizado em outra operação. Depois esse valor pode ser recuperado.
  • Alocação de variáveis locais:
    • Como já disse, suas variáveis locais ficam alocadas dentro da stack frame quando a execução entra em determinada função. E quando você precisa utilizar esses valores é usado o segmento que falamos antes, SS, stack segment.
  • Passar parâmetros para funções:
    • Para chamar determinada função geralmente é utilizado a instrução PUSH para enviar os valores para a stack e então chamar a função com a instrução CALL. Geralmente esses parâmetros são passados de forma reversa, da direta para a esquerda. f(p1, p2, p3) então passamos PUSH p3,p2,p1 e efetuamos a CALL.
  • Guarda o endereço de execução após a instrução CALL:
    • Quando o programa executa uma instrução CALL o curso de execução é alterado, então antes de uma CALL ser efetuada ela salva o endereço de execução que está na linha abaixo da CALL, pois quando a função termina de executar ela precisa retornar pro FLOW de executação na qual ela estava antes, isso acontece depois da instrução RETN ser executada.

Quando o programa entra em uma função as variáveis do escopo dessa função são armazenadas na stack. A stack nada mais é do que um pedaço de memória que o programa utiliza guardando os dados necessários. Para guardar os dados na stack utilizamos a instrução push e para resgatar os valores da stack utilizamos a intrução pop.

Toda vez que a execução do programa entra em uma função um stack frame é configurado. Stack frame nada mais é do que um bloco de dentro da stack. O stack frame é limitado pelos registradores ESP e EBP. ESP aponta para o topo da stack e o EBP para a base da stack. Vejamos um exemplo de código em assembly que realiza o stack frame:

PUSH EBP
MOV EBP,ESP
SUB ESP, SIZE

Primeiro o valor de EBP é salvo na própria stack para quando sairmos da execução da função o antigo stack frame possa ser restaurado. Então o valor que ESP está apontando agora é a nossa nova base do stack frame. Lembrando que o ESP é o topo da stack, então imagine que estamos colocando uma nova pasta de arquivos em cima de uma pasta de arquivos já existente. Quando a execução da função terminar imagina que retiramos essa pasta de arquivos que foi colocada em cima e sobra a pasta que já estava lá. Desta forma vemos que a stack funciona como LIFO (last in, first out) último que entra, é o primeiro que sai. Então por último é utilizado a instrução SUB que faz com que ESP aumente de tamanho, aumentando o tamanho do nosso stack frame.

Geralmente esse SUB é feito quando já se reserva o espaço da variável então somente é necessário acessar esse espaço dentro do stack frame diretamente usando nosso segmento de stack, SS, que vimos anteriormente. Teriamos algo parecido com isso:

mov EAX, PTR SS:[ESP+4]

Nessa última instrução o valor que está dentro de ESP somado com 4 bytes de posição, nos dá o endereço da variável desejada é movido para o registrador EAX. Nesse caso a variável é [ESP+4]. Se tivéssemos mais uma variável poderíamos ter outro endereço para ela como [ESP+8].

Uma das coisas que temos que entender é que a stack cresce para os endereços de memória menores, ou seja ela cresce para "baixo". Quando um programa começa sua execução a stack começa em seus maiores endereços e vai crescendo para os endereços menores. Vamos ao exemplo:

 
Funcionamento da stack

Heap


A Heap é uma área da memória onde os programas utilizam para alocação dinâmica de memória. O sistema operacional geralmente cuida dessa parte para os programas, então quando eles são iniciados e precisam de uma HEAP por alguma razão, o próprio sistema operacional cria este espaço em memória e entrega um ponteiro para esse espaço ao programa. Quando temos uma constante ou uma variável já iniciada como por exemplo:

char newText[] = "Testing how code storing data.";

O compilador já se encarrega de colocar o valor desta variável na seção de .data que é a seção onde ficam os dados inicializados do programa. Então dentro da execução do programa o compilador já coloca o endereço direto na linha da instrução que está sendo executada, pois o programa já sabe onde armazenou esta informação. Esse endereço podemos chamar de immediate constant dentro do programa ficaria algo como  program.ADDRESS, sendo ADDRESS o endereço da variável dentro da seção.

Contudo quando você vai carregar um arquivo externo o programa precisa alocar um espaço para esse arquivo e o tamanho que será necessário é dinâmico, então a HEAP será necessária. Se você abrir um arquivo de 5Kb então uma HEAP com o tamanho necessário será disponibilizada. Se você carregar um arquivo de 1Mb, então uma HEAP com o tamanho necessário será disponibilizada com o tamanho necessário. Heap nada mais é do que uma alocação dinâmica de memória.

Temos que ficar atentos quando se trata de HEAP em engenharia reversa, pois muitas informações importante são registradas nas HEAPs, veremos mais no decorrer dos posts e na prática como funciona.

Conclusão


Bom galera é isso! Quero pedir desculpa de antemão para qualquer erro ou mal-entendido durante a explicação. Estou tentando ser o mais objetivo e didático possível, contudo passar tudo isso em um post só é MUITO complicado, pois há muita informação. Acredito que com esse macro de informação irá facilitar e direcionar as pesquisas em cima do tema, pois sei que não da pra entender tudo só com esse post.

Qualquer dúvida em cima do tema é só me procurar, se eu puder ajudar ajudarei com maior prazer. E se eu não souber, pesquisaremos juntos. O intuito é só aprender. Quem sabe se juntar uma galera legal não montamos um grupo de estudo. Fica a dica. Qualquer coisa "tamo ai". :thumbs_up: 👍


Referências

  • Google
  • Eldad Eilam - Reversing: Secrets of Reverse Engineering
  • Reverse Engineering Code With IDA Pro
  • Professional Assembly Language - Richard Blum


Friday, June 23, 2017

Basics of Assembly

Introduction to Assembly

Hello everyone, it's me again bringing some basic stuff. Even being a basic stuff I hope that can help anyone. I am making this basic posts to my incoming ones, I will bring more technical analysis. Sorry for any mistakes that I maybe did on this post and if I did any, please send me a feedback.
Well, the assembly code it's the "only" way on reversing engineering. Or you interpret assembly mnemonics or you analyze opcodes (machine code). As said in the previous introductory topic (if you didn't read, I do recommend) the assembly code it's generated by the compiler for every code language that you use. So, understanding it is vital to RE. Assembly language doesn't is standart for all assemblers, the mnemonics(instructions) are different deppending of your processor architecture. Since the processor is made of several circuits and each processor has your own cricuits, the logic behind it's processing differs from each other. So the instructions that they "understand" is different from each processor architecture. In this blog I will give the approach of IA-32 architecture. I will not "teach", is more like an approach of what it is and how we work with. So knowing how the computer "works", as Memory-Data X Instruction-Operation. Logically the computer stocks all data on memory and all this data is interpreted and executed by the CPU.

 
Computer / Memory, CPU, IO Devices and BUS

The processor have some pointers to help the CPU keep track of what it need to do with what data. Then we have the instruction pointer and the data pointer. Through the posts we will see it in practice. So the instruction pointer points to the memory block that represents an instruction and the data pointer to a memory block that represents the data, then the CPU executes the instruction with the data that is pointed.

 
CPU Units X Memory

These pointers allocate the memory offsets in registers. As the processor executes these instructions the instruction pointer goes to the next instruction and the data pointer too. The instruction has between 1-3 bytes and is called opcode (operation code). So basically assembly has three "parts", opcode mnemonics, data sections and directives.

Opcode


Mnemonic code is the "english" representation of the instruction code, e. g. the '89' instruction is the 'mov' mnemonic. Different assembly types represent instructions differently.

 
OllyDbg / Right Assembly, Left OpCodes

Data


The data sections is the space used to store the data which the instrunction will use to execute, so this data can be in some memory section or it can use the stack (memory area, more later). All the data is stored in the hex representation and is referenced by it's memory address. So every data stored on the system has a memory address, it can be a immediate constant in the assembly code or it can be stored on the stack frame.

Directives


Directives are the elements used in assembly to tell the assembler (which compiles the assembly) how to interpret this type of data, data includes everything, like code and values. For example if you want to store a float value the assembler needs to know, then it can reserve memory properly. One of the important directives of assembly is the ".section" directive. This directive creates sections on memory for each type of data.

Sections


We can have any kind of section we want, but all programs have this by default:

  • .text:
    • All the code instructions are alocated in this section. No data is allowed here, except some fixed data of variables like a = 5, but that depends of the programmer on low-level programming and depends of the compiler on high-level programming.
  • .data:
    • This section is responsible for stores all data that the .text request. It will be referenced as an address in the .text section, like program.ADDRESS.
  • .bss:
    • This section generally used to unitialized data. I think the name might change from language to language, don't sure.


The IA-32 Architecture


The IA-32 architecture was designed for pentium processors by Intel. I don't know for sure if it is the most used nowdays, but it's a very known one and have a lot of documentation about it. When you learn assembly basis in one architecture, makes easy to learn in another one, because the base still the same. Some assembly let you do more others let do less, I think it's the basic difference. In my last topic I tried to wrote about computer in general form and I think that you already know how it is. I don't want to make this part too much extensive. So basically IA-32 is divided in 4 parts:

  • Control unit:
    • Control is responsible for bring all the information from memory, data and instructions. Then it decodes this instructions into micro-operations and pass to execution unit. The result of the operation is passed back for control unit that stores the result.
  • Execution unit:
    • Responsible for execute all the micro-operations.
  • Registers:
    • Registers are responsible to keep track of data that are been used. This registers are internal memory of the processor. Having this little memory inside the processor make it much more faster than going outside (from processor itself) searching data in RAM memory and retrieve it.
    • I want to keep it objective so I will not write about all of them. You can read in the book listed on the reference, I do recommend. So there are basically four types of registers (there are more like I said):
      • General Purposes: 8 32-bits registers. They are used to work with data.
      • Segment: 6 16-bit registers. Used to memory access.
      • Intruction Pointer: 1 32-bit register. Points to the next instruction to be executed.
      • Floating-point: 8 80-bit register. It used to work with floating-point numbers.
  • Flags:
    • Flags are used to keep control of the operations executed by processor. It's a way to know if some operation worked or not. There are specific type of flags to specific operations. We will see it.


Registers


General Purposes


These registers are mainly used to work with data as the code is been executed. All data used in the instructions are stored in these registers. They are 32-bits longer at the "top" level (32-bits), but it can be sliced in minor parts (16-bits, 8-bits) that stores minor data.

  • EAX (32-bits):
    • AH (16-bits):
      • AL (8-bits)
  • EBX (32-bits):
    • BH (16-bits):
      • BL (8-bits)
  • ECX (32-bits):
    • CH (16-bits):
      • CL (8-bits)
  • EDX (32-bits):
    • DH (16-bits):
      • DL (8-bits)
  • EDI (32-bits):
    • DI (16-bits)
  • ESI (32-bits):
    • SI (16-bits)
  • EBP (32-bits):
    • BP (16-bits)
  • ESP (32-bits):
    • SP (16-bits)

OllyDbg / Registers and Flags

These are the 8 32-bits registers that we will work with a lot. So it's important to say that modifing a top level register you will modify the low level register. For example if you put any value at AL and then put a new value on EAX the AL value will be overrided. Some of these registers are used in the default way. EBP and ESP for example are used to control the stack frame. Stack frame is a block of memory used to control some local variables of function/method. But you can use the stack when you need to call a method/function you push the parameter onto the stack so the call instructions can grab these parameters to call the method/function. As we analyze artifacts we will see the pattern of it's usage. Because of this it's important to know programming. The good way to learn it's to code and analyze it. On the later topics I will bring much more pratical examples with C language. It's amazing what we can do (I mean, you are managing energy, bro!! lol).


Segment


The segment registers are used to identify where data is located. Each segment register has a pointer to the section where it suposed to grab the data. The segment registers are:
  • CS: Code Segment.
  • DS: Data Segment
  • SS: Stack Segment
  • ES: Extra Segment
  • FS: Extra Segment
  • GS: Extra Segment
So each one is used in specific cases. For example, if you have a address memory on EAX (of data section) register and you have some data on EBX and want to save it on the .data section you maybe see that:

mov ds:[EAX], EBX

So you are moving the data on the EBX to where [EAX] pointing in the DS section. The [] brackets indicating that is a pointer to some memory address.

 
OllyDbg / Code acessing SS segment


We will see much more in pratical examples. The best way to learn.


Flags


The flags are maintained in a single 32-bits register called EFLAGS and each flag is represented by each bit. So as I said the flags are used to control if the operations that the processor executed worked or not. For example for conditional jumps through the code execution it checks if the Zero Flag (depends on the jump, others can be used) was set so it can know whether it will jump or not. There is an image as you already saw in registers topic. The flags is divided in three groups:

  • Status Flags
  • Control Flags
  • System Flags

I will only discuss about status. If you want to know more, there are books listed in the reference, I do recommend to read. The status flags are used to sign the result of mathematical operations executed by the processor. The flags are:
  • CF: Carry Flag.
    • Carry flag is used to manage the carry or borrow out in mathematical operations. It means that occured an overflow, i. e. there is some remaining data. Used in unsigned arithmetic.
  • PF: Parity Flag.
    • Is set when the result of the operation have sum of the 1's bit even number.
  • AF: Adjust Flag.
    • Used in Binary Coded Decimal (BCD), is set when the result of an operation is a borrow or carry. BCD it's a nice feature to work with decimals, if you want to know more at reference has what you need.
  • ZF: Zero Flag.
    • Is set when the result of an operation is 0.
  • SF: Sign Flag.
    • Is set in the most significant bit, when the operation results in a negative number.
  • OF: Overflow Flag.
    • Is used in signed integer operations and is set when the operation result is too large for positive numbers representation or too small in the negative numbers representation.

Stack


So now we are going to talk about the stack. Stack is very important in the reversing due it's common usage in the assembly world. The stack is a memory area where the program in execution uses to store short-term data. So the stack is generally used to:
  • Save register values
    • You can save the register in the stack to use it for another operation and then retrieve the old value to it.
  • Store local variables
    • Store the local variables at function scope. Like I said before, when doing it the variable is accessed directly using SS segment to access the offset in the stack.
  • Passing function parameters
    • To call a function generally you push all parameters on the stack from the right to left and call the function. For example, f(p1,p2,p3), you will pass p3,p2,p1.
  • Store the return address of the function call
    • The address of the next instruction after the call instruction, doing that the program will know where to back when it executes the retn instruction inside the function called.
Storing locally means that when you enter in a function scope all the local variables generally uses the stack to keep your data. In general everything is on memory, on the address space that the operating system gave us. The stack is just an area on that address space where the program uses to store data. So to put values on the stack you uses push instruction and to retrieve it you uses pop instruction. Everytime you enter a function a stack frame is set. Stack frame is set of addresses reserved to the function in execution, that set is limited by EBP(Base pointer) and ESP ('Top' Stack pointer). Generally this is how you see the stack frame been set:

PUSH EBP
MOV EBP,ESP
SUB ESP, SIZE

 First you save the old EBP, for when you back to the previous function will be able to reset the old stack frame, then you set the new base pointer and add the size (how many addresses) you need to that function. Stack are managed as LIFO type, last in first out. So the last element that you pushed onto the stack is the first element that will be poped out. But you can access this data directly if you need it, remember the SS segement ? So you can access using it together with the ESP (points to the top of the stack) register, like:

mov EAX, PTR SS:[ESP+4]

In the last instruction you moved the value of "ESP (plus) 4" pointing at to the general register EAX.

 Some important things about stack is that it grows up to lower addresses on IA-32 architecture. So bigger the stack is, lower addresses it access as we can see:

 
How stack works basically

In the image we can see the heap too and it's our next topic. This post I am bringing a theorical approach but in the next we will discuss it all in a pratical way. For now lets just abstract.


Heap


Heap is a memory area where the program uses for dynamic allocation. The heap is managed generally by the OS. So when the programmer need some space to store data that is bigger than the stack could manage, then the program store it in the heap memory. So the memory heap is passed to the program when the OS is loading it on memory. When you have a literal expression on your code like:

char newText[] = "Testing how code storing data.";

Regardless the data was inside some function the compiler generally uses the .data section and give to the instruction that will use this data the constant immediate address of the data, something like program.ADDRESS. But when you are loading something external for example, you don't know the size you going to need so the size is dynamic, then the program will have a routine that allocate the size you need in the heap.

 Heap it's important in RE because many program uses it to allocate data and sometimes identify which routine allocates the memory can be useful.

Now I think you are able to understand the incoming new posts that I will bring here. As I post new topics I will try to discuss a little more on technical topics. On the pratical part we will have a better view.

References


  • Google
  • Eldad Eilam - Reversing: Secrets of Reverse Engineering
  • Reverse Engineering Code With IDA Pro
  • Professional Assembly Language - Richard Blum

Sunday, June 4, 2017

Engenharia Reversa - Ponto de vista didático ?

Olá para todos!

Bom, estou iniciando esse blog para gerar conteúdo à toda a comunidade e como forma de estudo também. Como estou iniciando o blog achei melhor trazer um texto introdutório e a ideia é ir elevando o nível com o passar dos textos. Não é nenhuma super revelação esse post, porém acho importante dissertar sobre. Meu único interesse é gerar informação para todos, quero continuar trazendo conteúdo didático e objetivo para aprendizado. Tentarei traduzir meus posts e postá-los em português e inglês.

Caso tenham recebido malwares por e-mail e quiserem compartilhá-los comigo. Podem me enviar, ficarei agradecido. pimptechh@gmail.com.

Engenharia reversa é um assunto bem polêmico, pois envolve muitas questões legais e por esta razão é um tema que deve ser tratado com muito cuidado. No caso estarei utilizando o conceito para software de computador, visto que é um termo bem abrangente e pode ser referido para qualquer tipo de artefato fruto de engenharia. Basicamente se trata da desconstrução de algo que já está pronto. Muitos utilizam deste conhecimento para quebrar software, muitas vezes citados pelo termo "Cracker" que remete a "Crack" (quebrar) unindo o "er" no final ficaria algo como "Quebrador" ou o usuário que irá quebrar o software. Contudo é um processo muito importante hoje em dia na indústria, nos quesitos de segurança de software, manutenção de software, performance de software e etc. É uma visão de como funciona para que então possamos melhorar ainda mais o software em si.

Em engenharia reversa é fundamental que se tenha curiosidade no geral, pois é basicamente dai que nasce a ideia de reverter algo. "Ora, mas como será que isso foi feito ?" ou "Ora, por que será que isso está acontecendo ?". Um simples debugar de um programa com o código fonte em mãos já se encaixa no processo, a fim de descobrir o motivo de determinado valor ou bug, por exemplo. Portanto para entender um processo reverso devemos primeiramente ter uma base de como o processo foi desenvolvido em primeiro lugar. Se há a necessidade de se reverter um software é necessário que saiba ao menos como desenvolver um software. Entender o funcionamento de um software é imprescindível, pois antes de jogar nas ferramentas que auxiliam o processo temos de ter no mínimo uma base. A base é sempre importante, tentarei dissertar da melhor forma possível para tentar passar a importância da base pelos meus estudos.

Tudo começou, bem não sei exatamente onde tudo começou (ainda estou descobrindo), mas sabemos do "primeiro" grande invento ou algo aproximado do modelo de computadores que temos hoje, o ENIAC. Esse projeto já possuía o dedo de Neumann(criador do modelo atual de computadores), ajudando a resolver alguns problemas lógico matemáticos. ENIAC basicamente fazia cálculos aritméticos complexos, porém não possuía o conceito de memória e programa. Depois do ENIAC nasceu o EDVAC um pré computador moderno, com conceito de memória e programa.

O computador moderno "começou" com John von Neumann, uma mente brilhante com um memória fantástica. Recitava capítulos inteiros de livros usando exatamente o que estava escrito sem trocar nem acrescentar. Capaz até de recitar livros em alemão traduzindo simultâneamente. Em 1927 e 1928 publicou vários "papers" sobre fundamentos matemáticos da teoria quântica e probabilidade na estatística quântica, então se você não acredita em mecânica quântica ou duvida, desliga seu pc e joga fora hahaha.. Nesses "papers" ele demonstra o conhecimento nos fenômenos físicos. A abstração vem desses problemas extremamente complexos que por sinal Von Neumann dominava. Se ele não era um cara a frente do tempo, então não sei. A história é bem interessante, vale a pena ler. Tem um livro chamado "The computer and the brain" em que ele tenta explicar os conceitos e palpites que teve durante os testes com o modelo projetado principalmente por ele, nesse livro ele tentar fazer uma analogia entre o nosso sistema nervoso de um ponto matemático, utilizando conceitos de lógica e estatística. Corpo humano, máquina biológica.

Quando pensamos na mágica que o computador faz, isso o deixa ainda mais bonito haha. Armazenar energia ao mesmo passo que armazena informação, e com essa informação resolver problemas frutos do intelecto. O computador teve sua necessidade para ajudar a resolver os problemas de forma muito mais rápida. Fazendo cálculos complexos em uma velocidade difícil de imaginar. Basicamente você insere um "input" esse valor é armazenado em memória na forma de energia/tensão, essa tensão é diferenciada entre 0's e 1's. Isso tudo armazenado dentro do seu computador, e com auxilio da lógica booliana temos nosso "output", seja ele qual for, depende do tipo de problema que você quer resolver. Quando digo isso, é necessária abstração do que você encara como um problema. Não sei como era a conversa de Von Neumann e seus amigos matemáticos, mas a conversa deveria ser bem maluca ;p. 

Nós resolvemos e criamos muitos problemas todos os dias pelo simples fato de se ter um monte de energia estocada dentro do seu computador. Um dos conceitos de programação está aqui, a abstração, quanto mais no topo você está, mais você abstrai a computação. Quando você precisa falar com alguém, você chama este alguém no seu IM de escolha e resolver o problema(falar com este alguém), apenas um exemplo. Não perceber isso dificulta a quebra de paradigma. Contas de matemática (2+2). O que você fez ? Você olhou o 2 entendeu que é um 2, armazenou essa informação, adicionou mais 2 e então operou de forma lógica "+", obtendo o 4. Isso pode ser abstraído de por exemplo, olha tenho 2 maças, se como uma sobra só mais 1. Então você observa, estoca informação e processa, exatamente igual o computador. Agora como fazer isso tudo virar informação ? Subindo as camadas de representação. Temos nesse caso 0 e 1 armazenados em forma de energia no nosso computador podendo representar informações como ligado e desligado, cheio e vazio, e etc. Quando formamos blocos destes bits/valores temos um acervo maior de possibilidades. Como números, letras e etc.

Todas as informações estão em 0 e 1, juntando todos os 0's e 1's em blocos temos as informações. Tudo depende da maneira como você deseja interpretar esta informação. Portando por serem 2 valores possíveis podemos então deduzir que é uma representação de base 2, ou seja 2 possíveis valores. Não sou super fã de matemática, porém sei apreciar a beleza. Enfim, como podemos abstrair essas informações de conjuntos de 0 e 1 ? Vamos começar pelos decimais base 10. Todo mundo, acredito eu, viu na escola sobre grandezas e formas de representar essas grandezas, por exemplo 10, 100, 1000 e assim sucessivamente. Você implicitamente entende essas representações como dez, cem e mil, e consegue discernir isso pois adicionamos um 0 no fim de cada grandeza, desta forma elevando-as. Adicionando os dígitos decimais nós podemos contar (0,1,2,3... 10,11,12...), a cada quebra de grandezas temos uma nova representação.

O mesmo se dá com binário, porém alternando o 1 e o 0 de lugar. Na imagem abaixo é possível ver uma contagem com binários:



Podemos representar ou subir para uma representação mais amigável(numérica) convertendo a base 2 para a base 10 (a qual estamos acostumados) como pode ser demonstrado a seguir:



Agora, como é possível fazer operações com formas tão primitivas de representação ? Aqui entra o que citei lá em cima, lógica booliana. O que tem dentro da sua CPU são microcircuitos de lógica cada vez mais complexos. Temos o que eu acredito ser o menor microcircuito Half-Adder:





Nesse circuito correm dois inputs (A e B) de tensão, "A" pode ser tanto 0 quanto 1, o mesmo se da para o "B". Agora como funciona esse microcircuito ? Dentro do microcircuito existem os chamados portões lógicos como OU (OR), OU Exclusivo (XOR), E (AND), Negação (NOT) e etc. Esse circuito tem 2 outputs, soma (sum) e o valor carregado (carry), como vimos na imagem anterior. Com isso podemos adicionar 2 bits, assim como fazemos com 2 números. Pegue o exemplo de (5+5=10), tenha como sum o valor 0 e o carry o valor 1. O mesmo se dá com a soma de bits, por exemplo com o nosso Half-Adder podemos somar dois bits. 1+1.



Com isso criamos outro problema, sabemos que a representação do número 2 é "10" em binário, certo ? O Half-Adder fez o trabalho dele, agora temos que dar conta desse carry ai, por esta razão veio o Full-Adder (imagem abaixo), que por sua vez recebe 3 inputs, A, B e o carry. Percebem a abstração dos problemas ? Dentro do Full-Adder temos um Half-Adder. Nossa querida CPU tem muitas camadas de abstração extremamente complexas são muitas e muitas horas de estudo aprofundado, contudo é bom saber a base pelo menos, certo ?. Sugiro quem tiver interesse, a pesquisar por organização lógica computacional. Existem vários microcircuitos embutidos um dentro do outro e a CPU em si é um arranjo de circuitos extremamente complexos. 



Certo, agora temos nossos 0's e 1's representados como dados em blocos e sendo "operados" pelos seus respectivos operadores lógicos, fazendo nossa máquina gerar nossos outputs. Contudo mesmo com as conversões de dados para decimal e posteriormente hexadecimal, que veremos mais abaixo, temos que entender que primeiramente o computador foi necessário para realizar cálculos matemáticos complexos isso inclui números com domínio nos REAIS, certo ? Cálculos complexos necessitam de números "complexos", não vou me aprofundar muito até porque não saberia explicar com prioridade. Temos dois importantes tópicos para serem discutidos antes de avançarmos para a conversão de hexadecimal que é a principal forma de representação de dados que iremos trabalhar. Houve a necessidade de representar os números negativos para realizar as operações, para isso utilizaram o sistema de "Signed" (Que também é um assunto bem discutido quando falamos da linguagem C junto com "Unsigned", vamos chegar lá!). Isto significou utilizar dentre os 8bits de dados o bit mais significativo ou o primeiro bit (0)0000000 para representar "+" e "-", sobrando então 7bits para representar o valor. Desta forma não podemos representar mais de 0 à 255, pois o bit mais significante (msb=most significant bit) está sendo utilizado para representar se ele é positivo ou negativo. De forma que agora podemos representar entre -127 à +127, pois o primeiro bit (msb) foi "cortado" para representar o sinal do número. Contanto com essa forma de representação os nossos cálculos com binários ficaram falhos, certo ? Vamos ao seguinte exemplo (+2) + (-3), deveria dar -1, correto ? Vejamos o que acontece:



Como podemos ver ao somar dois números, sendo um negativo e o outro positivo temos um resultado errado. Por esta razão foi implementado o "Complemento" (Complement). Em sua primeira "versão" chamado de "One's complement", que nada mais é do que a inversão do número signed, por exemplo o número "00000010" (2) ao receber o complemento fica "11111101" (2), mas mesmo utilizando esse conceito o resultado sai como -1 do valor correto. Vamos ao exemplo 4 + (-2):



O resultado da operação resultou em "00000001" que em decimal é 1. Lembrando que nesse sistema o último carry é sempre descartado, como podem ver na tarceira casa da direita para a esquerda nossa operação de adição gera um carry que é carrregado para as operações restantes e que no final é descartado por sair do bloco de 8bits. Então para mostrar corretamente o valor da operação é necessário adicionar +1 ao resultado final. Por esta razão o "Two's complement" veio para resolver este problema. Vejamos no exemplo:



Basicamente o "Two's complement" adicionar + 1 ao final da operação resolvendo o problema com a falha do "One's complement".
Contudo ainda sim temos um pequeno problema, mas fácil de ser resolvido. Imaginando que o resultado da operação seja negativo, ao realizá-la nos deparamos com o seguinte problema 4 + (-5):



Como vemos na imagem acima o valor fica "errado" quando tentamos utilizar a soma com o número negativo maior que o número positivo, então quando isso acontece é necessário refazer o complemento do número no resultado da operação, ou seja fazendo um flip de 0 para 1 ou 1 para 0 em cada bit, adicionando (+1) ao resultado deste flip e trazendo o carry restante da operação para o novo número de forma que ele possa ser representado em forma de signed "10000001" (-1). A CPU utiliza de uma das flags, mais precisamente a flag (S)ign Flag para fazer esta operação, veremos isso em outro texto

Bom, depois de toda essa complicação hehe, vamos seguir com a conversão para base 10 que é a forma numérica mais próxima do entendimento humano. Contudo não paramos por aqui, pois em engenharia reversa utilizamos base 16 para representação dos dados, então ao interagir com dados em memória eles estarão em hexadecimal, até quando eu não sei =). Mas antes vamos entender como é a rotulação desses blocos de binário, pois é importante saber. Cada bloco/conjunto de binários possui um nome ou tamanho, que é um parâmetro para saber a quantidade de informação que aquele bloco de binários possui.

4 bits = 1 Nibble
8 bits = 1 Byte 
16 bits = WORD 
32 bits = DWORD
64 bits = QWORD

Com a introdução do hexadecimal podemos encapsular uma maior quantidade de dados dentro de um único dígito. Por exemplo, o bloco "1111" em decimal é "15" em hexadecimal é "F", portanto podemos representar 4bits com apenas 1 dígito de hex e com 2 dígitos podemos representar 8bits de dados. Hexadecimais tem uma tabela bem tranquila de se entender:



Existem diversos sites que fazem a conversão de hex para decimal e string, seguem alguns exemplos:

http://string-functions.com/hex-string.aspx
http://string-functions.com/hex-decimal.aspx

Para fazer tudo isso funcionar precisamos estocar tudo isso e então trabalhar com essas informações, a forma como utilizamos hoje para estocar essas informações são as memórias RAM. Memórias RAM (Random Access Memory) são células que armazenam essa informação, assim como todos sabem ela é volátil, pois ao cortar a energia os dados que são a energia, são perdidos. A memória RAM precisa ser "revisada" por assim dizer, em ciclos, para checar quais os valores que estão estocados nas células e garantir que eles continuem lá. E quem estoca e admnistra essa informação é o cérebro, também conhecido como CPU. É necessário garantir que os valores que foram estocados não "descarreguem".

Bom, agora como essas informaçõs são trafegádas ? Através dos BUS de dados. Quando vemos "BUS de dados" é necessário uma abstração, imaginar um canal, um meio pelo qual todas as informações estão sendo transmitidas e recebidas entre os componentes do nosso computador. Todos nossos 0's e 1's transitam por esses BUS de dados, esse canal de informação. Ai temos um novo problema, como eu vou comandar minha CPU estocar e manipular essa informação estocada ? Como ele vai saber onde buscar e para onde levar essa informação ? Qual algoritmo utilizamos para resolver esse problema ? Multiplexer e Demultiplexer, são os circuitos que cuidam desse "transporte" ou rotulação de endereços de origem e destino e virse versa, recomendo ler ler um pouco melhor sobre eles. Nas referências listadas abaixo tem um bom conteúdo.

Até agora "sabemos" como o computador interage e como opera as informações que nele são estocadas, contudo gostaria de lembrar que é muito básico o que eu estou escrevendo aqui, é a base. Nosso atual computador é MUITO complexo com muito mais componentes, porém a base é a mesma, os bits são os mesmos, o estoque de informação é o mesmo, a forma de operar a informação ainda é a mesma. A base ainda reside no estoque de informação e operação lógica dessas informações. Eu vejo esse texto como uma forma de pensar sobre computação. Sabendo a base o resto fica por conta da imaginação. Agora como fazer para interagir com essa máquina de complexas operações ? Temos então o SO (Sistema Operacional), o sistema que facilita operarmos a máquina. Em engenharia reversa devemos ter isso muito focado em nossa mente, tudo passa pelo SO. Então uma operação específica tem um certo padrão, o que quero dizer é que todas as funções que um software desempenha é o SO quem garante isso, "abstraindo" a conexão direto com o hardware, com a linguagem de máquina.

O sistema operacional conta muito na hora de se reverter algo, ele encapsula/abstrai os métodos necessários para a interação com a máquina. Entender a forma como ele trabalha é um trabalho árduo que requer tempo de estudo, e acima disso, experiência e prática. É bom deixar isso frizado na mente, leva-se tempo para ir digerindo tudo, por isso é importante estar sempre pensando sobre o funcionamento. Se formos voltar no tempo e analisar o histórico dos SO's para o atual é uma mudança enorme, muitos paradigmas quebrados com muitas novas tecnologias implementadas. Conforme nossos problemas ficam mais complexos necessitamos de mais camadas de abstração, por isso temos linguagens de programação de alto nível que encapsulam funcionalidades para agilizar a solução dos problemas, subindo cada vez mais camadas para facilitar as nossas vidas de operar máquinas computacionais. 

Para se comandar uma máquina computacional é necessário uma linguagem que instruam comandos para a máquina, e como sabemos, a linguagem que de fato interage com o computador é a linguagem de máquina, com seus operadores e operandos. Tudo, no fim das contas é linguagem de máquina. É só o que o seu computador entende, você pode usar qualquer linguagem que no fim você está utilizando linguagem de máquina para operar sua máquina. O que temos hoje são linguagem de nível maior, ou seja, que podemos ter um contato mais "humano" mais real do nosso dia-a-dia. Linguagens como Java e C# são encapsulamento das linguagens de baixo nível.

Hoje temos essas linguagens que eu citei que acredito serem as "mais" utilizadas, pois são muito mais rápidas na solução de problemas, porém não necessariamente de performance. Pois quão mais alto você estiver nas camadas de abstração/encapsulamento, mais tecnologia você tem abaixo que resolvem muitos problemas para acelerar nossas soluções, porém tudo em computação é suscetível ao erro. Então quando falamos de linguagens de baixo nível queremos dizer que são as linguagens mais próximas da máquina. Esse é o modelo atual das linguagens:



É importante salientar que assembly é linguagem de máquina, o que ocorre é uma tradução da linguagem de máquina para mnemônicos. Chamamos esses mnemônicos de assembly. Esse fluxo da linguagens aplicam-se apenas para as linguagens compiladas como C/C++. Java e C# (entre outras) não rodam diretamente pelo computador, eles tem o intermédio da suas respectivas máquinas virtuais. Java possui a JVM (Java virtual machine) e o C# o CLR (Common language runtime), essas máquinas virtuais por sua vez é que interagem com o SO e o computador em si. Quando "compiladas" ambas geram o que chamamos de bytecode que é a "linguagem de máquina" mais alto nível, elas interagem com suas respectivas máquinas virtuais.

Em engenharia reversa temos de ter consciência da base, pois a linguagem mais próxima da leitura humana e ao mesmo tempo mais próxima da máquina é o assembly. Ele lida diretamente com a memória e operações, contudo como eu disse antes, com intermédio do SO, então mesmo em assembly temos a total interação com os métodos que o SO abstraiu/encapsulou para que possamos interagir com a máquina. Então no fim tudo é linguagem de máquina e "antes" disso é assembly. Podemos dizer também que antes de assembly temos as linguagens C e C++, sendo que C++ possui conceitos mais modernos, como programção OOP. Se eu fosse dar uma dica seria estudar C, é a melhor forma de você estar perto da máquina realmente. É o mais próximo para se trabalhar com a manipulação de memória, precisando entender os conceitos de memória e performance para se programar utilizando ela. Você resolve seus problemas estando mais próximo da camada de hardware. Abaixo segue uma imagem que mostra de forma abstrata as camadas entre Hardware e SO:



Conclusão e opinão

Bom, esse primeiro texto quis abordar apenas um pouco da parte bem introdutória da ER/computação que por sinal é muita mágica, pois imaginar energia se transformando em informação é realmente muito "dahora". Quero continuar atualizando este blog tanto para me ajudar quanto para ajudar a comunidade, acredito que talvez ajude alguém :). A minha ideia é continuar escrevendo textos tentando ser o mais objetivo e didático possível. Pretendo trazer conteúdo mais técnico nos próximos textos como programação assembly/c, análise de malware, resolução de softwares/puzzles e etc. Sintam-se à vontade para questionar ou apontar algum erro/falha. O intuito aqui é somente conhecimento e crescimento.

Grande abraço e bons estudos!

Referências:
  • Google
  • Andrew S. Tanenbaum - Structured Computer Organization
  • Eldad Eilam - Reversing: Secrets of Reverse Engineering
  • Richard Blum - Professional Assembly Language (2005)

Windows Objects

Objects in windows are referred as kernel objects . They provide a link or an way to use any objects functionality according with the object...