Jump to content

Eduardo Bittencourt

Membros
  • Posts

    35
  • Joined

  • Last visited

Posts posted by Eduardo Bittencourt

  1. Faz sentido sim, afinal todo número sem sinal é positivo

    Só uma obs: Quando você faz 3 * 2 não seria a repetição de 3 duas vezes ?

    E se fosse 2 * 3 não seria a repetição de 2  três vezes?

    De qualquer forma, seria a  subtração a soma de um numero natural com a soma de um número inteiro negativo,  aplicando neste contexto é claro. Afinal todo número natural é subconjunto dos números inteiros positivos.

     

     

  2. É normal isso, kkkk. Fui perceber depois, que você estava entendo inteiro como int, o que é normal, por isso não estávamos nos entendendo. Abraços amigo! :)

  3. 14 horas atrás, thiago disse:

    Acho que a gente não tá conseguindo se entender, cara. :/

    Por exemplo, a sua definição de que o tamanho do endereço vai ter o tamanho de um inteiro é inválida no caso do x86_64. Como o @Fernando Mercês disse, o tamanho de um endereço numa arquitetura de 64 bits é 64 bits. Compila e testa isso (no x86_64):

    
    #include <stdio.h>
    
    int main()
    {
        printf("%zu %zu\n", sizeof(void *), sizeof(int));
        return 0;
    }

    Você vai ver que sizeof(void *) é 8 e sizeof(int) é 4.

    Acho que você está confundindo um pouco as coisas. O que você está dizendo sobre o endereço do dado ter o tamanho do tipo apontado não é verdade. Numa arquitetura de 64 bits, se um ponteiro aponta pra um char, o tamanho do ponteiro é 64 bits; se ele aponta pra um short, int, long, float, double, o tamanho do ponteiro continua sendo 64 bits.  A melhor maneira de ver isso é testando no próprio código.  

    Acho que na verdade está havendo uma falha de comunicação entre nós. Quando digo inteiro, não quero dizer que o ponteiro tem o tamanho do tipo int, mas sim que o tamanho não é um ponto flutuante, entendeu? Eu sei que em arquiteturas diferentes, tem tamanho distintos.

    Um ponteiro em arquitetura x86_x64 tem tamanho de um inteiro com modificador de tamanho long long 8 bytes, já em ambientes i386 o ponteiro geralmente tem tamanho de 4 bytes, ambos são inteiros, concorda comigo?

     

    Ao utilizar sizeof(*ptr); 

    Está retornando o tamanho do conteúdo para onde o ponteiro está apontando ptr, abraços.

  4. 5 horas atrás, thiago disse:

    Não entendi qual foi sua dúvida sobre a palavra. A dúvida é sobre o conceito de palavra?

     Eu rodei seu código aqui na minha máquina, modifiquei e aproveitei pra montar este esquema pra simplificar. O tamanho do ponteiro (tamanho do endereço) é o mesmo pra qualquer que seja o tipo (char, short, int, long, etc.). Você simplesmente obteve o tamanho de *teste, que é o mesmo tamanho de teste[0], que é o mesmo tamanho de um char, que possui o tamanho de 1 byte. Se ligue que são duas coisas diferentes: tamanho do endereço é uma coisa e tamanho do conteúdo apontado por aquele endereço é outra. :)

    Abraços!

     

    É isso que estou dizendo, o endereço de memória de um ponteiro sempre vai ter o tamanho de um inteiro. Mas quando você aponta para outro dado, o endereço do dado vai ter o tamanho do tipo apontado, abraços :)

  5. 7 horas atrás, thiago disse:

    E aí, cara? Gostei muito do que escreveu. Parabéns pela iniciativa de passar um assunto bem chato de maneira divertida. Se me permite, gostaria de te dar uns toques sobre o que você escreveu.

    Cuidado com a diferença sutil entre endereço de memória e conteúdo da memória naquele endereço. O ponteiro não tem o tamanho de um int; na verdade, o ponteiro tem o tamanho de uma palavra (normalmente está relacionada ao tamanho do registrador). Na arquitetura x86, o tamanho da palavra é 32 bits  e o tamanho de um int também, porém isso não é verdade para a arquitetura x86_64. O tamanho da palavra é 64 bits e o tamanho de um int é 32 bits. :)

    Cuidado também com esse lance de "ao apontar para um dado, o tamanho do ponteiro terá o mesmo tamanho do dado". O tamanho de um ponteiro é o mesmo, esteja ele apontando para um char, short, int, long, etc. Em todos os casos, o ponteiro terá o tamanho da palavra.

    Creio eu, que um endereço de memória de um ponteiro tem tamanho de um inteiro, no qual o tamanho pode ser variado. Mas não entendi, o que você quis dizer com tamanho de uma palavra, como assim palavra? E a respeito sobre apontar para char, short, int, long, etc, o tamanho pode variar sim, quando você aponta para um char, você estará reservando apenas 1 byte para aquele endereço de memória. Exemplo:

    char *teste = "array";

    printf("%d", sizeof(*teste)); // Tamanho de 1 byte

     

    Ah respeito do código em assembly, eu entendi o que você quis dizer, mt obrigado pelo feedback :D

  6. Bom,  resolvi postar essa dica simples, acho que a maioria aqui conheça, porém deve ter um ou dois que não conheça, enfim. Para gerar código em asm através do gcc usamos a seguinte linha de comando

    Para 64 bits

    $ gcc -S masm=intel programa.c

    Caso queira em 32 bits

    $ gcc -S masm=intel -m32 programa.c

     

    A linhas masm=intel, significa, usar o masm com syntax intel. Um ponto interessante é que com esta técnica, você pode ver as seções de memória do código, e muito outras coisas, claro que são coisas simples. Se realmente prefere desassemblar é recomendável usar o objdump, ou gdb, entre outros

     

    #include <stdio.h>
    
    long x = 5;
    
    int main(void)
    {
         char *cons = "teste";
    
         return 0;
    
    }

     

    Compilei em 64 bits e em 32 bits, e teve diferenças no código, repare;

    Output em 64 bits

    $cat teste.s

     

    	.file	"teste.c"
    	.intel_syntax noprefix
    	.globl	x;                                 ;variável x é global
    	.data
    	.align 8
    	.type	x, @object
    	.size	x, 8;                              ;tamanho de 8 byte
    x:
    	.quad	5
    	.section	.rodata 
    .LC0:
    	.string	"teste"                      ;essa string é da seção .rodata
    	.text
    	.globl	main                          ;função main é global,  oh não diga
    	.type	main, @function
    main:
    .LFB0:
    	.cfi_startproc
    	push	rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	mov	rbp, rsp
    	.cfi_def_cfa_register 6
    	lea	rax, .LC0[rip]
    	mov	QWORD PTR -8[rbp], rax
    	mov	eax, 0
    	pop	rbp
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE0:
    	.size	main, .-main
    	.ident	"GCC: (Debian 6.3.0-18) 6.3.0 20170516"
    	.section	.note.GNU-stack,"",@progbits

     

    Output em 32 bits

    $cat teste.s

     

    	.file	"teste.c"
    	.intel_syntax noprefix
    	.globl	x                                  ; variável global
    	.data
    	.align 4
    	.type	x, @object
    	.size	x, 4                             ; tamanho de 4 byte
    x:
    	.long	5
    	.section	.rodata
    .LC0:
    	.string	"teste"
    	.text
    	.globl	main
    	.type	main, @function
    main:
    .LFB0:
    	.cfi_startproc
    	push	ebp
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	mov	ebp, esp
    	.cfi_def_cfa_register 5
    	sub	esp, 16
    	call	__x86.get_pc_thunk.ax
    	add	eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
    	lea	eax, .LC0@GOTOFF[eax]
    	mov	DWORD PTR -4[ebp], eax
    	mov	eax, 0
    	leave
    	.cfi_restore 5
    	.cfi_def_cfa 4, 4
    	ret
    	.cfi_endproc
    .LFE0:
    	.size	main, .-main
    	.section	.text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
    	.globl	__x86.get_pc_thunk.ax
    	.hidden	__x86.get_pc_thunk.ax
    	.type	__x86.get_pc_thunk.ax, @function
    __x86.get_pc_thunk.ax:
    .LFB1:
    	.cfi_startproc
    	mov	eax, DWORD PTR [esp]
    	ret
    	.cfi_endproc
    .LFE1:
    	.ident	"GCC: (Debian 6.3.0-18) 6.3.0 20170516"
    	.section	.note.GNU-stack,"",@progbits

     

    • Curtir 1
  7. Programa simples que fiz em assembly, só faz ler o stdin mesmo, kkkk. Obs: Na label write,  era pra eu ter feito uma rotina que usasse como argumento uma string, assim não precisava repetir todo o código da label write na label writex, mas fiz desse jeito mesmo pra não usar a stack

     

    ;Author: WallasC0der
    ;
    ;montagem
    ;nasm -f elf32 stdin.asm
    ;ld -m elf_i386 stdin.o -o stdin
    
    
    section .bss
    
    buffer RESB 256 ; reserva 256 bytes
    size equ $ - buffer ; tamanho do buffer
    
    section .data
    
    msg db "Digite no stdin", 0ah
    len equ $ - msg	
    
    section .text
    
    global _start:
    
    ;protótipo do write
    ;ssize_t write(int fd, const void *buf, size_t count);
    
    _start:
    
    write:
    
    mov eax, 4 ; syscall do write
    mov ebx, 2 ; stdout
    mov ecx, msg; void *buf
    mov edx, len; size_t
    int 0x80 ; executa a syscall
    
    
    ;protótipo do read
    ;ssize_t read(int fd, void *buf, size_t count);
    
    read:
    
    mov eax, 3 ; syscall do read
    mov ebx, 1 ; stdin
    mov ecx, buffer ; void *buffer
    mov edx, size ; size_t
    int 0x80; executa a syscall
    
    mov esi, ecx
    
    writex:
    
    mov eax, 4
    mov ebx, 2
    mov ecx, esi
    mov edx, size
    int 0x80
    
    exit:
    
    mov eax, 1 ; syscall do exit
    int 0x80   ; executa a syscall

     

    • Curtir 1
  8. 1 hora atrás, Fernando Mercês disse:

    Que legal, @Eduardo Bittencourt! O fórum é pra isso mesmo! Acho fantástico quando alguém perde o medo e posta! haha

    Olha, eu não sou especialista no assunto mas, a meu ver, tem coisas que podem ser elevadas de nível, exatamente como o Frederico fez com as minhas aulas no canal. Mas são detalhes, tipo quando você fala que o ponteiro aponta pro endereço de uma variável, mas na verdade pode apontar pra qualquer lugar. Claro, normalmente tem um dado lá. rsrs Mas enfim, como falei, são pequenos detalhes que a revisão do Frederico me ajudou a ficar mais esperto sobre! Como você pediu feedback, vou listar aqui:

    • Ponteiros são variáveis que contém um endereço de memória.
    • É melhor usar o %p ao imprimir endereços de memória, porque ele já adapta pra arquitetura (%#x ou %#lx). Olha a confusão (em 64-bits):
    
    $ echo 'main(){ int x; printf("%x\n%p\n", &x, &x); }' | tcc -run -
    4fce29ac
    0x7fff4fce29ac
    • Pro size_t, usa o %zu por conta do tamanho (o Frederico fala sobre isso nos comentários no canal)
    • memset() é pra escrever em memória.
    • No código do item 6, aquele free() não vai rolar porque você incrementou o endereço do ponteiro. Uma saída pra isso seria usando um ponteiro auxiliar, pra guardar o primeiro endereço antes de incrementar (já que você usou isso pra iterar por ele):
    
    int main(void) {
    	char *ptr = malloc(11);  // Aloca 11 bytes, 10 bytes para a string, e 1 byte a mais para o nullbyte
    
    	strcpy(ptr, "aaaaaaaaab");
    	char *aux = ptr;
    
    	while (*ptr != 'b') { // percorre o loop até achar a letra b
    		memset(ptr, 'x', sizeof(char)); // escreve o byte 'x' no endereço de memória apontado por ptr (a escrita rola sizeof(char) vezes - no caso aqui é 1)
    		ptr++; // incrementa sizeof(char) bytes no endereço para o qual o ponteiro aponta (seu valor)
    	}
    
    	puts(aux);
    	free(aux);
    
    	return 0;
    }

    Ou fazer o loop com for. Mas no caso dessa função (colocar 'x' até encontrar 'b'), como é um caractere só, não precisa da memset():

    
    while (*ptr != 'b') {
    	*ptr = 'x';  // coloca o 'x' no endereço de memória apontado por ptr
    	ptr++;       // incrementa ptr, então ele vai agora apontar para a próxima letra
    }

    Dá pra fazer tudo numa linha só também, graças à magia negra precedência de operadores:

    
    while (*ptr != 'b')
    	*ptr++ = 'x';

    Mas de novo, tá muito bom o texto. O assunto parece complicado e assusta a galera, mas você tratou de boa. Isso é muito maneiro!

    Grande abraço!

    Vlw pelo feedback, ah propósito da função que uso while, não me toquei que aquela forma estava errada, fiz o teste com o valgrind e deu erro invalid free, enfim. Ah respeito de não usar o %p, na verdade estava até ciente, porém não coloquei porque podia confundir pra quem estava tendo o primeiro contato,  mas pensando bem compensava muito ter colocado,era a melhor forma. Agradeço novamente pelo feedback e pelas suas correções, foram de grande ajuda, abraços :) 

    • Curtir 1
  9. 2 minutos atrás, hanczjeeel disse:

    Entendi, obrigado.
    fiz uns testes aqui, por exemplo ali, pode-se chamar a função func por:
    int soma1 = func(1,3);
    ou
    int soma2 = ptr(1,4);
    bem louco.

    Isso ae, kkkk

  10. 2 horas atrás, hanczjeeel disse:

    Muito boa sua iniciativa, parabéns!
    fiquei com uma dúvida no tópico 8 na seguinte linha: int(*ptr)(int x, int y);

    qual seria a lógica desta linha?
     

    É um ponteiro para função, int quer dizer que o ponteiro vai retornar um inteiro, *ptr estou declarando o ponteiro, (int x. int y), informo que a função tem que passar dois parâmetros inteiros, entendeu?

  11. Interessante,  ao executar o programa com  buffer overflow já retorna a seguinte mensagem

    *** stack smashing detected ***

     

    Já para o efeito contrário, no qual se quer realizar um ataque buffer overflow para testes, pode ter que desativar algumas proteções

     

    -fno-stack-protector para desativar o canário
    
    echo 0 > /proc/sys/kernel/randomize_va_space para desativar o aslr

     

×
×
  • Create New...