Книга: Practical Malware Analysis: The Hands-On Guide to Dissecting Malicious Software
Назад: Object-Oriented Programming
Дальше: Creating and Destroying Objects

. There, we have code that is going to sendData over the network, and we want it to be able to send data via TCP and UDP. One easy way to accomplish this is to create a parent class called Socket with a virtual function called sendData. Then we have two children classes called UDPSocket and TCPSocket, which override the sendData function to send the data over the appropriate protocol.

In the code that uses the socket, we create an object of type Socket, and create whichever socket we are using in this instance. Each time we call the sendData function, the sendData function will be called from the proper subclass of Socket, whether UDPSocket or TCPSocket, based on which type of Socket object was originally created.

The biggest advantage here is that if a new protocol—QDP, for example—is invented, you simply create a new QDPSocket class, and then change the line of code where the object is created. Then all calls to sendData will call the new QDPSocket version of sendData without the need to change all the calls individually.

In the case of nonvirtual functions, the function to be executed is determined at compile time. If the object is an instance of the parent class, the parent class’s function will be called, even if the object at runtime belongs to the child class. When a virtual function is called on an object of the child class, the child class’s version of the function may be called, if the object is typed as an instance of the parent class.

shows a code snippet that will execute differently if the function is virtual or nonvirtual.

, when this code is compiled, the object at is of class A. While the object at could be a subclass of class A, at compile time, we know that it is an object of class A, and the foo function for class A is called. This is why the code on the left will print “Class A.”

In the case of virtual functions, the determination of which function to call is made at runtime. If a class A object is called at runtime, then the class A version of the function is called. If the object is of class B, then the class B function is called. This is why the code on the right will print “Class B.”

This functionality is often referred to as polymorphism. The biggest advantage to polymorphism is that it allows objects that perform different functionality to share a common interface.

shows a disassembly of g function from the two code snippets in . On the left is the nonvirtual function call to foo, and on the right is the virtual call.

Non-virtual function call

Virtual function call

shows how the virtual function is used in to determine which code to call. The first 4 bytes of the object are a pointer to the vtable. The first 4-byte entry of the vtable is a pointer to the code for the first virtual function.

, we see the first vtable entry being accessed. To find the code that is called, we must find the vtable in memory and then go to the first function in the list.

Nonvirtual functions do not appear in a vtable because there is no need for them. The target for nonvirtual function calls is fixed at compile time.

shows the vtable for a class with three virtual functions. When you see a vtable, only the first value in the table should have a cross-reference. The other elements of the table are accessed by their offset from the beginning of the table, and there are no accesses directly to items within the table.

. A switch offset table would have offsets to locations that are not subroutines, labeled loc_###### instead of sub_######.

shows the cross-references for a virtual function. Both cross-references are offsets to the function, and neither is a call instruction. Virtual functions almost always appear this way, whereas nonvirtual functions are typically referenced via a call instruction.

, an expansion of , includes vtables for two classes.

. The two cross-references are from the two vtables that point to this function, which suggests an inheritance relationship.

Remember that child classes automatically include all functions from a parent class, unless they override it. In , sub_4010E0 at and is a function from the parent class that is also in the vtable for the child class, because it can also be called for the child class.

You can’t always differentiate a child class from a parent class, but if one vtable is larger than the other, it is the subclass. In this example, the vtable at offset 4020F0 is the parent class, and the vtable at offset 4020DC is the child class because its vtable is larger. (Remember that child classes always have the same functions as the parent class and may have additional functions.)

sss
sss

© RuTLib.com 2015-2018