Traduzir Código fonte JAVA para binário

Boa noite pessoal!!!

Gostaria de saber se existe alguma forma de ver como um código fonte JAVA ficaria em binário?

1 curtida

Tem que converter cada letra em binario.

public String AsciiToBinary(String asciiString){  

          byte[] bytes = asciiString.getBytes();  
          StringBuilder binary = new StringBuilder();  
          for (byte b : bytes)  
          {  
             int val = b;  
             for (int i = 0; i < 8; i++)  
             {  
                binary.append((val & 128) == 0 ? 0 : 1);  
                val <<= 1;  
             }  
            // binary.append(' ');  
          }  
          return binary.toString();  
  }
1 curtida

Primeiro você precisa entender o que exatamente é o “binário” gerado a partir do código fonte.

Inicialmente temos o código fonte (o arquivo .java). Ao compilá-lo, é gerado um arquivo .class, contendo o bytecode, que não deixa de ser “binário”. Lembrando que aqui o termo “binário” geralmente quer dizer que “não é texto” - porque se formos levar ao pé da letra, tudo no computador vira binário no fim das contas (até texto), mas para o caso em questão, vamos considerar que binário é “um monte de bytes que não correspondem a um texto”.

Ou seja, o .class já é um tipo de “binário”. Ele contém o bytecode, que são as instruções que serão interpretadas pela máquina virtual (a JVM - Java Virtual Machine). Só que a JVM possui outro compilador (mais precisamente, um JIT - Just In Time compiler), que pode pegar trechos do bytecode e compilar novamente, gerando código de máquina específico da plataforma/sistema operacional no qual ela está rodando. Na verdade é mais complexo que isso, veja mais detalhes aqui.

Ou seja, a partir do código fonte, é gerado o bytecode (um tipo de arquivo “binário”), que por sua vez pode ser transformado em código de máquina nativo (dependente da plataforma/arquitetura/processador/sistema operacional/etc), e portanto é “outro binário”.


Se você quer ver os bytecodes (ou seja, as instruções da JVM que estão no .class), basta usar o javap. Por exemplo, se eu tiver um arquivo chamado Teste.java:

// Teste.java
public class Teste {
    public static void main(String args[]) {
        int x = 0;
        for (int i = 0; i < 1000000; i++) {
            x += 1;
        }
        System.out.println("fim");
    }
}

Primeiro eu compilo com javac Teste.java, gerando o arquivo Teste.class (que contém o bytecode). Basta rodar:

javap -c Teste.class

Que eu terei uma saída assim:

Compiled from "Teste.java"
public class Teste {
  public Teste();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iconst_0
       3: istore_2
       4: iload_2
       5: ldc           #2                  // int 1000000
       7: if_icmpge     19
      10: iinc          1, 1
      13: iinc          2, 1
      16: goto          4
      19: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      22: ldc           #4                  // String fim
      24: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: return
}

Este é o bytecode. Se você quer saber o que cada instrução significa, é “só” ler a especificação da JVM :slight_smile:

Lembrando que a saída do javap é formatada de um jeito mais “legível”, pois no .class não tem em nenhum lugar o texto “invokevirtual” ou “istore”. O que ele tem são bytes cujos valores correspondem a estas operações, e o javap só pega esses valores e mostra de um jeito mais “amigável” para entendermos.


Agora se você quer ver o binário gerado pela JVM (ou seja, pelo JIT), aí você pode rodar o programa com as opções -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Teste

Obs: tem que instalar a libhsdis0-fcml, existem instruções de instalação aqui e aqui (ou então procure no Google como instalar no seu sistema específico).

Neste caso, a saída será gigante e irá variar conforme o ambiente/sistema operacional, etc. Eu testei no Ubuntu 20.04, com OpenJDK 1.8.0_292 e a saída teve mais de 3000 linhas. Seguem as primeiras, para você ter uma ideia:

Loaded disassembler from /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/hsdis-amd64.so
Decoding compiled method 0x00007f6911103390:
Code:
Argument 0 is unknown.RIP: 0x7f6911103500 Code size: 0x00000290
[Disassembling for mach='amd64']
[Entry Point]
[Constants]
  # {method} {0x00007f68f3805000} 'hashCode' '()I' in 'java/lang/String'
  #           [sp+0x40]  (sp of caller)
  0x00007f6911103500: mov     0x8(%rsi),%r10d
  0x00007f6911103504: shl     $0x3,%r10
  0x00007f6911103508: cmp     %rax,%r10
  0x00007f691110350b: jne     0x7f6911045ba0    ;   {runtime_call}
  0x00007f6911103511: nopw    0x0(%rax,%rax)
  0x00007f691110351c: nop
[Verified Entry Point]
  0x00007f6911103520: mov     %eax,0xfffffffffffec000(%rsp)
  0x00007f6911103527: push    %rbp
  0x00007f6911103528: sub     $0x30,%rsp
  0x00007f691110352c: movabs  $0x7f68f39db0f0,%rax  ;   {metadata(method data for {method} {0x00007f68f3805000} 'hashCode' '()I' in 'java/lang/String')}
  0x00007f6911103536: mov     0xdc(%rax),%edi
  0x00007f691110353c: add     $0x8,%edi
  0x00007f691110353f: mov     %edi,0xdc(%rax)
  0x00007f6911103545: movabs  $0x7f68f3805000,%rax  ;   {metadata({method} {0x00007f68f3805000} 'hashCode' '()I' in 'java/lang/String')}
  0x00007f691110354f: and     $0x1ff8,%edi
  0x00007f6911103555: cmp     $0x0,%edi
  0x00007f6911103558: je      0x7f6911103691    ;*aload_0
                                                ; - java.lang.String::hashCode@0 (line 1466)

  0x00007f691110355e: mov     0x10(%rsi),%eax   ;*getfield hash
                                                ; - java.lang.String::hashCode@1 (line 1466)
... etc (mais um monte de linhas)
3 curtidas

hugokotsubo Vc sabe como fazer para obter o binário do código fonte de outras linguagens como C/C++, Python,Javascript?

C/C++

C e C++ compilam direto pro código de máquina, então o executável gerado já é o binário. É só abrir o arquivo em um editor hexadecimal se quiser ver os “bytes brutos”. Se quer ver o código de máquina correspondente, pode usar alguma ferramenta disassembler como o objdump, disponível no Linux:

objdump -d arquivo_executavel

A saída é algo do tipo:

Disassembly of section .init:

0000000000001000 <_init>:
    1000:	f3 0f 1e fa          	endbr64 
    1004:	48 83 ec 08          	sub    $0x8,%rsp
    1008:	48 8b 05 d9 2f 00 00 	mov    0x2fd9(%rip),%rax        # 3fe8 <__gmon_start__>
    100f:	48 85 c0             	test   %rax,%rax
    1012:	74 02                	je     1016 <_init+0x16>
    1014:	ff d0                	callq  *%rax
    1016:	48 83 c4 08          	add    $0x8,%rsp
    101a:	c3                   	retq  

No Windows tem o dumpbin, mas eu nunca usei (talvez tenha outras ferramentas, é só procurar).


Python

Em Python você pode fazer algo como:

import py_compile

py_compile.compile('arquivo.py')

Isso irá criar um arquivo .pyc na pasta __pycache__/, que é um binário contendo o byte code correspondente ao código que está no arquivo.py.

Mas você também pode fazer na linha de comando:

python -m dis arquivo.py

Isso irá mostrar o byte code em um formato mais “amigável”, exemplo:

  3           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (None)
              4 IMPORT_NAME              0 (io)
              6 STORE_NAME               0 (io)
              8 LOAD_CONST               0
etc...

JavaScript

Em JavaScript eu não faço ideia.

Se ele roda no browser, então isso deve ficar meio “escondido”. Por exemplo, no Chrome, a engine de JavaScript é o V8, que gera o byte code e ele também tem um JIT, que gera código de máquina sob demanda. Mas eu não sei onde esse binário fica (se é só em memória, ou se o Chrome guarda isso em algum lugar).

Se o JavaScript está rodando fora do browser (por exemplo, no Node), talvez ele guarde em algum lugar também (o Node também usa o V8, mas nada impede que ele faça alguma coisa diferente do browser com relação a isso). No fim, acho que depende da implementação (cada browser deve fazer de um jeito, e runtimes como o Node e o Deno também devem fazer cada um da sua maneira).

1 curtida