Jorge Luiz Gouveia Sousa Posted December 11, 2024 Posted December 11, 2024 Olá? Estou tentando criar uma função em ASSEMBLY A função: soma int_para_string imprimir Se puder me ajudar? Ficarei muito grato! ; ========================= ; Programa Teste ; ========================= ; Compilação ; ========================= ; nasm -f elf32 teste.asm ; ld -m elf_i386 -s -o teste teste.o ; ./teste ; ========================= ; Resultado ; ========================= ; X eh maior ou igual que Y ; ========================= section .data: ; x = 50 x dd 50 ; dd - Define Double Word - 4 bytes ; db - Define Byte - 1 Byte ; dw - Define Word - 2 Bytes ; dq - Define Quad Word - 4 Bytes ; dt - Define Ten Word - 10 Bytes ; y = 10 y dd 10 section .text: global _start _start: mov eax, DWORD [x] mov ebx, DWORD [y] ; ************************** ; resultado = soma(x, y) ; resultado_string = int_para_string(resultado) ; imprimir(resultado_string) ; ************************** jmp final ; pula para o => final: ; ************************** soma: int_para_string: imprimir: ; ************************** ; Saida final: mov eax, 1 mov ebx, 0 int 0x80 Quote
Administrators Fernando Mercês Posted December 12, 2024 Administrators Posted December 12, 2024 Oi @Jorge Luiz Gouveia Sousa, beleza? Recomendo começar pela função de soma(), que é mais simples que a int_para_string(). Outra coisa, você tá trabalhando em Intel 32-bits e há convenções de chamadas estabelecidas para esta arquitetura. Veja: Para complementar, leia aqui também: https://mentebinaria.gitbook.io/engenharia-reversa/assembly/funcoes-e-pilha No entanto, não vejo problema em você criar a sua própria convenção de chamadas para fins de estudo. No caso, o jeito que você escolheu para passar os argumentos para a função tá sendo via registradores (EAX e EBX no seu exemplo). Depois disso você vai precisar da instrução CALL e no fim da sua função, a instrução RET. Já que você começou assim (passando argumentos via registradores), pode seguir depois você muda para uma convenção mais convencional se quiser. Tenta aí e volta com o que conseguiu que a gente ajuda mais. 😉 Abraço e bons estudos! Muito legal o desafio! Quote
Jorge Luiz Gouveia Sousa Posted December 13, 2024 Author Posted December 13, 2024 Vou usar jmp em vez de call! Depois passo para call! Vou fazer devagar! Não está imprimindo o resultado no Terminal! Alguém pode me ajudar? ; ========================= ; Programa Teste ; ========================= ; Compilação ; ========================= ; nasm -f elf32 teste.asm ; ld -m elf_i386 -s -o teste teste.o ; ./teste ; ========================= ; Resultado ; ========================= ; 8 ; ========================= section .data: ; x = 5 x db 5 ; dd - Define Double Word - 4 bytes ; db - Define Byte - 1 Byte ; dw - Define Word - 2 Bytes ; dq - Define Quad Word - 4 Bytes ; dt - Define Ten Word - 10 Bytes ; y = 3 y dd 3 section .text: global _start _start: ; ************************** ; resultado = soma(x, y) ; resultado_string = int_para_string(resultado) ; imprimir(resultado_string) ; ************************** jmp soma jmp int_para_string mov edx, eax mov ecx, 4 jmp imprimir jmp final ; pula para o => final: ; ************************** soma: mov eax, DWORD [x] mov ebx, DWORD [y] add eax, ebx ; ************************** int_para_string: add eax, '0' ; converte para String int 0x80 string_para_int: sub eax, '0' ; converte para inteiro int 0x80 imprimir: mov ebx, 1 mov eax, 4 int 0x80 ; Saida final: mov eax, 1 mov ebx, 0 int 0x80 Quote
Administrators Fernando Mercês Posted December 14, 2024 Administrators Posted December 14, 2024 8 horas atrás, Jorge Luiz Gouveia Sousa disse: Vou usar jmp em vez de call! Depois passo para call! Beleza. Nesse caso, saiba que você vai precisar de uma instrução JMP no final do procedimento para voltar para onde estava. Algo assim: _start: ; código jmp soma depois_da_soma: ; código soma: ; código jmp depois_da_soma Do contrário o fluxo vai para a soma e não volta para onde deveria. Ao invés disso, vai continuar para a próxima instrução depois da última instrução do seu rótulo soma. O mesmo deve ser feito para os outros rótulos. Alternativamente, você pode por uma coisa depois da outra, sem saltos, só para começar (depois você implementa os saltos): _start: soma: ; código int_para_string: ; código imprimir: ; código final: ; código Agora vamos aos problemas nos seus rótulos: Em int_para_string, você não vai usar nenhuma syscall, por isso não tem que ter aquele int 0x80 ali. Em imprimir, você tá usando a syscall write (eax=4). Ela precisa de três parâmetros: O file descriptor (fd) onde você quer escrever, em ebx (no caso como você quer imprimir na tela, ebx precisa ser 1). O endereço do buffer para ser escrito, em ecx. O tamanho do buffer a ser escrito, em edx. Ou seja, antes de chegar na int 0x80 dela, você precisa desses três parâmetros configurados. Logo, você precisa de uma terceira "variável" na seção .data para o resultado, ou pode sobrescrever a sua x ou y. No início do rótulo de imprimir você pode fazer: mov byte [resultado], al mov ebx, resultado A primeira instrução vai pegar o byte que está em AL (a parte baixa de AX, que é a parte baixa de EAX), que conterá o seu 0x36 (6 + '0') e copiar para o buffer resultado (como disse antes, se quiser usar o próprio x ou y, fique à vontade pois nesse momento você não precisará mais deles). A segunda vai pegar o endereço de resultado e por em ebx, que é o que a syscall write precisa. O tamanho do buffer será 1 também, pois você vai imprimir apenas um caractere. Então você precisa por 1 em edx antes de chegar na int 0x80 do imprimir, que vai chamar a write. Depois que isso funcionar a gente segue aumentando o programa com a lógica que você quer, o que acha? 🙂 PS.: Tem um botão para postar código aqui no fórum. No editor, é o botão <>. Fica bem melhor de lermos se você utilizá-lo, e será mais fácil te ajudar. 😉 Abraço. Quote
Jorge Luiz Gouveia Sousa Posted December 20, 2024 Author Posted December 20, 2024 Estou testando o código e está dando erro: Falha de segmentação (imagem do núcleo gravada) ; ========================= ; Programa Teste ; ========================= ; Compilação ; ========================= ; nasm -f elf32 teste.asm ; ld -m elf_i386 -s -o teste teste.o ; ./teste ; ========================= ; Resultado ; ========================= ; 8 ; ========================= BUFFER_DE_IMPRESSAO equ 10 section .data: ; x = '50' x db '50' ; dd - Define Double Word - 4 bytes ; db - Define Byte - 1 Byte ; dw - Define Word - 2 Bytes ; dq - Define Quad Word - 4 Bytes ; dt - Define Ten Word - 10 Bytes ; y = '30' y db '30' section .bss resp_x: resb 0 resp_y: resb 0 resposta: resb 0 section .text: global _start _start: mov eax, [x] call string_para_int mov [resp_x], eax mov eax, [y] call string_para_int mov [resp_y], eax call soma mov eax, [resposta] call int_para_string mov ecx, eax call imprimir jmp final ; pula para o => final: soma: mov eax, [resp_x] mov ebx, [resp_y] add eax, ebx mov [resposta], eax ret int_para_string: ; Converter Inteiro para String ; Entrada EAX ESI ; Saída EAX mov esi, eax add esi, 9 mov byte[esi], 0 mov ebx, 10 .prox_digito: xor edx, edx div ebx add dl, '0' dec esi mov [esi], dl test eax, eax jnz .prox_digito ; eax == 0 mov eax, esi ret string_para_int: ; Converte String para Inteiro ; Entrada: EAX ESI ; Saida EAX com o valor xor ebx, ebx .prox_digito: movzx eax, byte[esi] inc esi sub al, '0' imul ebx, 10 add ebx, eax ; ebx = ebx*10 + eax loop .prox_digito ; while (--ecx) mov eax, ebx ret imprimir: mov eax, 4 mov ebx, 1 mov edx, BUFFER_DE_IMPRESSAO int 0x80 ret ; Saida final: mov eax, 1 mov ebx, 0 int 0x80 Onde errei? Quote
fredericopissarra Posted December 23, 2024 Posted December 23, 2024 Tem um montão de erros ai... Tem certeza que quer fazer isso para o modo i386 e não o x86-64? Quote
fredericopissarra Posted December 24, 2024 Posted December 24, 2024 Ok... vamos lá... Estou supondo que o uso do modo i386 seja obrigatório no seu caso... Nesse modo temos apenas 7 registradores disponíveis: EAX, EBX, ECX, EDX, ESI, EDI e EBP. São poucos registradores para criar rotinas mais complexas, então é recomendável que usemos alguma convenção de chamada. Com isso alguns desses registradores poderão ser preservados entre chamadas e outros estaão livres para serem modificados. Não usarei a convenção cdecl, tradicional em compiladores C para o modo i386, mas sim uma versão modificada: Os 3 primeiros argumentos de uma função serão passados usando ECX, EDX e EAX; EAX sempre serã o valor de retorno (ou o par EDX:EAX em caso de retornarmos 64 bits); Os registradores EBX, ESI, EDI e EBP têm que ser preservados pelas nossas funções, se usados; Prólogos/Epílogos não são usados, para liberarmos EBP para uso geral. Outra coisa que farei é quebrar o código em funções separadas. Aqui teremos main.asm (contendo o label _start), lib.asm (funções que usaremos) e syscalls.asm (com as chamadas à syscalls). Usarei também header files, ao estilo de C, para organizar melhor as coisas: lib.inc e syscalls.inc. ; lib.inc %ifndef LIB_INC_ %define LIB_INC_ extern atou extern atoi extern utoa extern itoa %endif ; syscalls.inc %ifndef SYSCALLS_INC_ %define SYSCALLS_INC_ %define SYS_EXIT 1 %define SYS_READ 3 %define SYS_WRITE 4 %define STDIN_FILENO 0 %define STDOUT_FILENO 1 %define STDERR_FILENO 2 ; O argumento pode ser numérico ou um registrador/endereço efetivo. %macro exit_ 1 mov eax,SYS_EXIT %ifnum %1,ebx mov ebx,%1 %else %ifnidni %1,ebx mov ebx,%1 %endif %endif int 0x80 %endmacro %endif Note que aqui fiz de exit_ uma "função inline", criando-a como macro. Agora, eis syscalls.asm: ; syscalls.asm bits 32 %include "syscalls.inc" section .text global writeStr, readStr ; Escreve uma string de EDX chars, apontada por ECX, em stdout. ; ; Input: ECX = ptr, EDX = length writeStr: push ebx mov eax,SYS_WRITE mov ebx,STDOUT_FILENO int 0x80 pop ebx ret ; Lê uma string de até EDX bytes para o buffer apontado por ECX. ; O caracter final será '\n', se couber no buffer. ; A syscall devolve em EAX o tamanho lido ou um valor negativo ; (-errno) em caso de erro. ; ; Input: ECX = ptr, EDX = length ; Output: EAX = tamanho dos bytes lidos ou < 0 em caso de erro. readStr: push ebx mov eax,SYS_READ xor ebx,ebx int 0x80 pop ebx ret Essas syscalls não podem ser inline para que obedeçamos a nossa convenção de chamada. Quanto as funções de conversão de/para inteiros de/para strings, elas estão em lib.asm: ; lib.asm bits 32 section .text global atou, atoi global utoa, itoa ; Converte uma string de EDX bytes, apontada por ECX, para um 'unsigned int' em EAX. ; EDX tem que ser > 0 e o buffer só pode conter caracteres numéricos. ; ; Input: ECX = ptr para string ; EDX = length ; Output: EAX align 4 atou: test edx,edx jz .exit push ebx xor eax,eax align 4 .loop: imul eax,eax,10 mov bl,[ecx] sub bl,'0' movzx ebx,bl add eax,ebx inc ecx dec edx jnz .loop pop ebx .exit: ret ; Quase a mesma função atou, mas considere o sinal. ; Além das restrições de atou o valor não pode ser INT_MIN (-2³¹). align 4 atoi: xor eax,eax test edx,edx jz .exit push ebx mov bl,[ecx] cmp bl,'-' jne .position inc ecx dec edx .position: call atou cmp bl,'-' jne .position2 neg eax .position2: pop ebx .exit: ret ; Função local que calcula a quantidade de dígitos que ; serão necessários para acomodar a string (unsigned int). ; ; Input: EDX = value ; Output: ESI = # of digits utoa_digits_: mov esi,1 cmp edx,10 jb .exit inc esi cmp edx,100 jb .exit inc esi cmp edx,1000 jb .exit inc esi cmp edx,10000 jb .exit inc esi cmp edx,100000 jb .exit inc esi cmp edx,1000000 jb .exit inc esi cmp edx,10000000 jb .exit inc esi cmp edx,100000000 jb .exit inc esi cmp edx,1000000000 jb .exit inc esi .exit: ret ; Converte um `unsigned int` em EDX para string num buffer apontado por ECX. ; O buffer tem que ter, pelo menos, 10 bytes de tamanho. ; Retorna o tamanho dos dados em EAX e a posição inicial onde a string está em ECX. ; ; Input: ECX = pointer to str ; EDX = int ; Output: EAX = string length ; ECX = beginning of the string ptr. align 4 utoa: push ebx push esi call utoa_digits_ .start: mov ebx,10 mov eax,edx lea ecx,[ecx+esi] align 4 .loop: dec ecx xor edx,edx div ebx add dl,'0' mov [ecx],dl test eax,eax jnz .loop mov eax,esi pop esi pop ebx ret ; Mesma coisa que utoa, mas requer buffer de 11 bytes. align 4 itoa: push ebx call utoa_digits_ test edx,edx jns .positive neg edx inc ecx inc ebx .positive: call utoa test ebx,ebx jz .exit dec ecx mov byte [ecx],'-' inc eax .exit: pop ebx ret Deixo-as aqui para seu estudo (Obs: Dá para melhorá-las, em termos de performance, mas elas ficarão um cadinho mais complexas). Finalmente o módulo principal, main.asm: ; main.asm bits 32 %include "syscalls.inc" %include "lib.inc" extern writeStr section .text global _start ; Exemplo de conversão de valor em EDX para string e a imprime. align 4 _start: sub esp,12 ; Aloca o buffer na pilha (multiplo de 4). mov ecx,esp mov edx,-123 ; Valor a converter. call itoa mov edx,eax call writeStr add esp,12 ; Dealoca buffer da pilha (não precisamos mais dele). lea ecx,[ln] mov edx,1 call writeStr exit_ 0 section .rodata align 4 ln: db `\n` Também deixo para seus estudos... Falta apenas um Makefile e compilarmos e linkarmos tudo: .PHONY: clean distclean main: main.o lib.o syscalls.o ld -s -m elf_i386 $^ -o $@ main.o: main.asm lib.inc syscalls.inc nasm -felf32 -o $@ $< lib.o: lib.asm nasm -felf32 -o $@ $< syscalls.o: syscalls.asm syscalls.inc nasm -felf32 -o $@ $< clean: -rm *.o distclean: clean rm main Compilando, linkando e executando: $ make nasm -felf32 -o main.o main.asm nasm -felf32 -o lib.o lib.asm nasm -felf32 -o syscalls.o syscalls.asm ld -s -m elf_i386 main.o lib.o syscalls.o -o main $ ./main -123 []s Fred Quote
Jorge Luiz Gouveia Sousa Posted January 1 Author Posted January 1 Não! Obrigado! Quero consertar o erro no meu código! Poderia consertar o erro no meu código? Pra depois avançar? Quote
Jorge Luiz Gouveia Sousa Posted January 1 Author Posted January 1 ; ========================= ; Programa Teste ; ========================= ; Compilação ; ========================= ; nasm -f elf32 teste.asm ; ld -m elf_i386 -s -o teste teste.o ; ./teste ; ========================= ; Resultado ; ========================= ; 80 ; ========================= BUFFER_DE_IMPRESSAO equ 10 section .data: ; x = '50' x db '50' ; dd - Define Double Word - 4 bytes ; db - Define Byte - 1 Byte ; dw - Define Word - 2 Bytes ; dq - Define Quad Word - 4 Bytes ; dt - Define Ten Word - 10 Bytes ; y = '30' y db '30' section .bss resp_x: resb 0 resp_y: resb 0 resposta: resb 0 section .text: global _start _start: ; ======================= ; resp_x = string_para_int(x) ; ======================= mov eax, [x] call string_para_int mov [resp_x], eax ; ======================= ; resp_y = string_para_int(y) ; ======================= mov eax, [y] call string_para_int mov [resp_y], eax ; ======================= ; resposta = soma(resp_x, resp_y) ; ======================= call soma ; ======================= ; eax = int_para_string(resposta) ; ======================= mov eax, [resposta] call int_para_string ; ======================= ; imprimir(eax) ; ======================= mov ecx, eax call imprimir ; ======================= ; sair ; ======================= jmp final ; pula para o => final: ; ======================= ; Função Soma ; ======================= soma: mov eax, [resp_x] mov ebx, [resp_y] add eax, ebx mov [resposta], eax ret ; ======================= int_para_string: ; Converter Inteiro para String ; Entrada EAX ESI ; Saída EAX mov esi, eax add esi, 9 mov byte[esi], 0 mov ebx, 10 .prox_digito: xor edx, edx div ebx add dl, '0' dec esi mov [esi], dl test eax, eax jnz .prox_digito ; eax == 0 mov eax, esi ret string_para_int: ; Converte String para Inteiro ; Entrada: EAX ESI ; Saida EAX com o valor xor ebx, ebx .prox_digito: movzx eax, byte[esi] inc esi sub al, '0' imul ebx, 10 add ebx, eax ; ebx = ebx*10 + eax loop .prox_digito ; while (--ecx) mov eax, ebx ret imprimir: mov eax, 4 mov ebx, 1 mov edx, BUFFER_DE_IMPRESSAO int 0x80 ret ; Saida final: mov eax, 1 mov ebx, 0 int 0x80 Depois de concertar os erros do código, depois iremos avançar! Quote
Jorge Luiz Gouveia Sousa Posted Thursday at 06:56 PM Author Posted Thursday at 06:56 PM Alguém pra ajudar uma pessoa que está aprendendo ASSEMBLY? Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.