Você está na página 1de 14

Análise do Comportamento de Malware utilizando redes

neurais recorrentes - uma abordagem por imtermédio da


previsão de opcodes

Ricardo Sant’Ana1

1
Centro de Desenvolvimento de Sistemas (CDS)
QGEx - Bloco G - 2º Piso, SMU - Brası́lia, DF, 70630-901
ricardo.santana@ime.eb.br

Abstract. This text presents works carried out after the publication of the article
[Gomes de Albuquerque et al. 2021], so, it presents the natural evolution of the
article.

Resumo. Este texto apresenta trabalhos realizados após a publicação artigo


[Gomes de Albuquerque et al. 2021], ou seja, apresenta a evolução natural do
artigo.
1. Visão Geral do Artigo
No artigo, a representação selecionada foi a de mnemônicos, ou seja, após o processo de
disassembling do artefato, apenas a sequência de mnemônicos foi armazenada. A Figura
1 apresenta uma visão geral do processo. Inicialmente, em (1), realiza-se o disassembling
do artefato malicioso. Em seguida, a partir do código assembly produzido, são selecinados
mnemônicos, produzindo uma sequeência de mnemônicos.

Figure 1. Em (1) tem-se o processo de Disassembly do artefato malicioso,


seguido pelo processo de seleção dos mnemônicos (2).

Para estes experimentos, foram utilizados os vetores pré-treinados conforme apre-


sentado em [Massarelli et al. 2019]. Esta codificação considerou tanto os mnemônicos
como os operandos e a diemnsão do vetor codificado é de 100. Como definiu-se
que somente mnemônicos fariam parte da representação, para obter, por exemplo,
a codificação do mnemônico mov foi calculado a média entre todos os vetores de
[Massarelli et al. 2019] que continham o mnemônico mov. Na Figura 2 é apresentado um
exemplo do processo de cálculo da média. Considerando que existem apenas 3 instruções
em assembly codificados na base [Massarelli et al. 2019] que contém o mnemônico mov:
mov eax, 10, mov [b + eax], 0 e mov eax, ebx conforme [Massarelli et al. 2019], para
obter uma codificação única para o mov, uma média entre estes três vetores é calculada.

Figure 2. Processo de cálculo da média de 3 vetores mov produzindo uma única


representação para o mnemônico mov .

O modelo de LSTM utilizado foi implementado em keras1 tem a seguinte


configuração de hiperparâmetros:
• units: valor igual a dimensão do vetor de saı́da, ou seja, 100;
1
Keras versão 2.3.1 e Tensorflow versão 2.1.0)
• input shape: tem dimensão 5 (quantidade de vetores utilizados para predição) por
100 (dimensão do vetor de entrada).
• batch: valor de 100. Não tem relação com a dimensão do vetor.
Cada modelo de LSTM foi treinado com amostras de uma das classes de artefatos
maliciosos ou seja, 9 modelos foram gerados. Cada modelo tenta realizar a predição de
mnemônicos a partir de uma sequência de N mnemônicos de entrada. Para este experi-
mento, foi escolhido N = 5. Assim, a partir da sequência completa de todos mnemônicos
codificados de uma amotra, são geradas sequências com 5 elementos. O objetivo do mod-
elo é predizer a próxima amostra da sequência. A Figura 3 mostra um exemplo de 3
sequência com 5 mnemôncios (representados pela cor) alimentando o modelo LSTM da
classe 1 para predizer o próximo mnemônico.

Figure 3. Processo de cálculo da média de 3 vetores mov produzindo uma única


representação para o mnemônico mov .

O extrato de código a seguir apresenta a configuração geral utilizada em


Python (biblioteca Keras) e foi retirado do arquivo 03 − GeraM odeloLST M −
pred opcodes i2v v3.py .
1 look_back = 5
2 epoch = 100
3 units = dim
4 batch = 100
5 model = Sequential()
6 model.add(LSTM(units, input_shape=(look_back, dim)))
7 model.compile(loss='mean_squared_error', optimizer='adam')

Outros valores de hiperparâmetros utilizados (valores padrão) estão apresentados


a seguir:
• return sequences: false
• return state”: false
• stateful: false
• activation: tanh
• recurrent activation: sigmoid
Neste experimento, um conjunto de batch vetores de entrada são fornecidos ao
modelo LSTM para treinamento. O processo é repetido epoch vezes. O extrato a seguir
apresenta o trecho de código relacionado com esta descrição.
1 for ep in range(epoch):
2 ...
3 a, b = create_dataset(linhanumerada,look_back)
4 history = model.fit(a, b, epochs=1, batch_size=batch,verbose=0)
2. Propostas de Evolução
2.1. Efeito do epoch no resultado do LSTM
O objetivo deste experimento é avaliar como varia o desempenho do classificador em
9 classes a partir do preditor de mnemônicos treinado para cada classe em função do
número de epochs escolhido para o treinamento do modelo. Se for possı́vel definir um
número MENOR de epochs sem que isso afete de forma significativa o desempenho do
modelo, a consequência é que o tempo de execução do treinamento e teste do modelo
serão diminuı́dos de forma proporcional.
O resultado deste experimento pode indicar um valor mais adequado (e menor)
para o número de epochs, o que afetaria, proporcionalmente, o tempo de treinamento do
modelo. Para se ter uma ideia, o tempo aproximado de geração dos modelos 0, 1, 2, 3,
4, 5, 6, 7 e 8 com 100 epochs foram, respectivamente, 160 horas, 70 horas, 245 horas,
16 horas, 196 horas, 8 horas, 17 horas, 263 horas e 60 horas, o que totaliza 1.034 horas
ou 43 dias2 . Se for possı́vel obter 9 processadores para a geração dos modelos de forma
paralelizada, o tempo total para gerar os 9 modelos seria de 10 dias.

2.1.1. Desempenho f1-score

A avaliação do modelo para cada classe foi feita utilizando-se a métrica f 1−score. Foram
treinados e armazenados 9 modelos - um para cada classe - para os valores de epoch de
40, 60, 80 e 100, totalizando 36 modelos.
A sequência de mnemônicos de todos os artefatos maliciosos que fazem parte do
conjunto de treinamento, foram submetidas para cada um dos 9 modelos. A amostra
pertence a classe do modelo que obtivesse melhor acerto na predição de mnemônicos.
Com isso, foi obtida a métrica de desempenho f 1 − score para cada um das 9 classes. Os
resultados estão apresentados Tabela 1.

Table 1. f1-score por classe por epoch


Classe 20 epochs 40 epochs 60 epochs 80 epochs 100 epochs
0 % 39,88 % 41,86 % 46,27 % 49,36 %
1 % 58,96 % 58,22 % 59,09 % 62,37 %
2 % 73,94 % 73,26 % 73,95 % 66,78 %
3 % 38,44 % 38,05 % 37,86 % 39,43 %
4 % 0,38 % 0,44 % 2,13 % 2,31 %
5 % 84,10 % 81,43 % 78,83 % 75,37 %
6 % 66,67 % 50,36 % 49,93 % 64,03 %
7 % 38,87 % 18,66 % 20,41 % 41,59 %
8 % 44,30 % 44,10 % 38,99 % 41,12 %
Total % 445,54% 406,38% 407,45% 442,36%

A conclusão parcial é que deve ser realizado experimentos com valores de epoch
abaixo de 40. Além disso, o uso de mais epochs não necessariamente melhora o desem-
penho do classificador baseado em predição de mnemônicos.
2
Processador i3 6a geração.
2.1.2. Desempenho Preditor Mnemônicos

A Figura 4 apresenta a evolução do erro do preditor de mnemônicos para cada uma das 9
famı́lias de artefatos maliciosos. Neste contexto, a métrica utilizada é a que foi definida
no modelo do LSTM, ou seja, o erro quadrático médio entre o vetor predito e o vetor alvo.
Pode-se observar que as famı́lias 0, 3, 5, 6, 7 apresentaram uma evolução ade-
quada do erro quadrático médio, ou seja, o erro foi diminuindo com o passar de epochs.
Por outro lado, algumas outras famı́lias apresentaram comportamento anômalo, ou seja,
gráfico sugere que o algoritmo não foi capaz de aprender padrões de predição para as
famı́lias 1, 2, 3 e 8.
Os possı́veis motivos para este comportamento anômalo são:

• A complexidade da arquitetura utilizada do LSTM é simples demais. Assim,


pode-se inferir que nos casos em que o algoritmo foi capaz de diminuir o erro
quadrático médio, a complexidade implementada foi suficiente;
• A representação escolhida, ou seja, apenas utilizou-se os mnemônicos do código
assembly dos artefatos, não é suficiente para realizar a predição ou para caracteri-
zar a famı́lia;
• A codificação utilizada da representação não é suficiente. Considerando que foi
utilizado o word2vec, diversos hiperparâmetros do modelo podem ser ajustados de
forma a melhorar o desempenho do preditor de mnemônicos, como dimensãio do
vetor e quantidade, quantidade de iterações, valor mı́nimo para ser considerado,
entre outros.

Figure 4. Desempenho do preditor de mnemônicos para cada uma das 9 famı́lias


para One Hot Encoder 63+1.
2.2. Redução de Dimensionalidade da Codificação Word2Vec
De forma a diminuir o tempo de treinamento e execução (teste) do modelo proposto, uma
tentativa é a de diminuir a dimensão do vetor codificado com word2vec. Neste sentido,
foram realizados dois experimentos em que se utilizou o PCA para reduzir a dimensão
dos vetores de [Massarelli et al. 2019] de 100 para 80 e 50. Todos os modelos foram
treinamentos utilizando 100 epochs. A Figura 5 apresenta um exemplo da redução da
dimensionalidade de 100 para 50 utilizando o PCA.

Figure 5. Aplicação do PCA para reduzir dimensão de 100 para 501.

O extrato de código que realiza a operação de redução de dimensionalidade uti-


lizando PCA está apresentado a seguir:
1 rom sklearn.decomposition import PCA
2 pca = PCA(n_components=50)
3 pca.fit(vector)
4 print(pca.explained_variance_ratio_.sum())
5 vector2=pca.transform(vector)
6 np.save("i2v_opcodes_pca50.npy",vector2)

2.2.1. Resultados preliminares

Os resultados, na forma da métrica f 1 − score, estão apresentados na Tabela 2. Para fins


de comparação, utilizou-se como referência os resultados da codificação com a dimensão
100 original.
Da análise da Tabela 2, pode-se observar que utilizando o PCA para reduzir a
dimensão dos vetores de codificação de 100 para 50 afetou de forma negativo no desem-
penho de 8 das 9 classes. Reduzindo a dimensão da codificação de 100 para 80, verifica-se
que o desempenho foi afetado de forma negativa em 5 das 9 classes. Em um primeiro mo-
mento, poder-se-ia inferir que a codificação com dimensão 80 é interessante. No entanto,
observando-se o somatório dos f 1 − score para todas as classes, pode-se verificar que a
perda do modelo com dimensão reduzida para 80 é significativa.
Table 2. f1-score para dimensão 100 original, com redução para 80 e com
redução para 50.
Classe Original - Dimensão 100 Dimensão 80 Dimensão 50
0 49,36 % 39,58 % 3,63 %
1 62,37 % 59,28 % 0,36 %
2 66,78 % 2,93 % 57,22 %
3 39,43 % 44,58 % 30,95 %
4 2,31 % 18,23 % 0,89 %
5 75,37 % 90,76 % 86,09 %
6 64,03 % 22,61 % 39,09 %
7 41,59 % 20,62 % 19,13 %
8 41,12 % 52,60 % 12,41 %
Total 442,36% 351,19% 249,77%

Desta forma, resolveu-se pesquisar mais sobre o assunto em busca de soluções


de redução de dimensionalidade para codificações do tipo word2vec em artigos. Em
[Raunak 2017], o autor prpõe uma solução que reduz a dimensionalidade de diversas
codificações utilizadas em Processamento de Linguagem Natural, incluindo word2vec. O
Algoritmo 1 apresenta um resumo da proposta.

Algorithm 1 Algoritmo de Redução de Dimensionalidade


Require: Matriz X de codificação de palavras, nova dimensão N , Limite D
Ensure: N ≤ dim(X)
Aplicar Post-Processing Algorithm
Transformar X usando PCA
Aplicar Post-Processing Algorithm

A proposta de [Raunak 2017] se baseia em um outro algoritmo já proposto


chamado de Post-Processing Algorithm. Uma visão geral deste está apresentada no Al-
goritmo 2.

Algorithm 2 Post-Processing Algorithm


Require: Matriz X de codificação de palavras, Limite D
Subtrair de X a média: X ← X - mean(X)
Calcular as D componentes principais de X: ui = P CA(X), i = 1, 2...D
Eliminar as D top Componentes: v = v − D T
P
i=1 (ui .v)ui

2.2.2. Implementação

A implementação do algoritmo proposto foi orientada pelo código3 disponibilizado pelo


autor. A seguir temo o código utilizado. Cabe ressaltar que a substração da média pre-
vista em Post-Processing Algorithm foi modificada em relação ao código do autor. O
autor subtrai de X a média geral de X (que é um único valor), enquanto que no código
desenvolvido, é substraı́do de X o vetor média (média por dimensão).
3
https://github.com/vyraun/Half-Size
1 dimensao=50
2 P=7
3 vector = np.load("mnemonic/i2v_opcodes_100.npy")
4
5 # Passo 1 : Aplicar Post-Processing Algorithm
6 pca = PCA(n_components=100)
7 vector=vector-vector.mean(axis=0)
8 pca.fit(vector)
9 print(pca.explained_variance_ratio_.sum())
10 vector2=pca.transform(vector)
11 U1=pca.components_
12 z=[]
13 for i,v in enumerate(vector):
14 for u in U1[0:P]: # projeta cada vetor de vector nos top D
principais
15 v=v-u.dot(v)*u
16 z.append(v)
17 z=np.array(z)
18
19 # Passo 2: transformar z usando PCA
20 pca = PCA(n_components = dimensao)
21 X_train = z - np.mean(z)
22 X_new_final = pca.fit_transform(X_train)
23
24 # Passo 3 : Aplicar Post-Processing Algorithm
25 pca = PCA(n_components = dimensao)
26 X_new = X_new_final - np.mean(X_new_final)
27 X_new = pca.fit_transform(X_new)
28 Ufit = pca.components_
29 X_new_final = X_new_final - X_new_final.mean(axis=0)
30
31 final_pca_embeddings=np.zeros((len(vector),50))
32 for i in range(len(vector)):
33 final_pca_embeddings[i] = X_new_final[i]
34 for u in Ufit[0:P]:
35 final_pca_embeddings[i] = final_pca_embeddings[i] - u.dot(
final_pca_embeddings[i]) * u
2.3. Efeito de uma Arquitetura LSTM mais Complexa

A nova arquitetura de LSTM proposta envolve o uso de hidden nodes e uma camada
completamente conectada ao final (Dense). A orientação geral para a quantidade de hid-
den nodes (antigo units) e valor de Dropout foram definidos conforme apresentando em
[Eckhardte 2018]4 .
A fórmula original5 especifica um valor máximo para o número de camadas es-
condidas de forma que o modelo não realize overfitting .

1 look_back = 5
2 epoch = 100
3 hidden_nodes = int(2/3 * (look_back * dim))
4 batch = 100
5 model = Sequential()
6 model.add(LSTM(hidden_nodes, input_shape=(look_back, dim)))
7 model.add(Dropout(0.2))
8 model.add(Dense(units=dim))
9 model.compile(loss='mean_squared_error', optimizer='adam')

Outra possibilidade de se obter um modelo mais complexo é o de ”empilhar”


modelos LSTM.

4
https://github.com/R4h4/Firstname gender prediction/blob/master/Article Gender Prediction.ipynb
5
https://stats.stackexchange.com/questions/181/how-to-choose-the-number-of-hidden-layers-and-
nodes-in-a-feedforward-neural-netw/136542#136542
2.4. Codificação One Hot Encoder
Neste experimento, inicialmente, foi realizado o processo de disassenbly do artefato ma-
licioso, seguido da seleção dos mnemônicos como forma de representação da informação
do código assembly. Este processo já foi apresentado na Figura 1em que mostra as etapas
de disassebly e seleção dos mnemônicos.

2.4.1. One-Hot-Encoder para Mnemônicos

A ideia de se utilizar o one-hot-encoder, ao invés de uma codificacão que envolve treina-


mento e aprendizado de semântica como word2vec, não é trivial. Os vetores fruto da
codificação one-hot-encoder possuem apenas um dos bits como 1 e o restante dos bits
como zero. Assim, se a codificação for realizada em 2000 elementos (mnemônicos), a
dimensão do vetor codificado será de 2000. Vetores com esta dimensão e com as car-
acterı́sticas do one-hot-encoder dificultam o treinamento de algoritmos de aprendizado
de máquina. Para contornar este problema, define-se um valor N e seleciona-se apenas
os N mnemônicos de maior ocorrência. O restante dos mnemônicos são condificados
como um ÚNICO código denomiado UNKNOWN. Esta mesma ideia é implementada em
[Sun and Qian 2018].
Considerando o dicionário criado, dict opcodes rva capstone.dic.gz, tem-se um
total de 424.441.178 mnemônicos contabilizados. A tı́tulo de exemplo, os 8 mnemônicos
que mais ocorrem representam 48.22% do mnemônicos totais. São eles, em ordem de-
screvente de ocorrência: add, mov, push, pop, inc, cmp, xor e xchg, ocorrendo, respec-
tivamente, 69.927.652, 44.964.469, 28.158.424, 16.505.165, 12.473.321, 11.530.640,
10.9737.92 e 10.152.895 de vezes.
Da mesma forma, os N =127 mnemônicos de maior ocorrência representam
98,79% de todas as ocorrências. Os N =63 mnemônicos que mais ocorrem representam
86.88% de todas as ocorrências. Assim, dois experimentos com a codificação one-hot-
encoder foram definidos: com N =63 e N =127. O restante dos mnemônicos foi repre-
sentado por UNKNOWN. Os 127 mnemônicos de maior ocorrência são: add, mov, push,
pop, inc, cmp, xor, xchg, dec, db, and, sub, or, adc, sbb, test, jmp, call, in, out, clc, ret,
nop, je, jne, lea, int3, imul, retf , jae, cld, jb, leave, pushal, ljmp, loopne, jle, lcall,
aaa, jo, jl, jbe, jge, ja, std, rol, stc, popal, shr, jg, insb, js, outsd, outsb, arpl, jns,
aas, loop, hlt, cli, cwde, sti, pushf d, lodsd, enter, daa, jp, int, das, aam, cmc, jnp,
int1, jno, stosd, loope, xlatb, salc, wait, insd, aad, jecxz, lodsb, movsd, iretd, movsb,
cdq, shl, stosb, popf d, sahf , into, lahf , scasb, sar, scasd, cmpsd, cmpsb, bound, ror,
les, rcl, sal, rcr, lds, f stp, f ld, neg, not, f ild, f add, movzx, f comp, div, idiv, f istp,
f isttp, mul, f sub, f mul, f div, f divr, f com, f subr, f iadd, f st e f imul.

2.4.2. Resultados

Para fins de comparação, o resultado da métrica f 1 − score dos experimentos com one-
hot-encoder para 63+1 e 127+1 mnemônicos foram comparados com o resultado da
métrica f 1 − score para o experimento utilizando a codificação word2vec apresentada
em [Massarelli et al. 2019]. Ambos experimentos utilizaram a quantidade de epochs com
valor igual a 100. A Tabela 3 apresenta os resultados obtidos.
Table 3. f1-score por classe por Codificação
Classe One-Hot-encoder 63+1 One-Hot-encoder 127+1 word2vec[Massarelli et al. 2019]
0 44,44 % 62,42 % 49,36 %
1 73,06 % 76,75 % 62,37 %
2 64,30 % 56,57 % 66,78 %
3 53,08 % 51,11 % 39,43 %
4 35,74 % 55,89 % 2,31 %
5 91,39 % 91,37 % 75,37 %
6 37,77 % 48,72 % 64,03 %
7 37,99 % 36,93 % 41,59 %
8 65,08 % 70,86 % 41,12 %
Total 502,83% 550,61% 442,36 % %

Da análise desta Tabela, fica claro que a utilização do one-hot-encoder é muito


mais interessante. Acredita-se que o seu verdadeiro ganho esteja no fato de LIMITAR
o número de mnemônicos utilizados, selecionando os de maior ocorrência e, ainda,
utilizando um mnemônico coringa para representar o restante de menor ocorrência.
Esta mesma técnica pode ser utilizada com word2vec com a codificação utilizada em
[Massarelli et al. 2019].
Como a codfificação one-hot-encoder com 63+1 mnemônicos é mais rápida,
foram realizados experimentos para verificar como a quantidade de epochs afeta o de-
sempenho do classificador baseado em predição de mnemônicos. A Tabela 4 apresenta o
f 1 − score para 20,25, 50 e 100 epochs utilizando a codificação one-hot-encoder 63+1.

Table 4. f1-score por classe por epoch para ohe 63+1


Classe 20 epochs 25 epochs 50 epochs 100 epochs
0 51,36 % 50,91 % 52,16 % 44,44 %
1 73,71 % 73,52 % 72,39 % 73,06 %
2 57,84 % 60,15 % 39,28 % 64,30 %
3 53,75 % 53,77 % 53,82 % 53,08 %
4 35,50 % 40,97 % 39,72 % 35,74 %
5 91,28 % 91,37 % 91,22 % 91,39 %
6 34,43 % 36,60 % 35,79 % 37,77 %
7 34,58 % 36,08 % 31,56 % 37,99 %
8 56,87 % 61,15 % 61,89 % 65,08 %
Total 489,30% 504,51% 477,83% 502,83 % %

2.4.3. Gráficos Desempenho Predição de Mnemônicos

Especificamente para a codificação One-Hot-Encoder 63+1, estão apresentados os


gráficos da evolução do preditor de mnemônicos em função do número de epochs. A
Figura 6 apresenta o desempenho do preditor de mnemônicos para cada uma das 9
famı́lias de artefatos maliciosos.
Ao observar os gráficos pode-se concluir que o aprendizado por parte do LSTM
realmente aconteceu para famı́lias 1,3 e 5 apenas. A evolução do aprendizado para as
outras famı́lias mostra que o LSTM não foi capaz de aprender e identificar as sequências
relevantes, considerando que, para este experimento, a configuração da predição é de 5
para 1 e que foi utilizada a codificação one-hot-encoder 63+1.

Figure 6. Desempenho do preditor de mnemônicos para cada uma das 9 famı́lias


para One Hot Encoder 63+1.
References
Eckhardte, K. (2018). Choosing the right Hyperparameters for a sim-
ple LSTM using Keras. https://towardsdatascience.com/
choosing-the-right-hyperparameters-for-a-simple-lstm-using-keras.
[Online; accessed 01-January-2022].
Gomes de Albuquerque, D., de Queiroz Vieira, L., and Sant’Ana, Ricardoand Duarte, J. C.
(2021). Análise de comportamento de malware utilizando redes neurais recorrentes -
uma abordagem por intermédio da previsão de opcodes. Revista Militar De Ciência E
Tecnologia, 37(3).
Massarelli, L., Di Luna, G. A., Petroni, F., Baldoni, R., and Querzoni, L. (2019). Safe:
Self-attentive function embeddings for binary similarity. In International Conference
on Detection of Intrusions and Malware, and Vulnerability Assessment, pages 309–
329. Springer.
Raunak, V. (2017). Simple and effective dimensionality reduction for word embeddings.
arXiv preprint arXiv:1708.03629.
Sun, G. and Qian, Q. (2018). Deep learning and visualization for identifying malware
families. IEEE Transactions on Dependable and Secure Computing.

Você também pode gostar