Jump to content

Recommended Posts

Posted

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

  • Administrators
Posted

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! 

Posted

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

  • Administrators
Posted
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.

Posted

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?

Posted

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

  • 2 weeks later...
Posted
; =========================
; 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!

  • 3 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...