Tutorial #2 by vman_ (aka Kaz)
Published by Tsehp 2002

Tools Needed:

1. A copy of the application (http://www.trillian.cc/) (v0.71)
2. Numega SoftIce (any version)
3. Windasm/IDA Pro 4.04+
4. Hex Workshop/Hiew

5. A Brain...

Thing You Need to Know:

1. A Basic knowledge of ASM
2. A Basic knowledge of PE File Headers
3. A Working knowledge of C/C++
4. A Working knowledge of Win32 API

5. Medium reversing experience

Introduction:

Many people today use Trillian as a Instant Messenger program. It combines AOL Instant Messenger, ICQ, MSN Messenger, and Yahoo Messenger all into one. The Trillian website (http://www.trillian.cc/) describes the program as the following:

"Communicate with Flexibility and Style. Trillian is everything you need for instant messaging.Connect to ICQ, AOL Instant Messenger, MSN Messenger, Yahoo! Messenger and IRC in a single, sleek and slim interface."

Upon downloading this application, most people will notice a very large flaw with Trillian; the logging capabilities of Trillian are very weak, logs aren't encrypted in anyway whatsoever (like ICQ does), and logs are left out in the open for anyone who has access to that PC. Living in a household dormant with family members that have questionable intentions, this design flaw in Trillian presents many problems.

Our solution: Have the logs get saved in an encrypted fashion (lame xor encryption).

Analysis:

Before we start doing anything, we have to plan out our steps

  1. Find the logging routine(s).
  2. Create new code for our encryption function.
  3. Make a decrypting program.

*** NOTE ***: A little tidbit before we start to work... When your in Trillian, double click on someone to message them and type in the following: /runwordwraptest. Enjoy :-D

Finding Logging Routines:

We begin by sorting through the log files. Open any log file that you have sitting in your log folder, and look for any signs of log specific text. In my case, I'm going to use a part of a log file from my friend Ikara on ICQ:

Session Start (ICQ - 2592580:Tifosi): Fri Jan 25 09:03:19 2002
*** NOTE: This user is offline. Your messages will be received when he/she logs into ICQ.
Kasra: wake up you BITCH
Session Close (Tifosi): Fri Jan 25 09:04:25 2002

This basically shows us when the message window was opened, what was inside of it, and when it was closed. The 'Session Start' and 'Session Close' messages don't appear inside the message window, which means that they are only ment for log files. Knowing that, lets search for *.exe's and *.dll's that contain those words in Trillian's main folder (Start Menu -> Search -> For Files or Folders...). The only file that shows up is "talk.dll".

Our next step is to disassemble this file. Make sure you have a backup of it made and put aside in case accidents are made. Open this file in IDA, and do a search for the text "Session Start" (without quotes). It'll drop you off in sub_10035D00. There are two references in this sub routine to text with the words "Session Start" in them, both being pushed in before a call to sub_10051A0F. Judging from the strings from both references being of formatted type we have to assume that sub_10051A0F is a function like sprintf()/printf()/fprintf.

If we move up the function we reach a piece of code that opens a file appending, then saves the pointer it returns:

10035D4F		 push	 offset	aA_2	 ; "a+" <--- 2nd param for fopen (mode)
10035D54		 push	 edx <--- 1st param for fopen (filename)
10035D55		 call	 _fopen <--- call fopen function
10035D5A		 add	 esp, 8 <--- fix stack
10035D5D		 mov	 [esi+108h], eax <--- save FILE pointer
10035D63		 test	 eax, eax <--- check to see if FILE pointer is NULL (0)
10035D65		 jz	 loc_10035E09 <--- if it is, jump to end of sub routine

Seeing this part of the function, we see information extracted from [esi+108h] in the two references we found. This means that sub_10051A0F must be fprintf(), but IDA's flirt signatures didn't pick up that function for some reason.

First Reference:

10035DA1		 mov	 edx, [esi+108h] <--- put FILE pointer into edx
10035DA7		 add	 esp, 8 <--- fix stack
10035DAA		 push	 eax <--- push address for the second %s
10035DAB		 push	 ecx <--- push address for the first %s
10035DAC		 push	 offset	aSessionStartSS	; "Session Start (%s): %s" <--- format string
10035DB1		 push	 edx <--- saved file ptr
10035DB2		 call	 sub_10051A0F <--- fprintf()

Second Reference:

10035DD5		 mov	 edx, [esi+108h] <--- put FILE pointer into edx
10035DDB		 add	 esp, 8 <--- fix stack
10035DDE		 push	 eax <--- push address for the third %s
10035DDF		 push	 edi <--- push address for the second %s
10035DE0		 push	 ecx <--- push address for the first %s
10035DE1		 push	 offset	aSessionStart_0	; "Session Start (%s:%s): %s" <--- format string
10035DE6		 push	 edx <--- saved file ptr
10035DE7		 call	 sub_10051A0F <--- fprintf()

Upon further investigation, you will notice that all other references to fprintf are for logs. Therefor we all be adding code to the fprintf routine to encrypt our log info before it gets written to the file. Trying to encrypt the string before hand will mess up the format specifications.

Manually Adding a PE Section to Talk.DLL:

In order to add extra code to our program, we will need to make extra room in the file. Each section header info is 40 bytes long. The 38 bytes consist of the following:

QWORD SectionName;
DWORD SizeOfContent;
DWORD RVA;
DWORD SizeOfRawData;
DWORD FileOffset;
DWORD PointerToRelocations; // NOT USED IN EXECUTABLES
DWORD PointerToLinenumbers; // NOT USED IN EXECUTABLES
WORD NumberOfRelocations; // NOT USED IN EXECUTABLES
WORD NumberOfLinenumbers; // NOT USED IN EXECUTABLES
DWORD Characteristics;

*** NOTE ***: For those of you who don't know what a QWORD (Quad Word) is, it is a 8 bytes long. So basically it's just four WORDs. The QWORD in our list is used as a non-null terminated string. It contains the sections name.

The easiest way to do this is to view the file in a hexeditor, and look to see how many SectionNames there are, go to the end of the last one, count 40 bytes, and begin at that point. Here is a hex dump of what you should see in your hex editor:

 000001F0:  00 00 00 00-00 00 00 00-2E 74 65 78-74 00 00 00          .text
 00000200:  32 C0 05 00-00 10 00 00-00 D0 05 00-00 10 00 00  2+       
 00000210:  00 00 00 00-00 00 00 00-00 00 00 00-20 00 00 60                 `
 00000220:  2E 72 64 61-74 61 00 00-34 34 00 00-00 E0 05 00  .rdata  44   
 00000230:  00 40 00 00-00 E0 05 00-00 00 00 00-00 00 00 00   @   
 00000240:  00 00 00 00-40 00 00 40-2E 64 61 74-61 00 00 00      @  @.data
 00000250:  74 C3 00 00-00 20 06 00-00 B0 00 00-00 20 06 00  t+      _    
 00000260:  00 00 00 00-00 00 00 00-00 00 00 00-40 00 00 C0              @  +
 00000270:  2E 53 48 41-52 44 41 54-08 00 00 00-00 F0 06 00  .SHARDAT    ­
 00000280:  00 10 00 00-00 D0 06 00-00 00 00 00-00 00 00 00      
 00000290:  00 00 00 00-40 00 00 D0-2E 72 73 72-63 00 00 00      @  .rsrc
 000002A0:  E0 74 00 00-00 00 07 00-00 80 00 00-00 E0 06 00  t         
 000002B0:  00 00 00 00-00 00 00 00-00 00 00 00-40 00 00 40              @  @
 000002C0:  2E 72 65 6C-6F 63 00 00-AE 49 00 00-00 80 07 00  .reloc  I   
 000002D0:  00 50 00 00-00 60 07 00-00 00 00 00-00 00 00 00   P   `
 000002E0:  00 00 00 00-40 00 00 42-00 00 00 00-00 00 00 00      @
 00000300:  00 00 00 00-00 00 00 00-00 00 00 00-20 00 00 00

".text" is the first PE section we notice. It starts off in 0x1F8. There are six PE Section headers in total listed down here. Here is a more organized way of looking at them when you put them in a chart:

 
Section 1
Section 2
Section 3
Section 4
Section 5
Section 6
SectionName
.text .rdata .data .SHARDAT .rsrc .reloc
SizeOfContent 0x0005C032 0x00003434 0x0000C374 0x00000008 0x000074E0 0x000049AE
RVA 0x00001000 0x0005E000 0x00062000 0x0006F000 0x00070000 0x00078000
SizeOfRawData 0x0005D000 0x00004000 0x0000B000 0x00001000 0x00008000 0x00005000
FillOffset 0x00001000 0x0005E000 0x00062000 0x0006D000 0x0006E000 0x00076000
PointerToRelocations 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
PointerToLineNumbers 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
NumberOfRelocations 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
NumberOfLineNumbers 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
Characteristics 0x60000020 0x40000040 0xC0000040 0xD0000040 0x40000040 0x42000040

What we want to do is create a seventh PE Section which we'll use to inject our own code into. We'll begin by writing out the header information. The file ends at 0x0007B000 and we want our section to be 0x500 bytes long. The easiest way is to go into Hex Workshop, move to the end of the file, go to Edit -> Insert, set the "Number of Bytes" to 500 Hex, set the "Fill with the following hex byte" field to 0, and press "Ok". Make note that our section starts at 0x0007B000.

After that's done, what needs to be done is the Section Header. The last Section Header is called ".reloc". Start from the start of the name (QWORD) and move across 40 bytes (come out to 0x2E8), here is where we should start writing out our section header info. We'll name our section after me, ".kaz":

QWORD SectionName = '.kaz';
DWORD SizeOfContent = 0x500;
DWORD RVA = 0x0007B000+0x2000; // Align RVA to match rest of file
DWORD SizeOfRawData = 0x500;
DWORD FileOffset = 0x0007B000;
DWORD PointerToRelocations = 0x00000000; // NOT USED IN EXECUTABLES
DWORD PointerToLinenumbers = 0x00000000; // NOT USED IN EXECUTABLES
WORD NumberOfRelocations = 0x0000; // NOT USED IN EXECUTABLES
WORD NumberOfLinenumbers = 0x0000; // NOT USED IN EXECUTABLES
DWORD Characteristics = 0x60000020; // Same characteristics value as .text

If you look at section six's RVA and FileOffset, you will notice that it has a difference of 0x2000 bytes. We need to maintain that alignment or else our PE section will be out of whack. Also, the characteristics were set to the same characteristics as section one's, because section one has it's flags set for executable code, and that's the same thing we need for our section since we'll be executing code from it. The end result once all this crap is entered into the hex editor should look like this:


 000001F0:  00 00 00 00-00 00 00 00-2E 74 65 78-74 00 00 00          .text
 00000200:  32 C0 05 00-00 10 00 00-00 D0 05 00-00 10 00 00  2+       
 00000210:  00 00 00 00-00 00 00 00-00 00 00 00-20 00 00 60                 `
 00000220:  2E 72 64 61-74 61 00 00-34 34 00 00-00 E0 05 00  .rdata  44   
 00000230:  00 40 00 00-00 E0 05 00-00 00 00 00-00 00 00 00   @   
 00000240:  00 00 00 00-40 00 00 40-2E 64 61 74-61 00 00 00      @  @.data
 00000250:  74 C3 00 00-00 20 06 00-00 B0 00 00-00 20 06 00  t+      _    
 00000260:  00 00 00 00-00 00 00 00-00 00 00 00-40 00 00 C0              @  +
 00000270:  2E 53 48 41-52 44 41 54-08 00 00 00-00 F0 06 00  .SHARDAT    ­
 00000280:  00 10 00 00-00 D0 06 00-00 00 00 00-00 00 00 00      
 00000290:  00 00 00 00-40 00 00 D0-2E 72 73 72-63 00 00 00      @  .rsrc
 000002A0:  E0 74 00 00-00 00 07 00-00 80 00 00-00 E0 06 00  t         
 000002B0:  00 00 00 00-00 00 00 00-00 00 00 00-40 00 00 40              @  @
 000002C0:  2E 72 65 6C-6F 63 00 00-AE 49 00 00-00 80 07 00  .reloc  I   
 000002D0:  00 50 00 00-00 60 07 00-00 00 00 00-00 00 00 00   P   `
 000002E0:  00 00 00 00-40 00 00 42-2E 6B 61 7A-00 00 00 00      @  B.kaz
 000002F0:  00 05 00 00-00 D0 07 00-00 05 00 00-00 B0 07 00           _
 00000300:  00 00 00 00-00 00 00 00-00 00 00 00-20 00 00 60

Now, all we have to do is change the PE Header "NumberOfSections" value from 6 to 7. It's the second WORD after the 0x00004550 ('PE'):

 00000100:  50 45 00 00-4C 01 06 00-4E 29 20 3C-00 00 00 00  PE  L N) <

So basically it should look like this:

 00000100:  50 45 00 00-4C 01 07 00-4E 29 20 3C-00 00 00 00  PE  L N) <

Ok, we basically have a new PE section added that's 0x500 bytes long, and we can add our own code into it. Now, onto the modifications.

Inside FPRINTF():

The fprintf() function in this program looks pretty standard:

10051A0F		 push	 ebx
10051A10		 push	 esi
10051A11		 mov	 esi, [esp+0Ch]
10051A15		 push	 edi
10051A16		 push	 esi
10051A17		 call	 __lock_file
10051A1C		 push	 esi
10051A1D		 call	 __stbuf
10051A22		 mov	 edi, eax
10051A24		 lea	 eax, [esp+20h]
10051A28		 push	 eax
10051A29		 push	 dword ptr [esp+20h]
10051A2D		 push	 esi
10051A2E		 call	 unknown_libname_23
10051A33		 push	 esi
10051A34		 push	 edi
10051A35		 mov	 ebx, eax
10051A37		 call	 __ftbuf
10051A3C		 push	 esi
10051A3D		 call	 __unlock_file
10051A42		 add	 esp, 20h
10051A45		 mov	 eax, ebx
10051A47		 pop	 edi
10051A48		 pop	 esi
10051A49		 pop	 ebx
10051A4A		 retn

After going over this function with SoftICE a couple of times, we notice that on address 10051A2E the format string and the values passed in for it get lumped up together in. A pointer pointing to the end of the new formatted string in ESI and the length of the new formatted string is returned in EAX. So basically what we must do is take out some of the ASM code after this call, then put a jump to our code, read the ASM code to the end of our own code section, then jump back.

Sticking it All Together:

To make room for our jump to our encryption code, we must take out certain ASM instructions and place them in the end of our encryption function. The instructions that have to be moved is everything after "call unknown_libname_23" at 10051A2E and before "push esi" at 10051A3C. The instructions that get replaced are a few nop's and a jmp.

.10051A33: 90               nop <--- no operation
.10051A34: 90               nop <--- no operation
.10051A35: 90               nop <--- no operation
.10051A36: 90               nop <--- no operation
.10051A37: E9C1B70200       jmp      .01007D1FD <--- jump to the start of our encryption function

I decided to write my encryption function starting at 1007D1FD, in the middle of the ".kaz" section :-D.

Mkay, now its time to write our ASM code. Here is the asm code I wrote up to encrypt the text (Yes, I know... very cheese):

.1007D1FD: 60               pushad <--- push all registers into stack
.1007D1FE: 8B0E             mov       ecx,[esi] <--- move end of text address into ecx
.1007D200: 2BC8             sub       ecx,eax <--- ecx will point to start of of string
.1007D202: 33D2             xor       edx,edx <--- clear out edx (used as counter)
.1007D204: 803C1100         cmp       b,[ecx][edx],000 <--- end of string?
.1007D208: 740A             je       .01007D214 <--- if yes, jump out of loop
.1007D20A: 80341106         xor       b,[ecx][edx],006 <--- xor byte in string with 0x06
.1007D20E: 42               inc       edx <--- increase counter by 1
.1007D20F: E9F0FFFFFF       jmp      .01007D204 <--- go back to end of string check
.1007D214: 61               popad <--- bring back all the registers saved in stack
.1007D215: 56               push      esi <--- code moved from fprintf function
.1007D216: 57               push      edi <--- code moved from fprintf function
.1007D217: 8BC3             mov       eax,ebx <--- code moved from fprintf function
.1007D219: E8CA9FFDFF       call     .0100571E8 <--- code moved from fprintf function (call __ftbuf)
.1007D21E: E91948FDFF       jmp      .010051A3C <--- jump back to fprintf function

If we were to save the changes we've made right now and run Trillian, this would work like a charm. Yet there is still one more problem we have to deal with! We don't want our unencrypted logs getting mangled into our encrypted logs. The solution? Change the extension in Trillian from .log to .kaz. This is simple to do, go into your favour hex editor and do a search for ".log" (without quotes). You will eventually see "%s\%s.log", this is the only format string in the entire DLL file that contains the extension .log, so it must be the saving extension. Change the ".log" part to ".kaz" or any other three letter extension of you choice. This will make Trillian now save logs to the ".kaz"extension instead of the ".log" extension.

The C Code:

Here is a simple C code to decrypt the logs for you to read them:

#include <stdio.h>
#include <string.h>

int main() {
	char encryptedFile[256];
	char decryptedFile[256];
	int ch = 0;

	printf("hello. welcome to vman's cheesy trillian log decrypter\n\n");
	printf("file to decrypt:");
	gets(encryptedFile);
	printf("file to save as:");
	gets(decryptedFile);

	if (strcmp(encryptedFile, "") == 0 || strcmp(decryptedFile, "") == 0)
	{
		printf("bye!\n");
		return 0;
	}

	FILE *fR = fopen(encryptedFile, "rb");
	FILE *fW = fopen(decryptedFile, "w");

	if ( fR == 0 || fW == 0 )
	{
		printf("uh oh! couldn't open file\n");
		return 0;
	}
	
	
	while( feof(fR) == 0 )
	{
		ch = fgetc(fR);
		ch ^= 0x06;
		fputc(ch, fW);
	}

	printf("done!\n");

	fclose(fR);
	fclose(fW);
}

Testing:

For the final test I will be using my friend Ikara on ICQ.

Log file with encrypted-logging enabled Trillian:

Ucuuoih&Urgtr&.OEW&+&43?43>6<Ro`iuo/<&@to&Lgh&43&7?<75<37&4664Mg
utg<&nsdctr*&qn&gtc&s&usen&g&ursvob&doren9Ro`iuo<&o&lsur&gk'M
gutg<&k&tceikkchbgroih*&uriv&dcoha&g&ursvob&dorenRo`iuo<&qojj&b
i(((Ucuuoih&Ejiuc&.Ro`iuo/<&@to&Lgh&43&7?<72<25&4664

That same log file run under the C Code in the section above:

Session Start (ICQ - 2592580:Tifosi): Fri Jan 25 19:13:51 2002
Kasra: hubert, why are u such a stupid bitch?
Tifosi: i just am!
Kasra: my recommendation, stop being a stupid bitch
Tifosi: will do...
Session Close (Tifosi): Fri Jan 25 19:14:43 2002

As we can clearly see, the original encrypted log file looks like what your twelve year old next door neighbor would write if he was messed up on acid. So the next time someone using the same PC as you attempts to access your log files, all they'll see is a bunch of scrambled text.

Conclusion:

In conclusion, after adding this bit of code you can have your mind put at ease about idiots wanting to look through it. You can also change the encryption routine and make it into something way better. If you need to contact me, you can email me at shogo2001@yahoo.com, ICQ me at 2592580, or contact me on IRC (efnet).