定位结构的成员

逆向工程 部件 结构体 抵消 指针
2021-06-20 22:12:36

在流水线上,我接到了一个电话gethostbyname(...)我读过该函数的返回值可以是指向hostent结构的指针(当然,它也可以是空指针,但对于我的问题,我需要指向宿主结构的指针)

我还查看了以下主机结构:

   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;

 

因此,稍后会出现以下几行:

  mov edx, [eax+0Ch]

我知道函数的返回值存储在eax. 因此,当eax指向hostent结构时,则edx必须指向hostent结构之一members,因为偏移量为0Ch

要确定它是哪个成员,我知道我必须计算成员的大小。我的问题来了:

当我数数直到到达 offset 时0Ch,我必须选择这些类型中的哪一种?我的意思是,当您查看第一个成员时,例如:

char FAR *h_name; 

char相关的大小还是 的大小FAR或两者 ?

我希望有人能给我解释一下。

2个回答

FAR只是一个属性,而不是数据类型。甚至不是标准属性,而是 MS-DOS C 编译器引入的属性,用于区分 16 位和 32 位指针,并延续到 windows 16 位、windows 32 位和最近的 windows 64 位。

结构体中存储的是一个指向字符的指针(实际上是一个字符数组),它有4个字节(在32位程序的情况下),或8个字节(在64位程序的情况下) . 你不知道你有哪个,而是mov edx, [eax+0Ch]暗示 32 位,因为 64 位寄存器的名称类似于rdxrax所以,你的结构有这个布局(注意你还需要知道shorts 有 16 位):

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

请注意,这并不总是那么容易,因为出于对齐原因编译器可能会在结构成员之间引入填充字节如果您可以访问 C 编译器,最安全的做法是使用offsetof

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

示例演练包含在 msvc++exp 中编译并在 32 位机器中打包的几行代码

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;
}

使用条件断点在windbg中运行编译的exe,如下解释

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 也可用于确定单个成员的大小

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

收集大小,因此以原始格式转储它变得很容易在未知的二进制文件中编写脚本

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)

结果以原始格式转储,不支持未知二进制文件中的符号

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