| Tasks |
Detailed steps |
| 1. Create a solution space |
- Create an empty directory called Demo in the IIS document space (i.e. under inetpubwwwroot).
- Create an empty directory called bin directly under the Demo directory.
- Make the demo virtual root an application.
|
| 2. Create a simple Web Service |
- Create a file in the Demo directory called Echo.asmx with the following content:
<%@ WebService Language="c#" Class="Echo" %>
using System;
using System.Web;
using System.Web.Services;
public class Echo : WebService
{
[WebMethod] public string YouSaid(string whatyousaid)
{
return “You said “ + whatyousaid;
}
}
- Browse to http://localhost/Demo/echo.asmx?wsdl to view the WSDL for this new Web service.
|
| 3. Create a test program for the Web Service |
- Create a proxy class source file for the Web service with the WSDL command-line tool:
wsdl http://localhost/Echo.asmx?wsdl
- Compile the proxy class:
csc /t:module Echo.cs
- Create a class with a Main function to call the Web service through the proxy:
using System;
public class Test
{
public static void Main(string [] args)
{
Echo e = new Echo();
Console.WriteLine(e.YouSaid("Hello there!"));
}
- Build the test program, linking in the proxy class:
csc /t:exe /addmodule:Echo.netmodule Test.cs
- Run the test program; the console window should show the message You said Hello there!
|
| 4. Create an empty SoapExtension |
- Create a new C# source file called SampleSoapExtension.cs with the following content:
using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
namespace SampleSoapExtension
{
public class SampleSoapExtensionClass : SoapExtension
{
public override Stream ChainStream(Stream stream)
{
return stream;
}
public override object GetInitializer(Type serviceType)
{
return null;
}
public override object GetInitializer(LogicalMethodInfo methodInfo,SoapExtensionAttribute attribute)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(System.Web.Services.Protocols.SoapMessage message)
{
}
}
}
- Compile the class into a dll:
csc /t:library /out:binSampleSoapExtension.dll SampleSoapExtension.cs
|
| 5. Create an attribute to apply the extension to the Web Service |
- Add the following class to SampleSoapExtension:
[AttributeUsage(AttributeTargets.Method)]
public class SampleSoapExtensionAttribute : SoapExtensionAttribute
{
private int priority;
public override Type ExtensionType
{
get
{
return typeof(SampleSoapExtensionClass);
}
}
public override int Priority
{
get
{
return priority;
}
set
{
priority = value;
}
}
}
- Rebuild the DLL.
|
| 6. Apply the SoapExtension to the Web Service |
- Add an ASP.NET Assembly directive specifying the namespace to Echo.asmx:
<%@ Assembly Name="SampleSoapExtension" %>
- Add a using directive referencing the SampleSoapExtension namespace to Echo.asmx:
using SampleSoapExtension;
- Add the decoration [SampleSoapExtension] to the method YouSaid.
- Run Test.exe (no need to rebuild). It should work exactly as it did before.
|
| 7. Add logging to the SoapExtension |
- Add the logMessage method to the SampleSoapExtension class.
private void logMessage(string header,string message)
{
FileStream fs = new FileStream("c:\\logs\\log.txt", FileMode.Append,FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
w.WriteLine("--- " + header);
w.WriteLine(message);
w.Flush();
w.Close();
}
- Add an invocation of logMessage(“Initialize”,””);
- Make sure that the logs directory exists and that Everyone has write access to it.
- Build the DLL and run the test program. You should have a log file with a single entry. This proves that the soap extension is being invoked.
|
| 8. Apply the SoapExtension to the client (Test) program |
- Create a new text file, named Test.exe.config
- Add the following lines to this file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin"/>
</assemblyBinding>
</runtime>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="SampleSoapExtension.SampleSoapExtensionClass, sampleSoapExtension" priority="0" group="1"/>
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
- Delete the log.txt file from the logs directory.
- Run the test program (no need to build anything). This time there will be two entries in the log.txt file—one from the client and one from the server
|
| 9. Make the SoapExtension actually do something |
- Add two private member variables to the SampleSoapExtensionClass:
private Stream inwardStream;
private Stream outwardStream;
- Replace the method body of ChainStream with:
outwardStream = stream;
inwardStream = new MemoryStream();
return inwardStream;
- Add the following method body to ProcessMessages:
StreamReader r;
StreamWriter w;
switch (message.Stage)
{
case SoapMessageStage.BeforeDeserialize:
logMessage("BeforeDeserialize",message.GetType().ToString());
r = new StreamReader(outwardStream);
w = new StreamWriter(inwardStream);
w.Write(r.ReadToEnd());
w.Flush();
inwardStream.Position = 0;
break;
case SoapMessageStage.AfterSerialize:
logMessage("AfterSerialize",message.GetType().ToString());
inwardStream.Position = 0;
r = new StreamReader(inwardStream);
w = new StreamWriter(outwardStream);
w.Write(r.ReadToEnd());
w.Flush();
break;
}
- Delete the log file, then build the dll and run the test program. You will see the following progression of calls:
Initialize AfterSerialize with a type of ClientSoapMessage Initialize BeforeDeserialize with a type of ServerSoapMessage AfterSerialize with a type of ServerSoapMessage BeforeDeserialize with a type of ClientSoapMessage |
| 10. Add the SOAP messages to the log file |
- In the ProcessMessages method of the SampleSoapExtensionClass, add two member variables:
string whatami;
string soapText;
- In the same method, delete the calls to logMessages
- Add the following line at the start of the BeforeDeserialize case:
whatami = (message is SoapClientMessage) ? "Response to Client" : "Request to Server";
- Add the following line at the start of the AfterSerialize case:
whatami = (message is SoapClientMessage) ? "Request from Client" : "Response from Server";
- Replace the line
w.write(r.ReadToEnd()); in both cases with:
soapText = r.ReadToEnd();
logMessage(whatami,soapText);
w.Write(soapText);
- Delete the log file, then rebuild the dll and run the test program. You will see the same progression of calls as in 9 above, but with clearer descriptions and the actual SOAP messages
|
| 11. |
- Add the following helper functions to the SampleSoapExtensionClass:
private void bumpup(ref StringBuilder instr)
{
for (int i=0; i<instr.Length; i++)
if (instr[i] >= 'a' && instr[i] < 'z')
instr[i] = Convert.ToChar(Convert.ToByte(instr[i])+1);
else if (instr[i] == 'z')
instr[i] = 'a';
}
private void bumpdown(ref StringBuilder instr)
{
for (int i=0; i<instr.Length; i++)
if (instr[i] > 'a' && instr[i] <= 'z')
instr[i] = Convert.ToChar(Convert.ToByte(instr[i])-1);
else if (instr[i] == 'a')
instr[i] = 'z';
}
- At the top of the file, add
using System.Text;
- Replace the declaration of soapText with
StringBuilder sb;
- Replace the three lines you added in 10e above in both cases with:
sb = new StringBuilder();
sb.Append(r.ReadToEnd());
logMessage("Unmodified "+whatami,sb.ToString());
logMessage("Modified "+whatami,sb.ToString());
w.Write(sb.ToString());
- In the BeforeDeserialize case, add
bumpup(ref sb); between the two logMessage calls. In the AfterSerialize case, add
bumpdown(ref sb);
- Delete the log file, then rebuild the dll and run the test program. You will see a pair of messages, one unmodified, one ummodified in place of the SOAP messages in 10 above. This demonstrates that obfuscated messages are being passed between the client and the server.
|