Yesterday I was sorting out an “obscene” trojan (http://vilgeforce.habrahabr.ru/blog/44130.html), and today I am splitting its descendant - ftp34.dll. This tvarinka, by the way, is much more interesting than the vast majority of Trojans. At least that steals information not from a disk, and directly from network traffic. How? Look under the cat.
In the first series, one of the components of the Troyan Zalupko complex was almost completely disassembled. He drove the ftp34.dll file to disk and loaded it with LoadLibrary. And this happened with each activation of the trojan.
ftp34.dll is a file of 4608 bytes in size, packed using UPX, therefore it is unpacked in Linux (at home in Linux) in 5 seconds. The unzipped file weighs 7608 bytes. DLL, like its dropper, uses the same principle of string encryption - XOR in one byte, the function code is the same. Script for IDA is written in a minute.
IDA carefully folded the entry point code by moving the cursor to DllMain. In DllMain, the cause of the call is checked: if a library is being loaded, some actions are performed, if the unloading is different. Since there were fewer calls for unloading, I started with this piece. In detach (let's call it this way), the strange code is:
')
.text: 10001D2F push eax; lpNumberOfBytesWritten
.text: 10001D30 push 6; nSize
.text: 10001D32 push offset unk_1001213C; lpBuffer
.text: 10001D37 push lpBaseAddress; lpBaseAddress
.text: 10001D3D push 0FFFFFFFFh; hProcess
.text: 10001D3F call ds: WriteProcessMemory
and it repeats 4 times. If I do not confuse anything, then hProcess equal to -1 means writing to my own address space. A quick analysis of the references to the buffer addresses for reading and writing showed that the DLL reads / writes quite actively in them using Read / WriteProcessMemory. The open question "Why?" I will leave for later.
Boot ActionsLike its parent, this library prepares lines containing the paths to the required files:% TEMP% \ r43q34.tmp and% TEMP% \ mpz.tmp. The presence of other library instances in the system is determined with the help of a mutex, and if it is not present, Thread1 is created. I don’t cite the names of mutexes, because I doubt that someone will check the system for their presence :-) Now - the most interesting! Library PATCHIT functions Windows Sockets in memory. How does this happen? Here is the code:
.text: 10001B72 push 4; int
.text: 10001B74 push offset aSw676Hh; "Ws2_32.dll"
.text: 10001B79 call decryptXor
.text: 10001B7E pop ecx
.text: 10001B7F pop ecx
.text: 10001B80 push eax; lpModuleName
.text: 10001B81 call ds: GetModuleHandleA; We get the hand ws2_32.dll
.text: 10001B87 mov [ebp + ws2_32handle], eax
.text: 10001B8A push 5; int
.text: 10001B8C push offset aWFs; "Recv"
.text: 10001B91 call decryptXor
.text: 10001B96 pop ecx
.text: 10001B97 pop ecx
.text: 10001B98 push eax; lpProcName
.text: 10001B99 push [ebp + ws2_32handle]; hModule
.text: 10001B9C call ds: GetProcAddress; Get the address of the recv function
.text: 10001BA2 mov recvAddr, eax
.text: 10001BA7 lea eax, [ebp + NumberOfBytesWritten]
.text: 10001BAA push eax; lpNumberOfBytesRead
.text: 10001BAB push 6; nSize
.text: 10001BAD push offset originalCode; lpBuffer
.text: 10001BB2 push recvAddr; lpBaseAddress
.text: 10001BB8 push 0FFFFFFFFh; hProcess
.text: 10001BBA call ds: ReadProcessMemory; We read the buffer in the originalCode first 6 bytes of the function recv
.text: 10001BC0 mov HookCode, 68h; In the buffer written to the beginning of recv () we place the opcode of the push command.
.text: 10001BC7 mov dword ptr HookCode + 1, offset newRecv; Following the push is the address of our new handler.
.text: 10001BD1 mov HookCode + 5, 0C3h; And now RET
.text: 10001BD8 lea eax, [ebp + NumberOfBytesWritten]
.text: 10001BDB push eax; lpNumberOfBytesWritten
.text: 10001BDC push 6; nSize
.text: 10001BDE push offset HookCode; lpBuffer
.text: 10001BE3 push recvAddr; lpBaseAddress
.text: 10001BE9 push 0FFFFFFFFh; hProcess
.text: 10001BEB call ds: WriteProcessMemory; We write our insert at the beginning of recv (). It is done!
Alas, it turned out to be much less readable than in IDA :-( In short: we got the address of the desired function. We counted 6 bytes from this address, we prepared a buffer with code
push offset myRecv
ret
and recorded it at the beginning of the intercepted procedure. The combination of push-ret - the transition to the address we need is not quite obvious way. The following functions are intercepted: recv (), WSARecv (), WSASend (), send (). Now it became clear that this is written to memory when a DLL is unloaded: it restores the original code of the functions being intercepted. The main question remains - how is control transferred to the original functions? And what about Thread1? It installs its handler (which, incidentally, does not give anything criminal) using SetWindowsHookEx. What for? I don’t know for sure ... But yes, it’s not important, I think.
Interceptor FunctionsAll interceptors have a lot in common: these are short procedures containing, roughly speaking, only 2 calls. The first call is the same for send () and WSASend () interceptors, and the second is for recv () and WSARecv (), that is, separation by functionality. I will name these two functions HookSend and HookRecv respectively. The second call in interceptors is different, it is a call to a function that patches the intercepted functions to the initial state, calls them, and then patches into the interceptor version.
The HookSend () and HookRecv () functions receive three parameters — socket, buffer, and length. The initial code also coincides: we get the address to which the socket is connected, we convert this address to a string, and also translates the address from the network byte order to the host one. Here there is a moment that is not completely clear to me:
.text: 100015CA push [ebp + s]; s
.text: 100015CD call ds: getpeername
.text: 100015D3 push dword ptr [ebp + name.sa_data + 2]; in
.text: 100015D6 call ds: inet_ntoa
.text: 100015DC push eax; Source
.text: 100015DD push offset byte_10011C10; Dest
.text: 100015E2 call strcpy
.text: 100015E7 pop ecx
.text: 100015E8 pop ecx
.text: 100015E9 push dword ptr [ebp-12h]; netshort
.text: 100015EC call ds: ntohs
.text: 100015F2 movzx eax, ax
.text: 100015F5 cmp eax, 25
.text: 100015F8 jnz short loc_10001607
s - socket. I do not understand how we get the port after calling ntohs in ax? Or is there really going to be a port and I read badly the docks? In general, intuition and familiar numbers (25, 80, 110 :-) suggested that the port to which the connection was made is being checked. For reception, traffic is intercepted on the following ports: 25, 80, 110. For transmission: 25, 80, 21. And traffic of the 21st port is handled somehow cleverly. Transmission on the 80th port seems to make some changes in traffic: if the line “gzip,” appears in the transmitted data, it will be replaced with 5 bytes with the code 0x6E (“n”). What for? I don’t know ... At this moment, only the search procedures in the transmitted password data for FTP and e-mail addresses, as well as the records of this property in the files, were not parsed. The lines for theft of mail passwords are not visible, as well as the code that sends the collected information over the network. For this, probably, has its own components.
If such an infection becomes widespread, then no recommendation from Pinch like “Do not store passwords on disks” will help. Only the transition to encrypted communication channels remains. But considering the interception of all traffic, I think this will help little :-(
That to me in all this is not clear and strange:
1) WriteProcessMemory uses -1 as a handle. Why I doubt that calls for all applications will be intercepted.
2) Why use SetWindowsHookEx?
3) Is the activity of the trojan detected by behavioral analyzers? And in general at least some software (except for signature search).
4) Where do so many people find out about my post? I read, as far as I know, less than 10 people :-D
Time for analysis - about 2 hours (at the same time responding to comments). Tools - UPX + IDA Pro + OllyDbg (it was possible without it), head with brains.