Sunteți pe pagina 1din 36

Practical C++ reversing

Benjamin Vanheuverzwijn ESET

About me
Employee @ ESET botnet analysis Student in electrical engineering @ E.T.S. Member @ CISSP groupies bvanheu@gmail.com

Win32/Kelihos
scan your harddrive network dump send spam DDoS 2 mb binary packed custom UPX multiple libraries Boost winpcap protocol library (http,...) heavy thread usage P2P protocol

Objectives
Reverse Kelihos network stack 1. raw data handler 2. packet handler 3. message handler Learn how C++ works in assembly 1. object construction 2. call convention 3. object layout in memory Exploit weakness in the Kelihos protocol

The plan
Infect a machine Kelihos listens on port 80 Hook a debugger Reverse the packet handling stack: raw data handler packet handler header handler message handler

First step (1/2)


Static analysis is hard (without context) Finding an entry point (network) Put a breakpoint on WSARecv Send some data

First step (2/2)


The stack.

Followed by static analysis


References to WSARecv(): deep in boost.asio!

Object construction
"new(size_t s)" wraps "malloc(size_t s)" Call the constructor function (using "edi" as "memory ptr")

Compiler paradigm: constructor


call to new(size_t) malloc() memory call to "base_class->construct()" function that initialize the structure call to "member->construct()" when object has object members call to "object->construct()" written by the programmer returns "this" in eax

Raw data handler


recv_handler function:

Raw data handler


From dynamic (debugger) analysis:

Raw data handler


From WSARecv documentation: "If no error occurs and the receive operation has completed immediately, WSARecv returns zero."

Raw data handler


There is still some question: Using ecx without initialization? Calling a register initialized with ecx?

Compiler paradigm: call convention


Register used for "this" ptr (usually ecx) Parameters on the stack Static method: A::static_fct(int x)
push 42 - push parameter call static_fct - call the static function

Classic method: a->fct(int y)


push 42 - push parameter (int z) mov ecx, esi - put "this" in ecx call fct - call the function

Virtual method: a->v_fct(int z)


push 42 - push parameter (int z) mov eax, [esi] - dereference vftable mov ecx, esi - put "this" in ecx call [eax] - call the function (vftable)

Raw data handler


We come from classic method: this->fct(int z) ecx is used without initialization ebx is used to reference "this"

Raw data handler


But... we still have a question: calling "[this+20C8]+0x4"?

Compiler paradigm: instance layout


Virtual function table (vft) Members

Raw data handler


What we know from the instance layout of the object: 1 vftable = 1 parent class vftable are hard to follow in static analysis

Packet handler

Packet handler

Packet handler

Packet handler
Call to string::append(char *buffer, size_t length) Now we know "member_2" is a String

Packet handler

Packet handler

Packet handler
Call to string::append()
1. push parameters 2. put the string pointer in "ecx" 3. call the function

Call to string::operator[]()
1. push parameter 2. put the string pointer in "ecx" 3. call the function 4. eax will now point on the position

We learn that for a valid packet:


must not begin with 0x0500 must have a minimum length of 0x08

Static analysis: header_handler


We then pass trough a lot of not efficient code: 1. validate packet headers 2. unpack packet headers 3. strip headers 4. call the message_handler

Header validation

Header unpacking

Static analysis: header_unpack


1. head1, head2, a and b = random integers 2. crypted_size = c - head1 - a 3. msg_type = d - crypted_size - 2* (head1 - a)

Message handler

Message handler
msg of type "Bootstrap"

More about kelihos P2P protocol


Payload is: Encrypted Compressed Serialized Bootstrap message contains: Peerlist Peer information (ip, port, etc) The bug: port is serialized with 4 bytes cast in 2 bytes needed

Bootstrap message
Label: m_live_time Type: Value Scalar value:73 size:8 ... Label: m_listenning_port Type: Value Scalar value:80 size:4 Label: m_real_target_ip Type: Value Scalar value:62.4.47.49 size:4 Label: m_bootstrap_list Type: Section ...

Inject peers
Peer with high live_time is kept first in the list Peers are identified with ip AND port Inject same ip but with different port (80 dec. = 0x50) 0xAAAA0050 & 0x0000FFFF = 0x50 0xBBBB0050 & 0x0000FFFF = 0x50 Injected peer list: m_ip = 64.12.41.51 m_live_time = 9999999 m_listening_port = 0xAAAA0050 ... m_ip = 64.12.41.51 m_live_time = 9999999 m_listening_port = 0xBBBB0050

Question? / Answer!

S-ar putea să vă placă și