Adding SLProxy project.

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@74 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
axial
2006-07-24 03:03:45 +00:00
parent 85274f024d
commit a4f80b3e7e
9 changed files with 3028 additions and 0 deletions

View 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" : " ")
+ "]"
;
}
}

View 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;
}
}

View 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
View 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.

File diff suppressed because it is too large Load Diff

BIN
applications/SLProxy/XmlRpcCS.dll Executable file

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long