EVM detalhado - os riscos por trás da questão trivial da classificação do contrato

No campo dos contratos inteligentes, o "Ethereum Virtual Machine EVM" e seus algoritmos e estruturas de dados são os primeiros princípios.

Este artigo começa com o motivo pelo qual os contratos devem ser classificados e combina que tipo de ataques maliciosos cada cenário pode enfrentar e, finalmente, fornece um conjunto de algoritmos de análise de classificação de contrato relativamente seguros.

**Embora o conteúdo técnico seja alto, ele também pode ser usado como material de leitura para palestras diversas.**Olhe para a floresta escura de jogos entre sistemas descentralizados.

1. Por que os contratos devem ser classificados?

Por ser tão importante, pode-se dizer que é a pedra angular de Dapps, como trocas, carteiras, navegadores blockchain, plataformas de análise de dados, etc.!

A razão pela qual uma transação é uma transferência ERC20 é porque seu comportamento está em conformidade com o padrão ERC20, pelo menos:

  1. O status da transação é sucesso
  2. O endereço Para é um contrato em conformidade com o padrão ERC20
  3. A função Transfer é chamada, e a característica é que os 4 primeiros dígitos do CallData da transação são 0xa9059cbb
  4. Após a execução, um evento de transferência é enviado no endereço To

Se a classificação estiver errada, o comportamento da transação será mal avaliado

Com o comportamento da transação como pedra angular, se o endereço To pode ser classificado com precisão, levará a uma conclusão completamente diferente no julgamento de seu CallData. Para Dapp, a comunicação de informações dentro e fora da cadeia é altamente dependente do monitoramento dos eventos da transação, e o mesmo código de evento só pode ser confiável se for enviado em um contrato que atenda aos padrões.

Se a classificação estiver errada, a transação irá para o buraco negro por engano

Se o usuário transferir um Token para um determinado contrato, se o contrato não tiver um método de função predefinido para transferência de Token, os fundos serão bloqueados da mesma forma que Burn e não poderão ser controlados

E agora que um grande número de projetos começou a adicionar suporte de carteira integrado, é inevitável gerenciar carteiras para usuários. É necessário classificar os últimos contratos implantados da cadeia em tempo real em todos os momentos, se eles podem atender os padrões de ativos.

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

2. Quais são os riscos da classificação?

**A cadeia é um lugar onde não há identidade nem estado de direito. Você não pode interromper uma transação normal, mesmo que seja maliciosa. **

Ele pode ser um lobo fingindo ser uma avó, fazendo a maioria dos comportamentos que você espera que uma avó faça, mas com o objetivo de invadir a casa e roubar.

**Padrão de reivindicações, mas pode não atender **

Um método de classificação comum é adotar diretamente o padrão EIP-165 para ler se o endereço suporta ERC20, etc. Claro, este é um método eficiente, mas afinal, o contrato é controlado pela outra parte, então uma declaração pode ser afinal forjado.

A consulta padrão 165 é apenas um método para evitar que os fundos sejam transferidos para buracos negros com o menor custo entre os códigos de operação limitados da cadeia.

É por isso que quando analisamos o NFT antes, mencionamos especificamente que haverá um método SafeTransferFrom no padrão, onde Safe se refere ao uso do padrão 165 para determinar se a outra parte tem a capacidade de transferir NFT.

Somente começando pelo bytecode do contrato, fazendo análises estáticas no nível do código-fonte e começando pelo comportamento esperado do contrato, ele pode ser mais preciso.

3. Projeto do esquema de classificação do contrato

Em seguida, analisaremos sistematicamente o plano geral e observaremos que nosso objetivo final são os dois indicadores principais de "precisão" e "eficiência". **

Você deve saber que mesmo que a direção esteja correta, o caminho para chegar ao outro lado do oceano não é claro. A primeira parada para fazer a análise de bytecode é obter o código

3.1 Como obter o código?

Do ponto de vista de ir para a cadeia, existe o getCode, um método RPC, que pode obter o bytecode do endereço especificado na cadeia. É muito rápido em termos de leitura, pois o codeHash é colocado na estrutura da conta do EVM, bem no topo.

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

Mas esse método equivale a obter apenas um determinado endereço. Quer melhorar ainda mais a precisão e a eficiência?

Se for uma transação de implantação de contrato, como obter o código implantado logo após sua execução ou mesmo quando ainda estiver no pool de memória?

Se a transação estiver no modo de contrato de fábrica, existe algum código-fonte no Calldata da transação?

No final, meu jeito é classificar em modo de peneira

  1. Para transações implantadas sem contrato, use diretamente getCode para obter os endereços envolvidos para classificação.
  2. Para as últimas transações do pool de memória, filtre as transações cujo endereço está vazio e cujo CallData é o código-fonte com o construtor
  3. Para a transação do modo contract factory, como o contrato implantado pelo contrato pode ser reciclado para chamar outros contratos para executar a implantação, ele analisará recursivamente as subtransações da transação e registrará cada chamada cujo tipo é CREATE ou CRIAR2 .

Quando fiz uma implementação de demonstração, descobri que a versão rpc está relativamente alta agora, porque a parte mais difícil de todo o processo é como encontrar recursivamente a chamada do tipo especificado ao executar 3. O método de nível inferior é restaurar o contexto através do opcode. Fiquei surpreso!

Felizmente, existe um método debug_traceTransaction na versão atual do geth, que pode ajudar a classificar as informações de contexto de cada chamada por meio do código de operação do opcode e classificar os campos principais.

No final, os bytecodes originais de vários modos de implantação (implantação direta, implantação única no modo de fábrica, implantação em lote no modo de fábrica) podem ser obtidos.

3.2 Como classificar a partir do código?

A maneira mais simples, mas insegura, é fazer correspondência de string diretamente com o código. Tomando o ERC20 como exemplo, a função que atende ao padrão tem

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

Após o nome da função está a assinatura da função. Conforme mencionado na análise anterior, a transação depende da correspondência dos primeiros 4 dígitos de callData para encontrar a função de destino. Leitura adicional:

Portanto, as assinaturas dessas 6 funções devem ser armazenadas no bytecode do contrato.

Claro, esse método é muito rápido e você pode encontrar todos os 6, mas o fator inseguro é que se eu usar o contrato de solidez e projetar uma variável com um valor de armazenamento de 0x18160ddd, ele pensará que eu tenho essa função.

3.3. Melhoria da taxa de precisão 1- descompilação

O método mais preciso é descompilar o Opcode! A descompilação é o processo de converter os bytecodes obtidos em opcodes, e a descompilação mais avançada é convertê-los em pseudocódigos, o que é mais propício à leitura humana. Não precisamos disso desta vez. O método de descompilação está listado no apêndice em o final do artigo.

solidity (linguagem de alto nível) -> bytecode (bytecode) -> opcode (código de operação)

Podemos encontrar claramente um recurso, a assinatura da função será executada pelo opcode PUSH4, então o outro método é extrair o conteúdo após PUSH4 do texto completo e combiná-lo com o padrão da função.

Aprofunde-se nos riscos por trás da questão trivial da classificação do contrato EVM

Também fiz um experimento simples de desempenho e devo dizer que a linguagem Go é muito eficiente e leva apenas 220ms para 10.000 vezes de descompilação.

O que se segue será difícil

3.4. Melhoria da taxa de precisão 2-localizar bloco de código

A taxa de precisão acima foi melhorada, mas não o suficiente, porque é uma pesquisa de texto completo PUSH4, porque ainda podemos construir uma variável, que é do tipo byte4, que também acionará o comando PUSH4.

Quando estava angustiado, pensei na implementação de alguns projetos de código aberto. O ETL é uma ferramenta de leitura de dados na cadeia para análise. Ele analisará a transferência de ERC20 e 721 em tabelas separadas, portanto deve ter a capacidade de classificar contratos.

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

Após análise, pode-se constatar que ele se baseia na classificação de blocos de código e processa apenas os primeiros blocos_básicos [0] A instrução push4 em

A questão vem, como julgar com precisão o bloco de código

O conceito do bloco de código vem dos dois opcodes consecutivos de REVERT + JUMPDEST. Deve haver dois opcodes consecutivos aqui, porque no intervalo do opcode de todo o seletor de função, se houver muitas funções, a lógica da virada de página aparecerá Em seguida, o comando JUMPDEST também aparecerá.

Aprofunde-se nos riscos por trás da questão trivial da classificação do contrato EVM

3.5. Melhoria da taxa de precisão 3-Find seletor de função

A função do seletor de função é ler os primeiros 4 bytes do Calldata da transação e combiná-lo com a assinatura de função de contrato predefinida no código e auxiliar a instrução a pular para o local de memória especificado pelo método de função

Vamos tentar uma execução simulada mínima

Esta parte é o seletor store(uint 256) e retrieve() das duas funções, e a assinatura pode ser calculada como 2e64cec1, 6057361d

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

Após a descompilação, você obterá a seguinte string opcode, que pode ser dividida em duas partes

primeira parte:

No compilador, apenas a parte do seletor de função do contrato obterá o conteúdo de callData, ou seja, obter a assinatura de chamada de função de seu CallData, conforme mostrado na figura abaixo.

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

Podemos ver o efeito simulando a alteração do pool de memória do EVM

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

a segunda parte:

O processo de julgar se corresponde ao valor do seletor

  1. Passe a assinatura da função de 4 bytes (0x2e64cec1) de retrieve() para a pilha,

  2. O opcode EQ extrai 2 variáveis da área de pilha, ou seja, 0x2e64cec1 e 0x6057361d, e verifica se elas são iguais

  3. PUSH2 transfere 2 bytes de dados (0x003b aqui, 59 em decimal) para a pilha.Existe um contador de programa na área da pilha, que especifica a posição do próximo comando de execução no bytecode. Aqui definimos 59 porque é onde o bytecode retrieve() começa

  4. JUMPI significa "Jump to if...", ele retira 2 valores da pilha como entrada e, se a condição for verdadeira, o contador do programa será atualizado para 59.

É assim que o EVM determina a localização do bytecode de função que precisa executar com base na chamada de função no contrato.

Na realidade, este é apenas um conjunto simples de "declarações if" para cada função no contrato e para onde elas saltam.

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

4. Resumo do esquema

O resumo geral é o seguinte

  1. Cada endereço de contrato pode obter o bytecode após a implantação por meio de rpcgetcode ou debug_traceTransaction, usando as bibliotecas VM e ASM no GO e obter o opcode após a descompilação
  2. No princípio de funcionamento do EVM, o contrato terá as seguintes características
  • Use REVERT+JUMPDEST como a distinção do bloco de código
  • O contrato deve ter a função de seletor de função, e esta função também deve estar no primeiro bloco de código
  • No seletor de função, todos os seus métodos de função usam PUSH4 como opcode,
  • No opcode contido neste seletor, haverá consecutivos PUSH1 00; CALLDATALOAD; PUSH1 e0; SHR; DUP1. A função principal é carregar os dados callDate e realizar operações de deslocamento. Da função contrato, outra sintaxe não irá gerar
  1. A assinatura da função correspondente é definida no eip, e há instruções claras obrigatórias e opcionais

4.1. Prova de Unicidade

Neste ponto, podemos dizer que um método de análise de contrato de alta eficiência e alta precisão foi basicamente realizado. Claro, como tem sido rigoroso por tanto tempo, podemos ser mais rigorosos. No esquema acima, nós use REVER+JUMPDEST para fazer distinções de blocos de código e combine o inevitável carregamento e deslocamento de CallDate para fazer um julgamento único.Existe que eu possa usar um contrato de solidez para implementar uma sequência de opcode semelhante?

Eu fiz um experimento de controle.Embora existam métodos de obtenção de CallData como msg.sig do nível de gramática solidity, os métodos de implementação do opcode após a compilação são diferentes.

Aprofunde-se no risco por trás da questão trivial da classificação do contrato EVM

Ver original
O conteúdo serve apenas de referência e não constitui uma solicitação ou oferta. Não é prestado qualquer aconselhamento em matéria de investimento, fiscal ou jurídica. Consulte a Declaração de exoneração de responsabilidade para obter mais informações sobre os riscos.
  • Recompensa
  • Comentar
  • Partilhar
Comentar
0/400
Nenhum comentário
  • Pino
Negocie cripto em qualquer lugar e a qualquer hora
qrCode
Digitalizar para transferir a aplicação Gate.io
Novidades
Português (Portugal)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)