Adding SLProxy project.
git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@74 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
97
applications/SLProxy/Analyst.cs
Normal file
97
applications/SLProxy/Analyst.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Analyst.cs: proxy that dumps all packets to and from the server
|
||||
*
|
||||
* Copyright (c) 2006 Austin Jennings
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the Second Life Reverse Engineering Team nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using SLProxy;
|
||||
using libsecondlife;
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
public class Analyst {
|
||||
public static void Main(string[] args) {
|
||||
ProtocolManager protocolManager = new ProtocolManager("keywords.txt", "protocol.txt");
|
||||
ProxyConfig proxyConfig = new ProxyConfig("Analyst", "austin.jennings@gmail.com", protocolManager, args);
|
||||
Proxy proxy = new Proxy(proxyConfig);
|
||||
|
||||
// register delegates for all packets
|
||||
RegisterDelegates(proxy, protocolManager.LowMaps);
|
||||
RegisterDelegates(proxy, protocolManager.MediumMaps);
|
||||
RegisterDelegates(proxy, protocolManager.HighMaps);
|
||||
|
||||
proxy.Start();
|
||||
}
|
||||
|
||||
// register delegates for each packet in an array of packet maps
|
||||
private static void RegisterDelegates(Proxy proxy, MapPacket[] maps) {
|
||||
PacketDelegate incomingLogger = new PacketDelegate(LogIncomingPacket);
|
||||
PacketDelegate outgoingLogger = new PacketDelegate(LogOutgoingPacket);
|
||||
foreach (MapPacket map in maps)
|
||||
if (map != null) {
|
||||
proxy.AddDelegate(map.Name, Direction.Incoming, incomingLogger);
|
||||
proxy.AddDelegate(map.Name, Direction.Outgoing, outgoingLogger);
|
||||
}
|
||||
}
|
||||
|
||||
// delegate for incoming packets: log the packet and return it unharmed
|
||||
private static Packet LogIncomingPacket(Packet packet, IPEndPoint endPoint) {
|
||||
LogPacket(packet, endPoint, Direction.Incoming);
|
||||
return packet;
|
||||
}
|
||||
|
||||
// delegate for outgoing packets: log the packet and return it unharmed
|
||||
private static Packet LogOutgoingPacket(Packet packet, IPEndPoint endPoint) {
|
||||
LogPacket(packet, endPoint, Direction.Outgoing);
|
||||
return packet;
|
||||
}
|
||||
|
||||
// helper method: perform the logging of a packet
|
||||
private static void LogPacket(Packet packet, IPEndPoint endPoint, Direction direction) {
|
||||
Console.WriteLine("{0} {1,21} {2,5} {3}{4}{5}"
|
||||
,direction == Direction.Incoming ? "<--" : "-->"
|
||||
,endPoint
|
||||
,packet.Sequence
|
||||
,InterpretOptions(packet.Data[0])
|
||||
,Environment.NewLine
|
||||
,packet
|
||||
);
|
||||
}
|
||||
|
||||
// produce a string representing a packet's header options
|
||||
private static string InterpretOptions(byte options) {
|
||||
return "["
|
||||
+ ((options & Helpers.MSG_APPENDED_ACKS) != 0 ? "Ack" : " ")
|
||||
+ " "
|
||||
+ ((options & Helpers.MSG_RESENT) != 0 ? "Res" : " ")
|
||||
+ " "
|
||||
+ ((options & Helpers.MSG_RELIABLE) != 0 ? "Rel" : " ")
|
||||
+ " "
|
||||
+ ((options & Helpers.MSG_ZEROCODED) != 0 ? "Zer" : " ")
|
||||
+ "]"
|
||||
;
|
||||
}
|
||||
}
|
||||
107
applications/SLProxy/ChatConsole.cs
Normal file
107
applications/SLProxy/ChatConsole.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* ChatConsole.cs: sample SLProxy appliation that writes chat to the console.
|
||||
* Typing on the console will send chat to Second Life.
|
||||
*
|
||||
* Copyright (c) 2006 Austin Jennings
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the Second Life Reverse Engineering Team nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using SLProxy;
|
||||
using libsecondlife;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
public class ChatConsole {
|
||||
private static ProtocolManager protocolManager;
|
||||
private static Proxy proxy;
|
||||
private static SessionInformation sessionInformation;
|
||||
|
||||
public static void Main(string[] args) {
|
||||
// configure the proxy
|
||||
protocolManager = new ProtocolManager("keywords.txt", "protocol.txt");
|
||||
ProxyConfig proxyConfig = new ProxyConfig("ChatConsole", "austin.jennings@gmail.com", protocolManager, args);
|
||||
proxy = new Proxy(proxyConfig);
|
||||
|
||||
// set a delegate for when the client logs in
|
||||
proxy.SetLoginDelegate(new LoginDelegate(Login));
|
||||
|
||||
// add a delegate for incoming chat
|
||||
proxy.AddDelegate("ChatFromSimulator", Direction.Incoming, new PacketDelegate(ChatFromSimulator));
|
||||
|
||||
// start the proxy
|
||||
proxy.Start();
|
||||
}
|
||||
|
||||
private static void Login(SessionInformation session) {
|
||||
// remember our session information
|
||||
sessionInformation = session;
|
||||
|
||||
// start a new thread that reads lines from the console
|
||||
(new Thread(new ThreadStart(ReadFromConsole))).Start();
|
||||
}
|
||||
|
||||
private static void ReadFromConsole() {
|
||||
// send text from the console in an infinite loop
|
||||
for (;;) {
|
||||
// read a line from the console
|
||||
string message = Console.ReadLine();
|
||||
|
||||
// construct a ChatFromViewer packet
|
||||
Hashtable blocks = new Hashtable();
|
||||
Hashtable fields;
|
||||
fields = new Hashtable();
|
||||
fields["Channel"] = (int)0;
|
||||
fields["Message"] = PacketUtility.StringToVariable(message);
|
||||
fields["Type"] = (byte)1;
|
||||
blocks[fields] = "ChatData";
|
||||
fields = new Hashtable();
|
||||
fields["AgentID"] = sessionInformation.agentID;
|
||||
fields["SessionID"] = sessionInformation.sessionID;
|
||||
blocks[fields] = "AgentData";
|
||||
Packet chatPacket = PacketBuilder.BuildPacket("ChatFromViewer", protocolManager, blocks, Helpers.MSG_RELIABLE);
|
||||
|
||||
// inject the packet
|
||||
proxy.InjectPacket(chatPacket, Direction.Outgoing);
|
||||
}
|
||||
}
|
||||
|
||||
private static Packet ChatFromSimulator(Packet packet, IPEndPoint sim) {
|
||||
// deconstruct the packet
|
||||
Hashtable blocks = PacketUtility.Unbuild(packet);
|
||||
string message = PacketUtility.VariableToString((byte[])PacketUtility.GetField(blocks, "ChatData", "Message"));
|
||||
string name = PacketUtility.VariableToString((byte[])PacketUtility.GetField(blocks, "ChatData", "Name"));
|
||||
byte audible = (byte)PacketUtility.GetField(blocks, "ChatData", "Audible");
|
||||
byte type = (byte)PacketUtility.GetField(blocks, "ChatData", "Type");
|
||||
|
||||
// if this was a normal, audible message, write it to the console
|
||||
if (audible != 0 && (type == 0 || type == 1 || type == 2))
|
||||
Console.WriteLine(name + ": " + message);
|
||||
|
||||
// return the packet unmodified
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
21
applications/SLProxy/Makefile
Normal file
21
applications/SLProxy/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
default: library examples
|
||||
|
||||
library: SLProxy.dll
|
||||
|
||||
examples: ChatConsole.exe Analyst.exe
|
||||
|
||||
clean::
|
||||
rm -f SLProxy.dll ChatConsole.exe Analyst.exe
|
||||
|
||||
SLProxy.dll: libsecondlife.dll XmlRpcCS.dll SLProxy.cs
|
||||
mcs SLProxy.cs -target:library -r:libsecondlife.dll -r:XmlRpcCS.dll
|
||||
|
||||
ChatConsole.exe: SLProxy.dll libsecondlife.dll ChatConsole.cs
|
||||
mcs ChatConsole.cs -r:SLProxy.dll -r:libsecondlife.dll
|
||||
Analyst.exe: SLProxy.dll libsecondlife.dll Analyst.cs
|
||||
mcs Analyst.cs -r:SLProxy.dll -r:libsecondlife.dll
|
||||
|
||||
libsecondlife.dll:
|
||||
@echo Please place a copy of libsecondlife.dll in this folder.; false
|
||||
XmlRpcCS.dll:
|
||||
@echo Please place a copy of XmlRpcCS.dll in this folder.; false
|
||||
221
applications/SLProxy/README
Normal file
221
applications/SLProxy/README
Normal file
@@ -0,0 +1,221 @@
|
||||
SLProxy is a library that works in conjunction with libsecondlife to
|
||||
allow applications to wedge themselves between the official Second
|
||||
Life client and servers. SLProxy applications can inspect and modify
|
||||
any packet as it passes between the client and the servers; remove
|
||||
packets from the stream; and inject new packets into the stream.
|
||||
SLProxy automatically takes care of tracking circuits and modifying
|
||||
sequence numbers and acknowledgements to account for changes to the
|
||||
packet stream.
|
||||
|
||||
The continued existence of this software of course rests on the good
|
||||
will of Linden Lab toward the Second Life reverse engineering effort.
|
||||
Please use common sense when designing applications and report any
|
||||
security holes you may find to security@lindenlab.com.
|
||||
|
||||
In addition to the SLProxy library, this package contains two short
|
||||
example applications. Analyst simply dumps every packet sent between
|
||||
the client and the server to the console. ChatConsole dumps all
|
||||
in-world chat to the console, and sends all text typed at the console
|
||||
to the world as chat.
|
||||
|
||||
To use an SLProxy application, you must first start the proxy, then
|
||||
start Second Life with the switch `-loginuri http://localhost:8080/'.
|
||||
In Windows, add this switch (without the quotes) to your Second Life
|
||||
shortcut. In MacOS X, this can be accomplished by sending the
|
||||
following commands to the Terminal (assuming Second Life is installed
|
||||
in /Applications):
|
||||
|
||||
cd "/Applications/Second Life.app"
|
||||
"Contents/MacOS/Second Life" -loginuri http://localhost:8080/
|
||||
|
||||
Note that for security reasons, by default, SLProxy applications must
|
||||
be running on the same computer as Second Life. If you need to run a
|
||||
proxy on a different machine or port, start the proxy with the
|
||||
--proxy-help switch and see the options available.
|
||||
|
||||
SLProxy can only handle one client using a proxy at a time.
|
||||
|
||||
PUBLIC INTERFACE
|
||||
================
|
||||
|
||||
This section describes the interface that SLProxy applications will
|
||||
use to interact with the packet stream. Please see ChatConsole.cs for
|
||||
a simple example of how this interface can be used.
|
||||
|
||||
SLProxy extends the functionality of libsecondlife, so we assume here
|
||||
that the reader is already familiar with libsecondlife's Packet and
|
||||
PacketBuilder classes.
|
||||
|
||||
1. ProxyConfig class
|
||||
--------------------
|
||||
|
||||
An instance of ProxyConfig represents the configuration of a Proxy
|
||||
object, and must be provided when constructing a Proxy. ProxyConfig
|
||||
has two constructors:
|
||||
|
||||
ProxyConfig(string userAgent, string author)
|
||||
ProxyConfig(string userAgent, string author, string[] args)
|
||||
|
||||
Both constructors require a user agent name and the author's email
|
||||
address. These are sent to Second Life's login server to identify the
|
||||
client, and to allow Linden Lab to get in touch with authors whose
|
||||
applications may inadvertantly be causing problems. The second
|
||||
constructor is preferred and takes an array of command-line arguments
|
||||
that allow the user to override certain network settings. For a list
|
||||
of command line arguments, start your appliation with the --proxy-help
|
||||
switch.
|
||||
|
||||
2. Proxy class
|
||||
--------------
|
||||
|
||||
The Proxy class represents an instance of an SLProxy and provides the
|
||||
methods necessary to modify the packet stream. Proxy's sole
|
||||
constructor takes an instance of ProxyConfig.
|
||||
|
||||
2.1 Login delegates
|
||||
- - - - - - - - - -
|
||||
|
||||
You may specify that SLProxy should call a delegate method in your
|
||||
application when the user successfully logs into Second Life:
|
||||
|
||||
delegate void LoginDelegate(SessionInformation session)
|
||||
void SetLoginDelegate(LoginDelegate loginDelegate)
|
||||
|
||||
LoginDelegate methods are passed a SessionInformation object, which
|
||||
contains the agentID and sessionID for the current session. These
|
||||
values are required when injecting certain kinds of packets.
|
||||
|
||||
Note that all delegates must terminate (not go into an infinite loop),
|
||||
and must be thread-safe.
|
||||
|
||||
2.2 Packet delegates
|
||||
- - - - - - - - - -
|
||||
|
||||
Packet delegates allow you to inspect and modify packets as they pass
|
||||
between the client and the server:
|
||||
|
||||
delegate Packet PacketDelegate(Packet packet, IPEndPoint endPoint)
|
||||
void AddDelegate(string packetName, Direction direction, PacketDelegate packetDelegate)
|
||||
void RemoveDelegate(string packetName, Direction direction)
|
||||
|
||||
AddDelegate adds a callback delegate for packets named packetName
|
||||
going direction. Directions are either Direction.Incoming, meaning
|
||||
packets heading from the server to the client, or Direction.Outgoing,
|
||||
meaning packets heading from the client to the server. Only one
|
||||
delegate can apply to a packet at a time; if you add a new delegate
|
||||
with the same packetName and direction, the old one will be removed.
|
||||
|
||||
RemoveDelegate simply removes the delegate for the specified type of
|
||||
packet.
|
||||
|
||||
PacketDelegate methods are passed a copy of the packet (in the form of
|
||||
a libsecondlife Packet object) and the IPEndPoint of the server that
|
||||
sent (or will receive) the packet. PacketDelegate methods may do one
|
||||
of three things:
|
||||
|
||||
1. Return the same packet, in which case it will be passed on.
|
||||
2. Return a new packet (built with libsecondlife), in which case the
|
||||
new packet will substitute for the original. SLProxy will
|
||||
automatically copy the sequence number and appended ACKs from the
|
||||
old packet to the new one.
|
||||
3. Return null, in which case the packet will not be passed on.
|
||||
|
||||
SLProxy automatically takes care of ensuring that sequence numbers and
|
||||
acknowledgements are adjusted to account for changes made by the
|
||||
application. When replacing a reliable packet with an unreliable
|
||||
packet or removing a reliable packet, a fake acknowledgement is
|
||||
injected. When replacing an unreliable packet with a reliable packet,
|
||||
SLProxy ensures delivery and intercepts its acknowledgement. Note
|
||||
that if a reliable packet is passed on but then lost on the network,
|
||||
Second Life will resend it and the delegate will be called again. You
|
||||
can tell if a packet is being resent by checking if (packet.Data[0] &
|
||||
Helpers.MSG_RESENT) is nonzero, although be advised that it's possible
|
||||
that the original packet never made it to the proxy and the packet
|
||||
will be marked RESENT the first time the proxy ever sees it.
|
||||
|
||||
Note that all delegates must terminate (not go into an infinite loop),
|
||||
and must be thread-safe.
|
||||
|
||||
2.3 Packet injection
|
||||
- - - - - - - - - -
|
||||
|
||||
New packets may be injected into the stream at any point, either
|
||||
during a delegate callback or by another thread in your application.
|
||||
Packets are injected with the InjectPacket method:
|
||||
|
||||
void InjectPacket(Packet packet, Direction direction)
|
||||
|
||||
This will inject a packet heading to either the client or to the
|
||||
active server, when direction is Direction.Incoming or
|
||||
Direction.Outgoing, respectively. The packet's sequence number will
|
||||
be set automatically, and if the packet is reliable, SLProxy will
|
||||
ensure its delivery and intercept its acknowledgement.
|
||||
|
||||
Injecting a packet immediately upon (or prior to) connection is not
|
||||
recommended, since the client and the server won't have initialized
|
||||
their session yet.
|
||||
|
||||
2.4 Starting the proxy
|
||||
- - - - - - - - - - -
|
||||
|
||||
Once you've constructed a Proxy and added your delegates, you must
|
||||
start it with the Start method:
|
||||
|
||||
void Start()
|
||||
|
||||
Once started, the proxy will begin listening for connections. The
|
||||
Start method spawns new threads for the proxy and returns immediately.
|
||||
|
||||
3. PacketUtility class
|
||||
----------------------
|
||||
|
||||
The PacketUtility class provides a handful of static methods which may
|
||||
be useful when inspecting and modifying packets.
|
||||
|
||||
3.1 Hashtable Unbuild(Packet packet)
|
||||
- - - - - - - - - - - - - - - - - -
|
||||
|
||||
The Unbuild method takes a Packet object and returns a table of
|
||||
blocks, structured in a format suitable for passing to PacketUtility's
|
||||
GetField and SetField methods or PacketBuilder's BuildPacket method.
|
||||
|
||||
For example, this should make an approximate copy of a packet:
|
||||
|
||||
Hashtable packetBlocks = PacketUtility.Unbuild(packet);
|
||||
Packet packetCopy = PacketBuilder.BuildPacket(packet.Layout.Name, protocolManager, packetBlocks, packet.Data[0]);
|
||||
|
||||
3.2 object GetField(Hashtable blocks, string block, string field)
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
The GetField method takes a table of blocks (produced by
|
||||
PacketUtility.Unbuild) and extracts the value of a particular field.
|
||||
If the field is part of a variable block, an arbitrary instance of the
|
||||
field will be returned. If the field does not exist, null will be
|
||||
returned.
|
||||
|
||||
3.3 void SetField(Hashtable blocks, string block, string field, object value)
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
The SetField method takes a table of blocks (produced by
|
||||
PacketUtility.Unbuild) and sets the value of a particular field. If
|
||||
the field is part of a variable block, all instances of the field will
|
||||
be set. If the field does not exist, SetField will have no effect.
|
||||
|
||||
This can be used by a packet delegate method in conjunction with
|
||||
PacketUtility.Unbuild and PacketBuilder.BuildPacket to substitute a
|
||||
new packet that is a copy of the original packet with certain fields
|
||||
modified.
|
||||
|
||||
3.4 string VariableToString(byte[] field)
|
||||
- - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
The VariableToString method interprets a variable field in a packet as
|
||||
a UTF-8 encoded string. If conversion fails, an empty string is
|
||||
returned.
|
||||
|
||||
3.5 byte[] StringToVariable(string str)
|
||||
- - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
The StringToVariable method returns a variable field representing the
|
||||
UTF-8 encoding of a string. If conversion fails, an empty field is
|
||||
returned.
|
||||
1072
applications/SLProxy/SLProxy.cs
Normal file
1072
applications/SLProxy/SLProxy.cs
Normal file
File diff suppressed because it is too large
Load Diff
BIN
applications/SLProxy/XmlRpcCS.dll
Executable file
BIN
applications/SLProxy/XmlRpcCS.dll
Executable file
Binary file not shown.
1509
applications/SLProxy/keywords.txt
Normal file
1509
applications/SLProxy/keywords.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
applications/SLProxy/libsecondlife.dll
Executable file
BIN
applications/SLProxy/libsecondlife.dll
Executable file
Binary file not shown.
1
applications/SLProxy/protocol.txt
Normal file
1
applications/SLProxy/protocol.txt
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user