A Stroll Down Memory Lane: Scripting AOL
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:
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, 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, from me 15 years ago.
Get a .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 serious 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 bytes. 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
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 somebody from a warez clan (these were a thing) would say, "I'm going to start serving". Everyone would get excited. Once the server started, usually announced with a flood of ASCII art, people could 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).
- List1 -> people waiting for a list of all the warez
- List2 -> Individual requests for specific emails
- List3 -> Search requests
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}"
Links
- The Truth - an open letter by "Happy Hardcore", the guy that wrote AOL4Free
- History of warez - hilarious
Comments (14)
osb | nov 03 2010, at 10:24pm
ah... memories.
Jeff N. | nov 03 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.
Charles Leifer | nov 03 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!
Nick / keeb | nov 03 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.
col0ny | nov 03 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.
Brandon | nov 03 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.
JP | nov 03 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?
JoshTheGoods | nov 03 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!
sujen | nov 03 2010, at 03:57am
A lot of people did like AOL. A lot of people like iThings today.
duran | nov 03 2010, at 03:55am
Wow, great memories. That's how I, and countless others, got their start coding. Great times as a kid.
Bruno | nov 03 2010, at 03:54am
Probably dating myself, but AOHell with the automatic, random account generator (with CC#!) was amazing.
Jason | nov 03 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
Denny Ferrassoli | nov 03 2010, at 12:09am
Wow! This brings back memories indeed. I started programming thanks to AOL "Proggiez"
Commenting has been closed.
unsakred | nov 05 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!