Página 1 de 1

M.1. Introdução à manipulação de memória

Enviado: 17 Ago 2018, 16:02
por Junior_Djjr
Imagem
Concluindo esta parte você vai:
Saber os princípios do manipulamento de memória, a sua utilidade e como usar os comandos READ_MEMORY e WRITE_MEMORY

Esta é uma parte fora dos "essenciais". É aqui que começamos a nos aprofundar na criação dos mods avançados!

Atenção:
Por favor, se você está aqui eu imagino que você já esteja começando a ficar bom na criação de mods cleos.
Este "episódio" onde eu ensino sobre memórias eu imagino que você já tenha lido todas as partes "Essenciais" do índice, senão você terá dificuldades.


Introdução

Os comandos WRITE_MEMORY e READ_MEMORY foram os 2 primeiros comandos da biblioteca CLEO, ainda lá na CLEO 1.
Ou seja, você está agora aprendendo algo que foi a origem de toda a CLEO.

E por que eles são tão especiais assim? Por que justamente estes dois comandos foram os primeiros de tantos? É agora que você entenderá.


Embaixo do capô

Modding é uma forma de hacking, e para fazermos estes hacks geralmente precisamos ir mais a fundo do que realmente podemos (poderíamos).
Com o manuseamento/manipulamento de memória nós podemos quebrar todos os limites que tínhamos, pegando/alterando qualquer valor, cada byte, cada bit do jogo inteiro, e até mesmo fora dele.

Isto serve principalmente para fazer coisas que nenhum comando existente faz. De fato, todo o funcionamento de um jogo (programa e seja o que for) funciona numa memória, como já foi levemente introduzido na parte Bits & Bytes do tutorial que você pode querer revê-la antes de prosseguir.

Tendo os endereços ("ponteiros", "pointers") para os locais destes valores na memória, nós podemos fazer o que bem entender, e estou falando de uma coisa que pode valer tanto para um endereço do próprio executável (que é estático) quanto para endereços dinâmicos, como o endereço onde está armazenado informações de um pedestre específico, como por exemplo, saber qual é o "skill" de arma dele (uma coisa que ainda não existe comando para isso, entendeu?)


Como usar os comandos?

Argumentos:

Código: Selecionar tudo

READ_MEMORY endereço tamanho virtual_protect (valor_retorno)
endereço
 (INT) um ponteiro para algum local da memória, geralmente escrito em hexadecimal (0x).

tamanho
(INT) número de bytes para serem lidos, geralmente 1, 2 ou 4.

virtual_protect
(BOOL)* ative-o caso a memória tenha proteção. De fato, você só precisará caso fazer operações mais avançadas como hooks e NOPs, portanto, não se preocupe tanto com isso, normalmente você usará desativado.
* Lembrando que boolean pode ser usado como "0", "FALSE" ou "OFF" para desativado, e "1", "TRUE" ou "ON" para ativado.

valor_retorno
(QUALQUER) a variável que ganhará o valor que foi lido. Aceita qualquer data type, onde obviamente o compilador não sabe que valor irá retornar, portanto ele não te alertará de nada caso você estiver usando data type incorreto.

Exemplo:

Código: Selecionar tudo

READ_MEMORY 0xB7CE50 4 FALSE (var)
0xB7CE50
ponteiro onde está armazenado o dinheiro do player.

4
está armazenado em DWORD, onde uma DWORD contém 4 bytes, portanto, vamos ler 4 bytes desse endereço.

FALSE
é só uma simples leitura de um endereço comum, podemos usar FALSE (0) sem problemas.

var
variável onde será armazenado o valor contido neste endereço, onde neste caso em particular, é o dinheiro do player, e o dinheiro está armazenado em um INT (número inteiro), portanto vamos usar uma variável INT aqui.

Ou seja, agora a nossa variável chamada var tem o valor presente no endereço 0xB7CE50, que é onde está guardado o dinheiro do player.
Se você agora mostrar na tela:

Código: Selecionar tudo

PRINT_FORMATTED_NOW "%i" 1000 (var)
Verá o valor do dinheiro do player.

E se nós quisermos escrever um novo valor lá? WRITE_MEMORY!

Argumentos:

Código: Selecionar tudo

WRITE_MEMORY endereço tamanho (valor) virtual_protect
Auto-explicativo. Igual ao READ_MEMORY, só mudando a ordem do argumento do valor.

Portanto...:

Código: Selecionar tudo

READ_MEMORY 0xB7CE50 4 FALSE (var)
var += 100
WRITE_MEMORY 0xB7CE50 4 (var) FALSE
Eu li o valor do dinheiro e aumentei 100, e em seguida re-escrevi o novo valor na memória, assim sobrescrevendo o valor anterior pelo meu novo valor. O que eu fiz foi simplesmente aumentar 100 no dinheiro do player.

Outro exemplo:

Código: Selecionar tudo

WRITE_MEMORY 0xB7CE50 4 (1000) FALSE
Isso simplesmente mudou o valor do dinheiro do player para 1000, simplesmente escrevendo o valor 1000 para onde o dinheiro dele está armazenado.

Ei, mas isso é totalmente inútil, pois nós podemos usar os comandos STORE_SCORE e ADD_SCORE para manipular o dinheiro do player!

Você está certo meu caro questionador anônimo. Então que tal criarmos uma previsão do tempo? Sim, vamos prever o tempo melhor do que qualquer meteorologista:

Código: Selecionar tudo

READ_MEMORY 0xC8131C 2 FALSE (next_weather)
Pronto. A variável next_weather agora tem o valor do próximo clima que está chegando (mais especificamente, um número de identificação do clima, se lhe interessou, dê uma olhada no source do meu mod Weather Forecast). E sim, você pode inclusive mudar este valor com o WRITE_MEMORY.

Mas possivelmente isso está te deixando mais confuso ainda: De onde diabos eu arranquei esse 0xB7CE50 e 0xC8131C????? Como que eu sei que um é 4 e outro é 2 bytes??

É aí que as coisas ficam mais interessantes!


Encontrando endereços para usar

Estes endereços são descobertos pela comunidade. Alguém conseguiu de alguma forma, geralmente tentando entender o arquivo .exe do jogo, usando um processo relativamente considerado ilegal chamado "engenharia reversa", usando IDA (Interactive Disassembler), e a tal pessoa soube que aquilo é o número de identificação do próximo clima, pois o número contido neste endereço de memória é usado dentro do .exe na função onde processa o próximo clima do jogo e assim por diante... (Ora ora, temos um Xeroque Rolmes aqui)

Você também pode encontrar seus próprios endereços de memória, mas eu não recomendo que você, neste momento, com baixa experiência, tente isso (principalmente caso não saiba os básicos do C++ e Assembly). Também porque no futuro eu tentarei criar um novo tutorial melhor aqui no fórum.
Ao invés, pesquise e encontre. Existe milhares de endereços já documentados que você pode usá-los, é só procurar no Google, você encontrará vários na GTA Forums por exemplo.

Eu recomendo altamente que você tente brincar com esta lista aqui:
http://www.gtamodding.com/wiki/Memory_Addresses_(SA)
Eu particularmente me encantei quando aprendi a manipular valores de endereços de memória e conheci esta lista!

Por exemplo:

Código: Selecionar tudo

0x863984 - [float] Gravity (default value: 1.0f/125.0f = 0.008f)
Isso quer dizer, que:

Código: Selecionar tudo

READ_MEMORY 0x863984 4 FALSE (gravity)
A variável gravity (que é uma FLOAT) agora terá o valor 0.008, e você pode por exemplo aumentar, diminuir etc e reescrever o valor lá.

Assim como... Que tal simular a gravidade de outros planetas/astros?

Código: Selecionar tudo

WRITE_MEMORY 0x863984 4 (0.00304) 0 // Mercúrio
WRITE_MEMORY 0x863984 4 (0.0072) 0 // Vênus
WRITE_MEMORY 0x863984 4 (0.00304) 0 // Marte
WRITE_MEMORY 0x863984 4 (0.021144) 0 // Júpiter
WRITE_MEMORY 0x863984 4 (0.009272) 0 // Saturno
WRITE_MEMORY 0x863984 4 (0.00936) 0 // Urano
WRITE_MEMORY 0x863984 4 (0.0088) 0 // Netuno
WRITE_MEMORY 0x863984 4 (0.000528) 0 // Plutão
WRITE_MEMORY 0x863984 4 (0.224) 0 // Sol
WRITE_MEMORY 0x863984 4 (0.00136) 0 // Lua

E você ainda pode estar confuso sobre a quantidade de bytes (argumento "size", o tamanho pra ser lido ou escrito), mas dá para simplificar:
Byte = 1 Byte é uma letra ou um número inteiro de valor 0 até 255 (ou -128 até 127 caso signed)
(2 bytes é até 65535, 4 bytes é até 2147483647)

Word = 2 Bytes
Dword = 4 Bytes
Qword = 8 Bytes — lembrando que nossas variáveis INT só cabem 4 bytes, e nem há variáveis de 8 bytes no jogo, exceto se for strings por exemplo.
Float = 4 Bytes

E isso é muito mais dinâmico que você possa imaginar.

Você pode ter o ponteiro de literalmente qualquer coisa (desde que você consiga, de alguma forma).

Por exemplo:

Código: Selecionar tudo

GET_VAR_POINTER var1 (pvar1)
A variável pvar1 terá o ponteiro para a variável var1.

Se por exemplo a variável var1 contém uma string (text label) com valor ABCDEF e agora você fizer isto:

Código: Selecionar tudo

pvar1 += 2
WRITE_MEMORY pvar1 1 (0x41) FALSE // 0x41(65) = "A"
Agora a variável var1 terá o valor ABADEF, pois eu peguei o ponteiro para a (o início da) variável var1, subi 2 bytes (ou seja, duas letras) e escrevi o valor hexadecimal 0x41 lá (que é o mesmo de decimal 65), que é o hexadecimal para a letra "A" maiúscula. Com size de 1 byte, pois é só 1 letra.

Sabendo que no fim de uma string contém um "null terminator" (você lembra da parte 9 do tutorial?) dizendo "fim da string", se nós escrevêssemos o valor 0 ali (que é o valor null, e quando eu digo "ali", me refiro a exatamente o mesmo código acima, ou seja, em vez de escrever A, escrever um null terminator), a string da variável var1 se tornará somente AB em vez de ABCDEF (ou ABADEF considerando o último exemplo). E isto aconteceu pois você colocou o null terminator (0 ou 0x0 ou 0x00, são a mesma coisa) logo após o B e assim agora a string só é considerada até lá, se tornando AB. Interessante, não? Não que isso seja útil ao criar mods simples, mas ilustra muito bem o que temos aqui.

Este é só um dos exemplos mais "avançados" e "dinâmicos" que você pode fazer, onde, de fato, você pode imaginar qualquer coisa, desde que entenda como os valores são armazenados numa memória (o que às vezes pode ser confuso, mas você vai aprendendo com o tempo usando muita lógica, teoria e prática).
 

Esta é só a primeira parte de uma parte mais avançada deste tutorial, e você ainda verá muito sobre endereços de memória, ponteiros, offsets etc nas próximas partes, o que lhe fará entender mais sobre o assunto.
Neste momento você pode treinar um pouco com a lista que já citei aqui, mesmo que não tenha tanta coisa, há bastante coisa útil.

Próxima parte:
Thread Memory

Re: M.1. Introdução à manipulação de memória

Enviado: 04 Out 2018, 22:48
por Legacy
Então o MixSets é basicamente os endereços dessa lista(http://www.gtamodding.com/wiki/Memory_Addresses(SA)) unidos em um mod só para facilitar o uso? 

Re: M.1. Introdução à manipulação de memória

Enviado: 05 Out 2018, 04:15
por Junior_Djjr
Legacy escreveu:
04 Out 2018, 22:48
Então o MixSets é basicamente os endereços dessa lista(http://www.gtamodding.com/wiki/Memory_Addresses(SA)) unidos em um mod só para facilitar o uso?
Essa lista não é nem 0,001% do que temos documentado no jogo. É deste tópico completamente revisado:
https://gtaforums.com/topic/194199-docu ... -adresses/
(no entanto acho que tem algumas coisas a mais que poderiam ser adicionadas daí)
Mais algumas coisas no source do MTA:SA, sources de outros mods entre outros endereços encontrados por mim com engenharia reversa.
A maioria são só um ou outro endereço, assim como funções com NOP para desativá-las (por exemplo para desativar os pássaros do jogo, é só um NOP), mas também tem uns hooks.

Re: M.1. Introdução à manipulação de memória

Enviado: 04 Jan 2019, 20:04
por Legacy
Só uma dúvida relacionada ao IDA. É possível utilizar o OllyDBG ou algum programa no lugar dele?
Não estava a fim de piratear.

Re: M.1. Introdução à manipulação de memória

Enviado: 04 Jan 2019, 21:06
por Junior_Djjr
Legacy escreveu:
04 Jan 2019, 20:04
Só uma dúvida relacionada ao IDA. É possível utilizar o OllyDBG ou algum programa no lugar dele?
Não estava a fim de piratear.
Não faço ideia, nunca vi alguém usando este, sempre usam IDA.

Re: M.1. Introdução à manipulação de memória

Enviado: 08 Mai 2019, 18:38
por PauloKim
Pra quem não quer piratear, tem uma boa alternativa pro IDA (se não melhor ou bem pareçido), o Ghidra
Que inclusive foi criada pela NSA ou National Security Agency (agência de segurança dos Estados Unidos)
https://ghidra-sre.org/
 

Re: M.1. Introdução à manipulação de memória

Enviado: 09 Mai 2019, 01:51
por Junior_Djjr
ElTacoBanido escreveu:
08 Mai 2019, 18:38
Pra quem não quer piratear, tem uma boa alternativa pro IDA (se não melhor ou bem pareçido), o Ghidra
Que inclusive foi criada pela NSA ou National Security Agency (agência de segurança dos Estados Unidos)
https://ghidra-sre.org/
Se não abre arquivos .IDB e não existe uma maneira de convertê-los, não é uma alternativa.

Re: M.1. Introdução à manipulação de memória

Enviado: 28 Ago 2020, 09:06
por kurt
pq esse loop n funciona ? 

Código: Selecionar tudo

READ_MEMORY 0xBAA420 4 FALSE (wanted_level)
wanted_level++
WRITE_MEMORY 0xBAA420 4 wanted_level FALSE

Re: M.1. Introdução à manipulação de memória

Enviado: 28 Ago 2020, 19:22
por Junior_Djjr
Botar isso em loop em pouquíssimos milissegundos já ultrapassa o valor máximo e tudo deixa de funcionar direito.
Outra, o jogo deve alterar esse valor todos os frames, não deixa o script alterar.
Isto é só o "last wanted level", não deve ter nenhuma utilidade a não ser consulta.

Re: M.1. Introdução à manipulação de memória

Enviado: 01 Set 2020, 15:09
por Withdrawpurpose
Junior_Djjr escreveu:
17 Ago 2018, 16:02
Sabendo que no fim de uma string contém um "null terminator" (você lembra da parte 9 do tutorial?) dizendo "fim da string", se nós escrevêssemos o valor 0 ali (que é o valor null), a string da variável var1 seria, de fato, somente AB, pois agora você colocou o null terminator (0 ou 0x0 ou 0x00, não importa como você escreva) logo após o B e assim agora a string só é considerada até lá. Interessante, não? Não que isso seja útil ao criar mods simples, mas ilustra muito bem o que temos aqui.
Não entendi muito bem, no caso se no lugar do 0x41 eu colocasse 0 ou 0x0 ou 0x00(que é a mesma coisa ?) e colocasse para aparecer essa string na tela, ela so apareceria AB por que o resto não foi levado em conta pois definimos um null terminator ? 

Re: M.1. Introdução à manipulação de memória

Enviado: 02 Set 2020, 14:30
por Junior_Djjr
Withdrawpurpose escreveu:
01 Set 2020, 15:09
Junior_Djjr escreveu:
17 Ago 2018, 16:02
Sabendo que no fim de uma string contém um "null terminator" (você lembra da parte 9 do tutorial?) dizendo "fim da string", se nós escrevêssemos o valor 0 ali (que é o valor null), a string da variável var1 seria, de fato, somente AB, pois agora você colocou o null terminator (0 ou 0x0 ou 0x00, não importa como você escreva) logo após o B e assim agora a string só é considerada até lá. Interessante, não? Não que isso seja útil ao criar mods simples, mas ilustra muito bem o que temos aqui.
Não entendi muito bem, no caso se no lugar do 0x41 eu colocasse 0 ou 0x0 ou 0x00(que é a mesma coisa ?) e colocasse para aparecer essa string na tela, ela so apareceria AB por que o resto não foi levado em conta pois definimos um null terminator ? 
Eu acho que porque não deixei claro o que é "ali", eu me refiro ao exato mesmo código acima da explicação, ou seja, em vez de escrever A, escrever um null terminator. Eu atualizei a explicação lá agora.