-1

I am reverse engineering some assembly for a server binary, and am having some trouble understanding some code around a recv call.

I derived the following pseudo-code from the assembly, and the while loop condition confuses me:

     func_804a890(int socket, void *buffer_start, size_t buffer_len, int num_open_descriptors) {
  int a, b, c;

  if (buffer_start == 0 || buffer_len == 0)
    //goto 0x804a8e8

  size_t msg_len = 0;
  void* buffer;
  ssize_t packet_len = 0;

  do {
    buffer_len -= packet_len;
    buffer = buffer_start + msg_len;
    packet_len = recv(socket, buffer, buffer_len, 0);
    if (packet_len == -1) {
      return -1;
    }
    else if (packet_len == 0) {
      //goto 0x804a8f8
    }
    else {
      //goto 0x804a8bc
    }

    msg_len += packet_len;

  } while(buffer_len > msg_len);
}

Why would I loop if the buffer is larger than the message from the client? Next is the relevant assembly function in case I am not interpreting one of the variables correctly in my pseudo-code:

.text:0804a890 55                               push   ebp
.text:0804a891 57                               push   edi
.text:0804a892 56                               push   esi
.text:0804a893 53                               push   ebx
.text:0804a894 83 ec 0c                         sub    esp,0xc
.text:0804a897 8b 74 24 24                      mov    esi,DWORD PTR [esp+0x24]
.text:0804a89b 8b 7c 24 20                      mov    edi,DWORD PTR [esp+0x20]
.text:0804a89f 8b 5c 24 28                      mov    ebx,DWORD PTR [esp+0x28]
.text:0804a8a3 85 f6                            test   esi,esi
.text:0804a8a5 74 41                            je     0x0804a8e8
.text:0804a8a7 85 db                            test   ebx,ebx
.text:0804a8a9 74 3d                            je     0x0804a8e8
.text:0804a8ab 31 c0                            xor    eax,eax
.text:0804a8ad 31 ed                            xor    ebp,ebp
.text:0804a8af eb 13                            jmp    0x0804a8c4
.text:0804a8b1 8d b4 26 00 00 00 00             lea    esi,[esi+eiz*1+0x0]
.text:0804a8b8 85 c0                            test   eax,eax
.text:0804a8ba 74 3c                            je     0x0804a8f8
.text:0804a8bc 01 c5                            add    ebp,eax
.text:0804a8be 39 eb                            cmp    ebx,ebp
.text:0804a8c0 89 e8                            mov    eax,ebp
.text:0804a8c2 76 24                            jbe    0x0804a8e8
.text:0804a8c4 89 da                            mov    edx,ebx
.text:0804a8c6 6a 00                            push   0x0
.text:0804a8c8 29 c2                            sub    edx,eax
.text:0804a8ca 01 f0                            add    eax,esi
.text:0804a8cc 52                               push   edx
.text:0804a8cd 50                               push   eax
.text:0804a8ce 57                               push   edi
.text:0804a8cf e8 3c ec ff ff                   call   0x08049510
.text:0804a8d4 83 c4 10                         add    esp,0x10
.text:0804a8d7 83 f8 ff                         cmp    eax,0xffffffff
.text:0804a8da 75 dc                            jne    0x0804a8b8
.text:0804a8dc 83 c4 0c                         add    esp,0xc
.text:0804a8df 5b                               pop    ebx
.text:0804a8e0 5e                               pop    esi
.text:0804a8e1 5f                               pop    edi
.text:0804a8e2 5d                               pop    ebp
.text:0804a8e3 c3                               ret    
justin
  • 53
  • 8
  • Try to run derevenets.com's snowman decompiler on your function to get corrected pseudocode. You may also copy full disassembly as plain text in your question, not as partial listing inside jpg. Now I can assume that your pseudocode is incorrect, as there is `call ...; esp+=16; cmp eax and -1; if eax != -1 ([there was no error in recv](http://man7.org/linux/man-pages/man2/recv.2.html#RETURN_VALUE)) jmp to a8b8. So you probably have wrong argument of while loop; there should be some additional logic and control flow inside the loop. – osgx Jan 22 '17 at 05:47
  • 3
    Post text as text, not pictures of text. – melpomene Jan 22 '17 at 05:53
  • Edited the post and posted the function as text. I can't post the entire file as it is very very long. – justin Jan 22 '17 at 06:00
  • @osgx snowman decompiler only works with IDA for non-windows os's, correct? Unfortunately, I couldn't get IDA working on my linux computer. Do you know of any alternatives? Also, I forgot to paste a few lines into my pseudocode, so it now includes the additional logic. – justin Jan 22 '17 at 06:04
  • not correct. It is free, it has standalone versions (no IDA required); and it can be compiled on linux (or try with wine). Post not full file disassembly, but function in question. And your while condition `while(buffer_len > msg_len);` still incorrect (don't try to optimize pseudo-code early, decode your assembler exactly). PS where is je "0x0804a8f8"? First ret may be not end of the function, and your code is may be c++. – osgx Jan 22 '17 at 06:09

1 Answers1

2

You appear to have applied the name buffer_len to two different values in the program. There's the buffer_len that passed in an argument and a second buffer_len that's a parameter to recv. These are actually two different things. The first is stored in EBX and never changes. The second is temporarily stored in EDX before the call to recv.

A more literal translation of the function would be:

int
func_804a890(int arg1, int arg2, int arg3) {
    int esi = arg2;
    int edi = arg1;
    unsigned ebx = arg3;

    if (esi != 0 && ebx != 0) {
        int eax = 0;
        unsigned ebp = 0;
        while(1) {
            int edx = ebx;
            edx -= eax;
            eax += esi;
            eax = recv(edi, (void *) eax, edx, 0);
            if (eax == -1) {
                return eax;
            }
            if (eax == 0) {
                /* ??? */
            }
            ebp += eax;
            eax = ebp;
            if (ebx <= ebp) {
                break;
            }
        }
    }
    /* ??? */
}

Simplified this becomes:

int
func_804a890(int socket, char *buffer, unsigned len) {
    if (buffer != 0 && len != 0) {
        unsigned pos = 0;
        while(1) {
            int recv_len = recv(socket, buffer + pos, len - pos, 0);
            if (recv_len == -1) {
                return recv_len;
            }
            if (recv_len == 0) {
                /* ??? */
            }
            pos += recv_len;
            if (len <= pos) {
                break;
            }
        }
    }
    /* ??? */
}

The loop continues while total amount received (pos) is less than the length of the buffer passed to the function (len).

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • Thanks for the helpful answer. Why would the loop be written with this condition? If the full length of the buffer isn't filled, why would recv return more content to fill the whole length? – justin Jan 22 '17 at 07:55
  • 1
    @ideanl Because more data can be sent later. If this a TCP stream being read then `recv` doesn't read "packets", it just reads bytes from the stream. The code may be either waiting until one complete application level packet has been received, or it may just be filling an arbitrary sized buffer so it can process the data in chunks. – Ross Ridge Jan 22 '17 at 09:01