Challenge RE #28

Let’s start with challenge #28. In this case we have a bigger code than our previous two challenges, but I think will be more understandable than those. The code as always can be found the original website. This challenge is an example of a useful code, I mean something you might use. I’ll start analyzing it by parts as always.

Analysis

f1

Here something to notice is that in this case we have a function f_main that will be our main thread, but let’s start analyzing function f1. Seems quite simple. We have the following(commented code by myself)

;; this function compares two 32-bit integers
;; returning:
;; -1 in case a < b
;; 0 in case a = b
;; 1 in case a > b
;; int f(const void *a, const void *b) NOTE this method it's passed as an the comparison function to qsort
f1:
	mov	eax, DWORD PTR [rsi]
	xor	edx, edx
	cmp	DWORD PTR [rdi], eax ;; compare first char with second string
	mov	eax, -1
	setg	dl ;; set byte in dl if str1[0] > str2[0]; this could be a ternary operation [edx]dl = str1[0] > str2[0] ? 1 : 0;
	cmovge	eax, edx ;; copy 0 to eax if greater of equal
	ret

As you can see from the comments I added, this method seems to compare two elements from rsi and rdi, which seems to be array of integers. The output number will be -1, 0 or 1 depending on the comparison result. Looking down in the code you can notice that this method, it’s passed to qsort as the comparison function. Then we can infer that must hold the signature int (*compar)(const void *, const void *).

code in C

int f1(const void *a, const void *b)
{
	if (*(int *)a < *(int *)b) return -1;

	return *(int *)a > *(int *)b ? 1 : 0;
}

my_memdup

The next method to analyze is my_memdup, here is the snippet for the code

my_memdup:
	push	rbp
	mov	rbp, rdi ;;  arr, 1st argument of my_memdup
	mov	rdi, rsi ;; 1st arg to malloc and 2nd from this method
	push	rbx
	mov	rbx, rsi ;; copy same number to rbx, to be used in the call to memcpy
	sub	rsp, 8
	call	malloc ;; the reserved memory is in rax

	add	rsp, 8
	mov	rdx, rbx ;; 3rd arg (size_t n) number of bytes to copy
	mov	rsi, rbp ;; 2nd arg (src)
	pop	rbx
	pop	rbp
	mov	rdi, rax ;; 1st arg (dst)
	jmp	memcpy

Looking at this code snippet we can infer a couple of things. We have a call to malloc followed by a memcpy. This code can be put in C code very shortly in this way

int *my_memdup(int *arr, size_t sz)
{
	int *rax = malloc(sz);
	return memcpy(rax, arr, sz);
}

Basically this method make a duplicate of memory in arr. I don’t see the use of this code in the main thread or any other function, so no idea why it’s here. Maybe the compiler added? They do weird stuffs.

f2

This method is the simpler of all of them, we receive an 32-bit integer as argument. The operations followed is a right shift to 31 bits, which will make it literally 0. I don’t understand why the compiler decided to to this…compilers are dark magicians. In this case I think all 5 operations are just a right shift to one position.

int f2(int a)
{
	return a >> 1;
}

f1_main

Now, the main thread. Here is the code for f_main

;;int f_main(int *arr, int nmemb)
f_main:
	push	r12
	mov	r12, rdi
	push	rbp
	lea	rbp, [0+rsi*4] ;; rsi contains nmemb
	push	rbx
	mov	rdi, rbp ;; amount of memory to reserve nmemb*4 (4 is the sizeof(int))
	mov	rbx, rsi ;; rbx will contain nmemb
	call	malloc

	mov	rdx, rbp ;; 3rd arg, size
	mov	rsi, r12 ;; 2nd arg, src (rdi)
	mov	rdi, rax ;; 1st arg, dst
	call	memcpy

	mov	ecx, OFFSET FLAT:f1 ;; 4th arg comparison function
	mov	edx, 4 ;; 3rd arg
	mov	rsi, rbx ;; 2nd arg (nmemb)
	mov	rdi, rax ;; 1st arg
	mov	rbp, rax ;; reserved memory
	call	qsort

	test	bl, 1 ;; checking for parity, nmemb % 2
	jne	.L12 ;; odd
	;; nmemb is even
	shr	rbx
	mov	eax, DWORD PTR [rbp+0+rbx*4]
	add	eax, DWORD PTR [rbp-4+rbx*4]

	pop	rbx
	pop	rbp
	pop	r12
	mov	edx, eax
	shr	edx, 31
	add	eax, edx
	sar	eax
	ret
.L12: ;; 
	shr	rbx ;; nmemb >> 1; division by 2
	mov	eax, DWORD PTR [rbp+0+rbx*4] ;; rbp contains the memory reserved, and array just sorted with qsort
	pop	rbx
	pop	rbp
	pop	r12
	ret

An equivalent code in C of this case would be

int f_main(int *arr, int nmemb)
{
	int *rax = malloc(nmemb*sizeof(int));

	memcpy(rax, arr, nmemb*sizeof(int));
	qsort(rax, nmemb, 4, f1);

	if (nmemb % 2 == 1)
		return rax[nmemb >> 1];


	return (rax[nmemb] + rax[nmemb-1]) >> 1;
}

Looking at this, it’s easy to infer that we are extracting the median of the array arr, the formal description for this code is

formal description

Computes the median in an array of integers.

Conclusion

Something that I didn’t understand here, is the declaration of f2 and my_memdup, they are not used in the code.

Extra documentation

From now on, I’ll include some extra documentation here of something you might be unaware.

  1. qsort
  2. Quicksort