November 02, 2010 23:53 / 14 comments / aol visual-basic

When I started working at my current job I was surprised to see that everyone used IRC as their primary means of communication - much more so than email or IM. I recently wrote a small irc bot library in python - it was a ton of fun and reminded me of some of the first programs I wrote that were bots and scripts for America Online:

The Internet

Remember AOHell? I wrote probably 10 different kinds of ripoffs (mass-mailers, room busters, punters?) but my favorite AOL scripts were always chat bots. There was string parsing involved (something every programmer loves), real time interaction, and of course it was fun to try and predict how people would pwn your bot and then write clever checks to make sure this didn't happen.

Coincidentally, I was going through my backup drive and ran across a bunch of old source code I'd squirreled away -- stuff I'd written almost 15 years ago! So, here's a guided tour of AOL bot writing!

Apocalypse

Gotta get your .bas file

In Visual Basic 3.0 parlance, the .bas file was a module full of constants, globals, subroutines and functions. In order to do any srs work it was necessary to talk to AOL through the Windows API, sending fake messages directly to AOL's user-interface. This bad-boy:

Declare Function SendMessage& Lib "User" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam As Any)

Windows API was divided up into a few big .dll files, User, GDI, Kernel and probably some more, and to call out to the API you needed to throw a declaration like the one above in your code. This was useful for, say, killing the hourglass, which one would do by invoking the modal "About Window" then killing it (don't ask me why this worked, it just did):

Sub TB_KillWait ()
    'Gets rid of the hourglass
    Dim hWnd%, about%, x
    hWnd% = FindWindow("AOL Frame25", 0&)
    Call RunMenuByString(hWnd%, "&About America Online")
        Do: DoEvents
        Loop Until FindWindow("_AOL_Modal", 0&) <> 0
    about% = FindWindow("_AOL_Modal", 0&)
    x = SendMessageByNum(about%, WM_DESTROY, 0&, 0&)
    x = SendMessageByNum(about%, WM_CLOSE, 0&, 0&)
End Sub

The .bas file I had was comprised of functions like TBC_Send, which would send a string into the currently active chat room:

Sub TBC_Send (Text)
    'Sends text to the chatroom
    AOL% = FindWindow("AOL FRAME25", 0&)
    List% = TB_FindChildByClass(AOL%, "_AOL_Listbox")
    If List% = 0 Then
        Exit Sub
    End If
    Room% = GetParent(List%)
    TXT% = TB_FindChildByClass(Room%, "_AOL_EDIT")
    SendBtn% = TB_FindChildByTitle(Room%, "Send")
    x = SendMessageByString(TXT%, WM_SETTEXT, 0, Text)
    x = SendMessageByNum(TXT%, WM_CHAR, 13, 0)
    timeout .05
End Sub

As you can see, it identifies the chatroom naively as "the room with the listbox" (!), then it finds the edit box, sets the text in it and appends an "Enter" key.

VB is Slow

In addition to SendMessage, the FindWindow and GetWindow API calls were also very useful, as they would find controls, textboxes, windows, etc that could then be manipulated with calls to SendMessage. I ran into some problems with VB being pokey, so I ended up writing a small .dll file to find child windows by their titles or class-names (I've taken the liberty of cleaning up the variable names as they were originally all one-letter and making the indentation uniform):

#include <string.h>
#include <windows.h>

extern "C" HANDLE _export _far _pascal TB_FindChildByTitle (HANDLE hWnd, char title[])
{
    HANDLE chld, tmp_chld;
    char tmp[200];
    int len;

     chld = GetWindow(hWnd, GW_CHILD);

     while (chld) {
        len = GetWindowText(chld, tmp, 199);
        tmp[len] = '\0';
         if (!strcmpi(tmp, title))
            return chld;

        tmp_chld = TB_FindChildByTitle(chld, title);
        if (tmp_chld)
            return tmp_chld;

        chld = GetWindow(chld, GW_HWNDNEXT);
    }
    return NULL;
}

The fun stuff

There was this really handy file called VBMsg.vbx that basically allowed your program to listen in on events that were sent to other windows. This was useful when the chat textbox was updated - just listen for WM_SETTEXT and parse the incoming bits. The following is taken from an "AFK Bot", basically an answering machine for when you're idling in chat or just wanted to seem cool:

Sub VBMsg1_WindowMessage (hWindow As Integer, Msg As Integer, wParam As Integer, lParam As Long, RetVal As Long, CallDefProc As Integer)
ChatText$ = agGetSTringfromLPSTR(lParam)
Colon% = InStr(ChatText$, ":")
SN$ = Mid(ChatText$, 3, Colon% - 3)
TextSaid$ = Mid(ChatText$, Colon% + 2)
If UCase(Left(TextSaid$, 4)) = "/MSG" Then
    Mesg$ = Mid(TextSaid$, 5)
    List1.AddItem SN$ & ":  " & Mesg$
    TBC_Send (CBracket(SN$ & ", message saved!"))
End If
End Sub

I still totally remember that incantation: $SN = Mid(ChatText, 3 Colon% - 3) -- I think the first two must've been CR/LF, then there was a tab before the colon.

Another fun one was the 8-Ball Bot which would just respond with a randomly chosen phrase:

Sub VBMsg1_WindowMessage (hWindow As Integer, Msg As Integer, wParam As Integer, LPARAM As Long, RetVal As Long, CallDefProc As Integer)
ChatText$ = agGetStringfromLPSTR(LPARAM)
Colon% = InStr(ChatText$, ":")
SN$ = Mid(ChatText$, 3, Colon% - 3)
TextSaid$ = UCase(Mid(ChatText$, Colon% + 2))
If InStr(TextSaid$, "/8BALL") Then
    x = Int(Rnd * 7) + 1
    Select Case x
        Case 1
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", Outlook good")
        Case 2
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", Reply hazy, try again")
        Case 3
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", Sources point to 'Yes'")
        Case 4
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", Don't count on it")
        Case 5
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", Definetely 'no'")
        Case 6
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", Yes")
        Case 7
        TBC_Send ("<-·´¯`·.·°( " & SN$ & ", No")
    End Select
End If
End Sub

The Warez Server!

Micro Server

The last snippet I'll paste is from a "warez server" -- to me this was like the holy grail! The way that it worked was you'd go into a chat room and some d00d would say, "Hey, who wants free software?" Everyone would say "I do" so he'd start up his warez server. People would type in something like "/Send List" and the bot would email you a list of everything the guy had in his mailbox. If you saw something you liked, you could say "/Send 123" or whatever number it was on the list and the bot would forward you a copy which you could then download. Pretty ingenious! This source code comes from the last AOL script I wrote, some time around 1997 or so -- it processes several lists (aptly named List1, List2, and List3).

It's worth noting that it does no caching, no prioritizing, and there's even the use of a few "GoTo" statements with descriptive labels like "HD" and "H". One of the neat tricks I see is it repeatedly clicks "Send" and checks for either the compose window going away or an error window appearing. If an error window appears it scans it for problematic screen names and tries again. Also, all those GetWindow calls with integer arguments -- 2 = gw_next (the window's sibling).

Sub Timer1_Timer ()
AOL% = FindWindow("AOL Frame25", 0&)
NewMail% = TB_FindChildByTitle(AOL%, "New Mail")
Tree% = TB_FindChildByClass(NewMail%, "_AOL_TREE")
ReadBtn% = TB_FindChildByTitle(NewMail%, "Read")
KeepAs% = TB_FindChildByTitle(NewMail%, "Keep As New")
MailCount% = SendMessageByNum(Tree%, LB_GETCOUNT, 0, 0)
If List1.ListCount Then
    Label4.Caption = Str(Val(Label4.Caption) + 1)
    MailLst$ = "<p align = center><font size=2 color=""#000080""><B>" & TBC_Bracket2("micro server by timeBomb") & Chr(13) & TBC_Bracket2("warez server list [" & MailCount% & "] items") & Chr(13) & "</p><p align=left></b><br>"
    For i = 0 To MailCount% - 1
        MailStr$ = String$(255, " ")
        Q% = SendMessageByString(Tree%, LB_GETTEXT, i, MailStr$)
        NoDate$ = Mid$(MailStr$, InStr(MailStr$, "/") + 4)
        NoSN$ = Mid$(NoDate$, InStr(NoDate$, Chr(9)) + 1)
        MailLst$ = MailLst$ & Chr(13) & "[" & i & "]  ~" & TBT_TrimNull(NoSN$)
        DoEvents
    Next i
    For i = 0 To List1.ListCount - 1
        m$ = m$ & List1.List(i) & ", "
    Next i
    TBA_RunMenu 3, 1
    Do: DoEvents
        AOL% = FindWindow("AOL Frame25", 0&)
        MailWin% = TB_FindChildByTitle(AOL%, "Compose Mail")
    Loop Until MailWin% <> 0
    MailTo% = TB_FindChildByClass(MailWin%, "_AOL_Edit")
    D% = GetWindow(MailTo%, 2)
    D% = GetWindow(D%, 2)
    D% = GetWindow(D%, 2)
    Subj% = GetWindow(D%, 2)
    Rich% = TB_FindChildByClass(MailWin%, "RICHCNTL")
    F = SendMessageByString(MailTo%, WM_SETTEXT, 0&, m$)
    F = SendMessageByString(Subj%, WM_SETTEXT, 0&, TBC_Bracket("micro server ~ list"))
    D% = GetWindow(Subj%, 2)
    D% = GetWindow(D%, 2)
    Body% = GetWindow(D%, 2)
    F = SendMessageByString(Body%, WM_SETTEXT, 0&, MailLst$)
    F = SendMessageByString(Rich%, WM_SETTEXT, 0&, MailLst$)
    Button% = TB_FindChildByClass(MailWin%, "_AOL_Icon")
    GoTo Hd
Hd:
    F = SendMessageByNum(Button%, WM_LBUTTONDOWN, &HD, 0&)
    F = SendMessageByNum(Button%, WM_LBUTTONUP, &HD, 0&)
    Timeout (.1)
    Do: DoEvents
        TBW_Click Button%
        Timeout (.15)
        MailWin% = TB_FindChildByTitle(AOL%, "Compose Mail")
        ErrorWin% = TB_FindChildByTitle(AOL%, "Error")
    Loop Until MailWin% = 0 Or ErrorWin% <> 0
    If ErrorWin% <> 0 Then
        ViewStr$ = UCase(TBW_GetWinText(FindChildByClass(ErrorWin%, "_AOL_View")))
        For i = 0 To List1.ListCount - 1
            If InStr(ViewStr$, UCase(List1.List(i))) Then
                List1.RemoveItem i
            End If
        Next i
        m$ = ""
        For i = 0 To List1.ListCount - 1
            m$ = m$ & List1.List(i) & ", "
        Next i
        F = SendMessageByString(MailTo%, WM_SETTEXT, 0&, m$)
        GoTo Hd
    End If
    List1.Clear
    DoEvents
    List1.Clear
    TBC_Send ("")
    TBC_Send (TBC_Bracket("pending lists sent out!"))
End If
If List2.ListCount Then
    Label4.Caption = Str(Val(Label4.Caption) + 1)
    m$ = List2.List(0)
    List2.RemoveItem 0
    Req = Left(m$, InStr(m$, ":") - 1)
    SN$ = Mid(m$, InStr(m$, ":") + 1)
    If Not IsNumeric(Req) Then Exit Sub
    If Req > MailCount% Then Exit Sub
    x = SendMessageByNum(Tree%, LB_SETCURSEL, Req, 0&)
    Do: DoEvents
        TBW_Click ReadBtn%
        Timeout (.2)
        Stat1% = TBM_MMForwardWin(False)
    Loop Until Stat1% <> 0
    Forward% = Stat1%
    FwdBtn% = TBM_MMForwardWin(True)
    Do: DoEvents
        TBW_Click FwdBtn%
        Timeout (.5)
        SendWin% = TBM_MMFindFwd()
    Loop Until SendWin% <> 0
    ToEd% = TB_FindChildByClass(SendWin%, "_AOL_EDIT")
    SubjEd% = TBW_GetChild(SendWin%, "_AOL_EDIT", 3)
    If TBA_AOLVer() = 25 Then
    MainEd% = TBW_GetChild(SendWin%, "_AOL_EDIT", 4)
    Else MainEd% = TB_FindChildByClass(SendWin%, "RICHCNTL")
    End If
    SendBttn% = TB_FindChildByClass(SendWin%, "_AOL_ICON")
    DoEvents
    x = SendMessageByString(ToEd%, WM_SETTEXT, 0&, SN$)
    DoEvents
    x = SendMessageByString(MainEd%, WM_SETTEXT, 0&, "<p align=center><font color=""#000080""><B>" & TBC_Bracket2("micro server by timeBomb") & "<BR>" & TBC_Bracket2("warez server item index [" & Req & "]"))
    DoEvents
    SubjText$ = TBW_GetWinText(SubjEd%)
    SubjText$ = Mid$(SubjText$, 5)
    x = SendMessageByString(SubjEd%, WM_SETTEXT, 0&, SubjText$)
    Do: DoEvents
        TBW_Click SendBttn%
        Timeout (1)
        SendWin% = TBM_MMFindFwd()
        ErrorWin% = TB_FindChildByTitle(AOL%, "Error")
    Loop Until SendWin% = 0 Or ErrorWin% <> 0
    If ErrorWin% <> 0 Then
        TBW_KillWin ErrorWin%
        TBW_KillWin Forward%
        TBW_KillWin SendWin%
        TBC_Send ("")
        TBC_Send (TBC_Bracket(SN$ & ", unable to send mail"))
        GoTo H
    End If
    TBW_KillWin Forward%
    GoTo H
H:
    DoEvents
    TBW_Click KeepAs%
    Timeout (.01)
    TBW_Click KeepAs%
    TBC_Send ("")
    TBC_Send (TBC_Bracket(SN$ & ", item #" & Req & " was sent!"))
End If
If List3.ListCount Then
    Label4.Caption = Str(Val(Label4.Caption) + 1)
    Colon% = InStr(List3.List(0), ":")
    reqz$ = Left(List3.List(0), Colon% - 1)
    SN$ = Mid(List3.List(0), Colon% + 1)
    reqz$ = UCase(reqz$)
    List3.RemoveItem 0
    For i = 0 To MailCount% - 1
        MailStr$ = String$(255, " ")
        Q% = SendMessageByString(Tree%, LB_GETTEXT, i, MailStr$)
        NoDate$ = Mid$(MailStr$, InStr(MailStr$, "/") + 4)
        NoSN$ = Mid$(NoDate$, InStr(NoDate$, Chr(9)) + 1)
        NoSN$ = TBT_TrimNull(NoSN$)
        If InStr(UCase(NoSN$), UCase(reqz$)) Then
            iCnt% = iCnt% + 1
            sRes$ = sRes$ & "Item [" & i & "] ~ " & NoSN$ & Chr(13)
        End If
        DoEvents
    Next i
    TBA_RunMenu 3, 1
    Do: DoEvents
        AOL% = FindWindow("AOL Frame25", 0&)
        MailWin% = TB_FindChildByTitle(AOL%, "Compose Mail")
    Loop Until MailWin% <> 0
    MailTo% = TB_FindChildByClass(MailWin%, "_AOL_Edit")
    D% = GetWindow(MailTo%, 2)
    D% = GetWindow(D%, 2)
    D% = GetWindow(D%, 2)
    Subj% = GetWindow(D%, 2)
    Rich% = TB_FindChildByClass(MailWin%, "RICHCNTL")
    F = SendMessageByString(MailTo%, WM_SETTEXT, 0&, SN$)
    F = SendMessageByString(Subj%, WM_SETTEXT, 0&, "micro tools server found: [" & iCnt% & "]")
    D% = GetWindow(Subj%, 2)
    D% = GetWindow(D%, 2)
    Body% = GetWindow(D%, 2)
    F = SendMessageByString(Body%, WM_SETTEXT, 0&, "<p align = center><font size=2 color=""#000080""><B>" & TBC_Bracket2("micro server by timeBomb") & Chr(13) & TBC_Bracket2("warez server find results [" & iCnt% & "] items") & Chr(13) & ("<p align=left>") & sRes$ & " ")
    F = SendMessageByString(Rich%, WM_SETTEXT, 0&, "<p align = center><font size=2 color=""#000080""><B>" & TBC_Bracket2("micro server by timeBomb") & Chr(13) & TBC_Bracket2("warez server find results [" & iCnt% & "] items") & Chr(13) & ("<p align=left>") & sRes$ & " ")
    Button% = TB_FindChildByClass(MailWin%, "_AOL_Icon")
    F = SendMessageByNum(Button%, WM_LBUTTONDOWN, &HD, 0&)
    F = SendMessageByNum(Button%, WM_LBUTTONUP, &HD, 0&)
    Timeout (.1)
    Do: DoEvents
        TBW_Click Button%
        Timeout (.15)
        MailWin% = TB_FindChildByTitle(AOL%, "Compose Mail")
        ErrorWin% = TB_FindChildByTitle(AOL%, "Error")
    Loop Until MailWin% = 0 Or ErrorWin% <> 0
    If ErrorWin% <> 0 Then
        TBW_KillWin ErrorWin%
        TBW_KillWin MailWin%
        TBC_Send ("")
        TBC_Send (TBC_Bracket(SN$ & ", unable to send mail"))
        Exit Sub
    End If
    DoEvents
    TBC_Send ("")
    TBC_Send (TBC_Bracket(SN$ & ", find results sent!"))
End If
End Sub

Conclusion

I hope you found this entertaining! I wish I had some screen-shots - I'll see about getting a Windows 95 VM going tomorrow and take some. Edit screen shots added!

If you remember writing proggies, using them, any anecdotes, feel free to post them in the comments! What epic things did you do to get your account TOSed?

AppActivate "America  Online" ' remember how it had two spaces?!?!
SendKeys "%{F4}"

Epic Intro Screen, Bro

Links

More like this

Comments (14)

Denny Ferrassoli | nov 2010, at 12:09am

Wow! This brings back memories indeed. I started programming thanks to AOL "Proggiez"

Jason | nov 2010, at 12:26am

Oh man this surely brings back memories. IM punters and the rest were always a blast, especially when aol changed versions on you and did not support the older stuff. I remember there were some big program names out there "FateX", "Hellraiser" etc. Wow, all the bad stupid stuff I did. Faking aol sign-on windows to steal accounts for random purposes. Thanks for the still through my early teens. -jason

Bruno | nov 2010, at 03:54am

Probably dating myself, but AOHell with the automatic, random account generator (with CC#!) was amazing.

duran | nov 2010, at 03:55am

Wow, great memories. That's how I, and countless others, got their start coding. Great times as a kid.

sujen | nov 2010, at 03:57am

A lot of people did like AOL. A lot of people like iThings today.

JoshTheGoods | nov 2010, at 06:20am

Awesome! I got my start programming AOL "progz." I hung out in private room: "test" with the "DoW Jones" crew (I was GooDy). I remember when some of the crew discovered the various uses of chr(0). I wrote a mass mailer that let the user re-arrange the mail before sending it out, what an innovation I thought at the time :P. Things got really crazy when we learned to hex edit the aol class name to allow opening multiple instances of the client. Things got super extra crazy when we cracked the protocol (with the help of leaked docs) that let us log in many many accounts without a client at all. Man, those were the days!

JP | nov 2010, at 08:24am

This brings back so much memories! This is how I got started in programming. Did anyone use to hang out in the room vb32?

Brandon | nov 2010, at 02:47pm

Wow this is great! Writing proggiez was my first programming experience too. I was all of 13 years old in 1997 and I dove into the world of AOL "hacking" head first. I remember all of this too well. I wish I had some of my old source code...but it's gone with the wind. I'm sure there are a few dozen 3.5 disks sitting in landfills with my old proggiez on them.

I got my account TOSed several times, so I entered the world of AOL "phishing". It was amazingly easy to get people to give you their password, and so I lived on dozens of different <>< (phish) accounts for a year or so.

Now I'm 26 and a professional software engineer. Not as exciting anymore.

col0ny | nov 2010, at 04:17pm

Man - zeraw, private(#), red, blue, vb, vb32, and was it server(#) or something else for all the server rooms? When beginning programming, "they say" to pick a project and just get at it. AOL was an awesome and fun project, and definitely helped to get me hooked. One of my favorite periods of life.

Nick / keeb | nov 2010, at 05:27pm

.. private channel vb4. That's what it was all about. I remember when chat com / mp3 players became popular. I was a 7th grader who wrote one and used it as a final test for my Advanced Programming in Visual Basic college class.

Then it was AIM spammers, and spam in general. So much money to be made by finding vulnerable email gateways.

I miss those days like no other. Thanks for the trip down memory lane.

Charles Leifer | nov 2010, at 06:03pm

It's great to see that so many people remember this stuff and enjoyed it as much as I did! Thanks for reading!

Jeff N. | nov 2010, at 08:59pm

Wow. I wish that I still had some of my old AOL source. Doing that was what made me decide to be a software engineer "when I grew up."

My favorite program was a baited to find internal aol accts called "Live Bait.". Me and one of my buddies would then try to crack them later.

osb | nov 2010, at 10:24pm

ah... memories.

unsakred | nov 2010, at 11:45am

AOL Progz are what got me into the game, then jumping to AIM, then Yahoo now I'm a web developer... go figure...

oh man, the memories..the memories.

Thanks for this blast from the past!


Commenting has been closed, but please feel free to contact me