2

In an assembly line, I have a call to gethostbyname(...). I have read that the return value of that function can be a pointer to a hostent structure (of course, it could be also a null pointer but for my question I need the pointer to the hostent structure)

I also take a look to the hostent structure which is the following:

   typedef struct hostent{
    char FAR *h_name;
    char FAR FAR **h_aliases;
    short h_addrtype;
    short h_length;
    char FAR FAR **h_addr_list;
   } HOSTENT, *PHOSTENT, FAR *LPHOSTENT;

So, some lines later the following appears:

  mov edx, [eax+0Ch]

I know that the return value of a function is stored in eax. So, when eax points to hostent structure, then edx must point to one of the hostent structure members, because the offset is 0Ch.

To determine which of the member it is, i know that I must count the sizes of the members. And here comes my question:

When I count until I reach the offset 0Ch, which of these types I must take? I mean, when you look at the first member, for example:

char FAR *h_name; 

Is the size of char relevant or the size of FAR ? Or both ?

I hope someone can explain me that.

2

FAR is just an attribute, not a data type. Not even a standard attribute, but an attribute that was introduced with MS-DOS C compilers, to distinguish between 16-bit and 32-bit pointers, and carried over to windows 16 bit, windows 32 bit and, recently, windows 64 bit.

What is stored in the structure is a pointer to a character (in fact, an array of characters), which has 4 bytes (in the case of a 32 bit program), or 8 bytes (in the case of a 64 bit program). You don't tell which you have, but mov edx, [eax+0Ch] implies 32 bit, as 64 bit registers have names like rdx and rax. So, your structure has this layout (note you also need to know shorts have 16 bit):

offset   bytes bits member
00-03    4     32   h_name
04-07    4     32   h_aliases
08-09    2     16   h_addrtype
0a-0b    2     16   h_length
0c-0f    4     32   h_addr_list

Note that it's not always that easy, because compilers may introduce padding bytes between struct members for alignment reasons. If you have access to a C compiler, the safest thing is to use offsetof:

printf("%02x\n", offsetof(struct hostent, h_addr_list));
1

A sample walkthrough containing few lines of code compiled in msvc++exp and windbagged in 32 bit machine

int _tmain(int argc, _TCHAR* argv[]) {
    WSADATA wsaData;    
    in_addr addr;
    hostent *myhost;
        if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) == 0)
        if ( ( myhost = gethostbyname("www.google.com") ) != NULL) {
            printf("host name = %s\n",myhost->h_name);
            if (myhost->h_addrtype == AF_INET) {
                addr.s_addr = *(u_long *) myhost->h_addr_list[0];
                printf("IPv4 Addr = %s\n",inet_ntoa(addr));
            }
        }    
        return 0;
}

run the compiled exe in windbg with a conditional break point as below explanation being

break when ws2_32!gethostbyname is called
gu is to go up back to caller (eax will hold * to hostent structure)
display the structure pointed by eax with a c++ expression evaluator  ?? and quit

.

:\>cdb -c "bp ws2_32!gethostbyname \"gu ; r eax; ?? (hostent *) @eax;q\";g" ghostbyname.exe    
0:000> cdb: Reading initial command 'bp ws2_32!gethostbyname "gu ; r eax; ?? (ho
stent *) @eax;q";g'
eax=0016c3b8
struct hostent * 0x0016c3b8
   +0x000 h_name           : 0x0016c3f8  "www.google.com"
   +0x004 h_aliases        : 0x0016c3c8  -> (null)
   +0x008 h_addrtype       : 0n2
   +0x00a h_length         : 0n4
   +0x00c h_addr_list      : 0x0016c3cc  -> 0x0016c3e4  "J}???"
quit:

:\>

windbg can also be used to ascertain size of individual members

0:000> ?? sizeof(((hostent *) @eax)->h_name)
unsigned int 4
0:000> ?? sizeof(((hostent *) @eax)->h_aliases)
unsigned int 4
0:000> ?? sizeof(((hostent *) @eax)->h_addrtype)
unsigned int 2
0:000> ?? sizeof(((hostent *) @eax)->h_length)
unsigned int 2
0:000> ?? sizeof(((hostent *) @eax)->h_addr_list)
unsigned int 4
0:000> ?? sizeof(hostent)
unsigned int 0x10

with sizes gleaned thus dumping it in raw format becomes easy to script in an unknown binary

r $t0 = ${$arg1}
.printf /D "<b>eax          \t%p </b>\n",@$t0
.printf /D "<b>hostent *    \t%p </b>\n",poi(@$t0)
.printf /D "<b>->h_name     \t%ma</b>\n",poi(@$t0)
.printf /D "<b>->h_alias    \t%p </b>\n",poi(poi(@$t0+0x4))
.printf /D "<b>->h_addrtype \t%p </b>\n",low(poi(@$t0+0x8))
.printf /D "<b>->h_length   \t%p </b>\n",hi(poi(@$t0+0x8))
.printf /D "<b>->h_addrlist \t%p </b>\n",poi(poi(@$t0+0xc))
.printf /D "<b>->h_addrlist contains ip address in network byte order</b>\n"
db poi(poi(@$t0+0xc)) l10
.printf /D "<b>Ipv4 Address of google.com %d.%d.%d.%d\n" , 

by(poi(poi(@$t0+0xc))),by(poi(poi(@$t0+0xc))+1),by(poi(poi(@$t0+0xc))+2),by(poi(poi(@$t0+0xc))+3)

result dumped in raw format without symbol support in an unknown binary

0:000> $$>a< ghostbyname.txt @eax

eax             0016c3b8 
hostent *       0016c3f8 
->h_name        www.google.com
->h_alias       00000000 
->h_addrtype    00000002 
->h_length      00000004 
->h_addrlist    0016c3e4 
->h_addrlist contains ip address in network byte order
0016c3e4  4a 7d ec d0 4a 7d ec d4-4a 7d ec d3 4a 7d ec d1  J}..J}..J}..J}..
Ipv4 Address of google.com 74.125.236.208

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.