COVIL HACKER

, ! .


» COVIL HACKER » C/C++/C#/.NET/Java » Artigo Gerando código lixo em C++ em modelos


Artigo Gerando código lixo em C++ em modelos

1 2 2

1

Ao escrever software, às vezes é necessário esconder seu código de olhares indiscretos, confundir o reversor (ou antivírus) para que o software não grunhisse (ou para que o software não fosse disparado por antivírus).
Uma maneira de fazer isso é com um gerador de código lixo que cria instruções sem sentido para diluir o código que faz o trabalho real.
Embora o tópico de geração de lixo tenha sido mencionado mais de uma vez em artigos neste fórum, mas principalmente o lixo foi gerado no nível do montador, e é por isso que o binário final não parece o trabalho de um compilador real,
portanto, um reversor com um olho treinado, ou uma IA bem treinada, será capaz de decifrar esse código, mas e se o próprio compilador for forçado a gerar lixo?
A ideia principal é escrever blocos de lixo manualmente como este (seja criativo):

inline unsigned Block(unsigned dummy) {
    unsigned result = dummy;
    for (int i = 0; i < dummy % 1024; i++) {
        result *= 2;
        result += i;
    }
    return result + dummy;
}
Importante: cada bloco deve ser inline (melhor __forceinline, ou, para gcc, always_inline) para que o lixo seja construído no site de chamada
E de alguma forma chame os blocos em ordem aleatória, e a ordem deve ser determinada em tempo de compilação, e o lixo em si deve estar alinhado ao site da chamada.
Também deve ser possível especificar a quantidade de lixo a ser gerada (não escreva 50 Junk(); Junk(); Junk(); chamadas seguidas).
Primeiro, vamos resolver o problema de indicar a quantidade de lixo, escrever a função auxiliar constexpr para (análoga a constexpr if, honestamente emprestada de onde não me lembro)

template <auto Start, auto End, auto Inc, class F>
constexpr void constexpr_for(F&& f) {
    if constexpr (Start < End) {
        f(std::integral_constant<decltype(Start), Start>());
        constexpr_for<Start + Inc, End, Inc>(f);
    }
}

É simples, colocamos o número de iterações e os valores de onde e onde iterar nos parâmetros do modelo e colocamos lambda e constexpr no parâmetro regular para que tudo isso seja considerado em tempo de compilação.
Agora precisamos de alguma forma chamar os blocos de lixo no loop, escolhendo um bloco aleatoriamente em tempo de compilação:

template <unsigned Seed, int Amount>
inline unsigned GenerateJunk(unsigned dummy) {
    unsigned result;
    constexpr auto kBlocksAmount = 3;
    constexpr_for<0, Amount, 1>([&](auto i) {
        if constexpr ((Seed * i) % kBlocksAmount == 0) {
            result *= Block1(dummy);
        } else if constexpr ((Seed * i) % kBlocksAmount == 1) {
            result *= Block2(dummy);
        } else if constexpr ((Seed * i) % kBlocksAmount == 2) {
            result *= Block3(dummy);
        }
    });
    return result;
}

Para que este código funcione, você precisa declarar as funções Block1, Block2 e Block3 em algum lugar,

Seed * i

- esta é a própria parte responsável pela aleatoriedade da ordem da chamada, Seed deve ser determinado em tempo de compilação e multiplicação por i para que o mesmo lixo não seja gerado a cada iteração do loop, constexpr se garante que o ordem não será calculada em tempo de execução.

result *= Block1...

para que o compilador não otimize todo o lixo e o jogue fora do binário resultante.
Um exemplo de uso de uma função:

Como o primeiro parâmetro do modelo - qualquer número aleatório conhecido em tempo de compilação (__LINE__, __COUNTER__, __TIME__), como o segundo - qualquer número que EXATAMENTE não será conhecido em tempo de compilação,
porque se o compilador souber esse número, ele poderá executar todo o lixo durante a compilação e substitua o resultado no local da chamada, jogando fora todo o lixo do binário. Também é necessário usar o número que o lixo retornou para criar efeitos colaterais (por exemplo, especificar esse número como código de retorno ou imprimi-lo no console), caso contrário, o compilador cortará todo esse código. seu resultado não é usado.
Resultado final: https://godbolt.org/z/311eKqjWc
15 mil linhas de assembler, mas mesmo com __forceinline o lixo não foi totalmente embutido (no código principal tem algumas transições, multiplicações, mas é difícil entender o que significam mesmo com dicas godbolt), então esse código tem pouco valor prático, mas acho que se você terminar, brincar com os sinalizadores do compilador, forçando-o a inline tudo, adicionar mais blocos, você pode obter algo adequado para uso real

0

2


» COVIL HACKER » C/C++/C#/.NET/Java » Artigo Gerando código lixo em C++ em modelos


|