Added support for v4 framework only and made script generation more generic.

This commit is contained in:
James Forshaw
2017-05-16 13:57:04 +01:00
parent 05df0921cf
commit bf6601e546
7 changed files with 336 additions and 132 deletions
+3
View File
@@ -44,8 +44,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="IScriptGenerator.cs" />
<Compile Include="JScriptGenerator.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VBAGenerator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
+38
View File
@@ -0,0 +1,38 @@
// This file is part of DotNetToJScript - A tool to generate a
// JScript which bootstraps an arbitrary v2.NET Assembly and class.
// Copyright (C) James Forshaw 2017
//
// DotNetToJScript is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// DotNetToJScript is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with DotNetToJScript. If not, see <http://www.gnu.org/licenses/>.
namespace DotNetToJScript
{
enum RuntimeVersion
{
None,
v2,
v4,
Auto,
}
interface IScriptGenerator
{
string GenerateScript(byte[] serialized_object,
string entry_class_name,
string additional_script,
RuntimeVersion version,
bool enable_debug);
bool SupportsScriptlet { get; }
string ScriptName { get; }
}
}
+127
View File
@@ -0,0 +1,127 @@
// This file is part of DotNetToJScript - A tool to generate a
// JScript which bootstraps an arbitrary v2.NET Assembly and class.
// Copyright (C) James Forshaw 2017
//
// DotNetToJScript is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// DotNetToJScript is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with DotNetToJScript. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Text;
namespace DotNetToJScript
{
class JScriptGenerator : IScriptGenerator
{
static string jscript_template =
@"var serialized_obj = [
%SERIALIZED%
];
var entry_class = '%CLASS%';
try {
setversion();
var stm = new ActiveXObject('System.IO.MemoryStream');
var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter');
var al = new ActiveXObject('System.Collections.ArrayList')
for (i in serialized_obj) {
stm.WriteByte(serialized_obj[i]);
}
stm.Position = 0;
var n = fmt.SurrogateSelector;
var d = fmt.Deserialize_2(stm);
al.Add(n);
var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class);
%ADDEDSCRIPT%
} catch (e) {
debug(e.message);
}";
static string version_detection = @"function setversion() {
var shell = new ActiveXObject('WScript.Shell');
ver = 'v4.0.30319';
try {
shell.RegRead('HKLM\\SOFTWARE\\Microsoft\\.NETFramework\\v4.0.30319\\');
} catch(e) {
ver = 'v2.0.50727';
}
shell.Environment('Process')('COMPLUS_Version') = ver;
}";
static string version_v2 = @"function setversion() {
new ActiveXObject('WScript.Shell').Environment('Process')('COMPLUS_Version') = 'v2.0.50727';
}";
static string version_v4 = @"function setversion() {
new ActiveXObject('WScript.Shell').Environment('Process')('COMPLUS_Version') = 'v4.0.30319';
}";
static string version_none = @"function setversion() {}";
static string debug_enabled = @"function debug(s) { WScript.Echo(s); }" + Environment.NewLine;
static string debug_disabled = @"function debug(s) { }" + Environment.NewLine;
static string GetSetVersionJScript(RuntimeVersion version)
{
string script = version_none;
switch (version)
{
case RuntimeVersion.Auto:
script = version_detection;
break;
case RuntimeVersion.v2:
script = version_v2;
break;
case RuntimeVersion.v4:
script = version_v4;
break;
}
return script + Environment.NewLine;
}
public string ScriptName
{
get
{
return "JScript";
}
}
public bool SupportsScriptlet
{
get
{
return true;
}
}
public string GenerateScript(byte[] serialized_object, string entry_class_name, string additional_script, RuntimeVersion version, bool enable_debug)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < serialized_object.Length; ++i)
{
builder.Append(serialized_object[i]);
if (i < serialized_object.Length - 1)
{
builder.Append(",");
}
if (i > 0 && (i % 32) == 0)
{
builder.AppendLine();
}
}
return (enable_debug ? debug_enabled : debug_disabled) + GetSetVersionJScript(version)
+ jscript_template.Replace("%SERIALIZED%", builder.ToString()).Replace("%CLASS%", entry_class_name).Replace("%ADDEDSCRIPT%", additional_script);
}
}
}
+46 -125
View File
@@ -27,78 +27,10 @@ using System.Text;
using System.Xml;
using System.Xml.Schema;
namespace Serialize
namespace DotNetToJScript
{
class Program
{
static string jscript_template =
@"
var serialized_obj = [
%SERIALIZED%
];
var entry_class = '%CLASS%';
try {
var stm = new ActiveXObject('System.IO.MemoryStream');
var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter');
var al = new ActiveXObject('System.Collections.ArrayList')
for (i in serialized_obj) {
stm.WriteByte(serialized_obj[i]);
}
stm.Position = 0;
var n = fmt.SurrogateSelector;
var d = fmt.Deserialize_2(stm);
al.Add(n);
var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class);
%ADDEDSCRIPT%
} catch (e) {
WScript.Echo(e.message);
}";
static string vba_template =
@"
Private Function decodeHex(hex)
On Error Resume Next
Dim DM, EL
Set DM = CreateObject(""Microsoft.XMLDOM"")
Set EL = DM.createElement(""tmp"")
EL.DataType = ""bin.hex""
EL.Text = hex
decodeHex = EL.NodeTypedValue
End Function
Function Run()
Dim serialized_obj
%SERIALIZED%
entry_class = ""%CLASS%""
Dim stm As Object, fmt As Object, al As Object
Set stm = CreateObject(""System.IO.MemoryStream"")
Set fmt = CreateObject(""System.Runtime.Serialization.Formatters.Binary.BinaryFormatter"")
Set al = CreateObject(""System.Collections.ArrayList"")
Dim dec
dec = decodeHex(serialized_obj)
For Each i In dec
stm.WriteByte i
Next i
stm.Position = 0
Dim n As Object, d As Object, o As Object
Set n = fmt.SurrogateSelector
Set d = fmt.Deserialize_2(stm)
al.Add n
Set o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class)
%ADDEDSCRIPT%
End Function
";
static string scriptlet_template =
@"<?xml version='1.0'?>
<package>
@@ -112,6 +44,11 @@ End Function
</component>
</package>
";
enum ScriptLanguage
{
JScript,
VBA,
}
static object BuildLoaderDelegate(byte[] assembly)
{
@@ -151,7 +88,7 @@ End Function
const string DEFAULT_ENTRY_CLASS_NAME = "TestClass";
static string CreateScriptlet(string script, bool register_script)
static string CreateScriptlet(string script, string script_name, bool register_script)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(scriptlet_template);
@@ -162,7 +99,7 @@ End Function
XmlNode root_node = doc.SelectSingleNode(register_script ? "/package/component/registration" : "/package/component");
XmlNode script_node = root_node.AppendChild(doc.CreateElement("script"));
script_node.Attributes.Append(doc.CreateAttribute("language")).Value = "JScript";
script_node.Attributes.Append(doc.CreateAttribute("language")).Value = script_name;
script_node.AppendChild(doc.CreateCDataSection(script));
using (MemoryStream stm = new MemoryStream())
@@ -204,6 +141,16 @@ End Function
{
WriteError(String.Format(format, args));
}
static string GetEnumString(Type enum_type)
{
return String.Join(", ", Enum.GetNames(enum_type));
}
static void ParseEnum<T>(string name, out T value) where T : struct
{
value = (T)Enum.Parse(typeof(T), name, true);
}
static void Main(string[] args)
{
@@ -211,17 +158,19 @@ End Function
{
if (Environment.Version.Major != 2)
{
WriteError("This tool only works on v2 of the CLR");
WriteError("This tool should only be run on v2 of the CLR");
Environment.Exit(1);
}
string output_file = null;
string entry_class_name = DEFAULT_ENTRY_CLASS_NAME;
string script_file = null;
string additional_script = String.Empty;
bool mscorlib_only = false;
bool scriptlet_moniker = false;
bool scriptlet_uninstall = false;
bool vba_code = false;
bool enable_debug = false;
RuntimeVersion version = RuntimeVersion.None;
ScriptLanguage language = ScriptLanguage.JScript;
bool show_help = false;
@@ -229,10 +178,15 @@ End Function
{ "n", "Build a script which only uses mscorlib.", v => mscorlib_only = v != null },
{ "m", "Build a scriptlet file in moniker format.", v => scriptlet_moniker = v != null },
{ "u", "Build a scriptlet file in uninstall format.", v => scriptlet_uninstall = v != null },
{ "v", "Build a VBA file.", v => vba_code = v != null },
{ "d", "Enable debug output from script", v => enable_debug = v != null },
{ "l|lang=", String.Format("Specify script language to use ({0})",
GetEnumString(typeof(ScriptLanguage))), v => ParseEnum(v, out language) },
{ "v", "Build a VBA file (use -lang switch).", v => language = ScriptLanguage.VBA },
{ "ver=", String.Format("Specify .NET version to use ({0})",
GetEnumString(typeof(RuntimeVersion))), v => ParseEnum(v, out version) },
{ "o=", "Specify output file (default is stdout).", v => output_file = v },
{ "c=", String.Format("Specify entry class name (default {0})", entry_class_name), v => entry_class_name = v },
{ "s=", "Specify file with additional script. 'o' is created instance.", v => script_file = v },
{ "s=", "Specify file with additional script. 'o' is created instance.", v => additional_script = File.ReadAllText(v) },
{ "h|help", "Show this message and exit", v => show_help = v != null },
};
@@ -246,10 +200,17 @@ End Function
Environment.Exit(1);
}
if (vba_code && (scriptlet_moniker || scriptlet_uninstall))
IScriptGenerator generator;
switch (language)
{
WriteError("Cannot use '-v' in combination with scriptlet options.");
Environment.Exit(1);
case ScriptLanguage.JScript:
generator = new JScriptGenerator();
break;
case ScriptLanguage.VBA:
generator = new VBAGenerator();
break;
default:
throw new ArgumentException("Invalid script language option");
}
byte[] assembly = File.ReadAllBytes(assembly_path);
@@ -284,54 +245,14 @@ End Function
MemoryStream stm = new MemoryStream();
fmt.Serialize(stm, mscorlib_only ? BuildLoaderDelegateMscorlib(assembly) : BuildLoaderDelegate(assembly));
byte[] ba = stm.ToArray();
string template;
StringBuilder builder = new StringBuilder();
if (vba_code)
{
template = vba_template;
string hex_encoded = BitConverter.ToString(ba).Replace("-", "");
for (int i = 0; i < hex_encoded.Length; i++)
{
if (i == 0)
{
builder.Append(" serialized_obj = \"");
}
else if (i % 100 == 0)
{
builder.Append("\"");
builder.AppendLine();
builder.Append(" serialized_obj = serialized_obj & \"");
}
builder.Append(hex_encoded[i]);
}
builder.Append("\"");
} else {
template = jscript_template;
for (int i = 0; i < ba.Length; ++i)
{
builder.Append(ba[i]);
if (i < ba.Length - 1)
{
builder.Append(",");
}
if (i > 0 && (i % 32) == 0)
{
builder.AppendLine();
}
}
}
string script = template.Replace("%SERIALIZED%", builder.ToString()).Replace("%CLASS%", entry_class_name).Replace("%ADDEDSCRIPT%", File.Exists(script_file) ? File.ReadAllText(script_file) : "");
string script = generator.GenerateScript(stm.ToArray(), entry_class_name, additional_script, version, enable_debug);
if (scriptlet_moniker || scriptlet_uninstall)
{
script = CreateScriptlet(script, scriptlet_uninstall);
if (generator.SupportsScriptlet)
{
throw new ArgumentException(String.Format("{0} generator does not support Scriptlet output", generator.ScriptName));
}
script = CreateScriptlet(script, generator.ScriptName, scriptlet_uninstall);
}
if (!String.IsNullOrEmpty(output_file))
+112
View File
@@ -0,0 +1,112 @@
// This file is part of DotNetToJScript - A tool to generate a
// JScript which bootstraps an arbitrary v2.NET Assembly and class.
// Copyright (C) James Forshaw 2017
//
// DotNetToJScript is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// DotNetToJScript is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with DotNetToJScript. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Text;
namespace DotNetToJScript
{
class VBAGenerator : IScriptGenerator
{
static string vba_template =
@"
Private Function decodeHex(hex)
On Error Resume Next
Dim DM, EL
Set DM = CreateObject(""Microsoft.XMLDOM"")
Set EL = DM.createElement(""tmp"")
EL.DataType = ""bin.hex""
EL.Text = hex
decodeHex = EL.NodeTypedValue
End Function
Function Run()
Dim serialized_obj
%SERIALIZED%
entry_class = ""%CLASS%""
Dim stm As Object, fmt As Object, al As Object
Set stm = CreateObject(""System.IO.MemoryStream"")
Set fmt = CreateObject(""System.Runtime.Serialization.Formatters.Binary.BinaryFormatter"")
Set al = CreateObject(""System.Collections.ArrayList"")
Dim dec
dec = decodeHex(serialized_obj)
For Each i In dec
stm.WriteByte i
Next i
stm.Position = 0
Dim n As Object, d As Object, o As Object
Set n = fmt.SurrogateSelector
Set d = fmt.Deserialize_2(stm)
al.Add n
Set o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class)
%ADDEDSCRIPT%
End Function
";
public string ScriptName
{
get
{
return "VBA";
}
}
public bool SupportsScriptlet
{
get
{
return false;
}
}
public string GenerateScript(byte[] serialized_object, string entry_class_name, string additional_script, RuntimeVersion version, bool enable_debug)
{
if (version != RuntimeVersion.None)
{
throw new ArgumentException("VBA output does not support version detection");
}
string hex_encoded = BitConverter.ToString(serialized_object).Replace("-", "");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < hex_encoded.Length; i++)
{
if (i == 0)
{
builder.Append(" serialized_obj = \"");
}
else if (i % 100 == 0)
{
builder.Append("\"");
builder.AppendLine();
builder.Append(" serialized_obj = serialized_obj & \"");
}
builder.Append(hex_encoded[i]);
}
builder.Append("\"");
return vba_template.Replace("%SERIALIZED%", builder.ToString()).Replace("%CLASS%", entry_class_name).Replace("%ADDEDSCRIPT%", additional_script);
}
}
}
+3
View File
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
+7 -7
View File
@@ -1,5 +1,5 @@
This file is part of DotNetToJScript - A tool to generate a
JScript which bootstraps an arbitrary v2.NET Assembly and class.
JScript which bootstraps an arbitrary .NET Assembly and class.
Copyright (C) James Forshaw 2017
DotNetToJScript is free software: you can redistribute it and/or modify
@@ -18,11 +18,10 @@ along with DotNetToJScript. If not, see <http://www.gnu.org/licenses/>.
Usage Notes:
This only works from full trust JScript(obviously), so should work in
scriptlets etc. It also only works if v2/v3/v3.5 is installed. If
only v4+ is installed then the system will prompt for installation of
.NET v2. Don't think there's any way around this, seems that while the
COM objects are registered it doesn't fall-forward to using .NET 4 even
though it should work in theory.
scriptlets etc. By default it will only works if v2/v3/v3.5 is installed.
However if you specify the '-ver auto' switch when building the output it
will also work on v4+ only, however that will introduce a dependency on
WScript.Shell which you might not want.
To use this you'll need to create an assembly which targets .NET 2 (though
in most cases you can also use 3.5 as you don't tend to see .NET 2 installed
@@ -59,7 +58,8 @@ o.DoSomething("SomeArg");
The default mode is to output a JScript file which can be executed in Windows
Scripting Host. However if you want a scriptlet pass either -m (for a scriptlet
which can be used from a scriptlet moniker) or -u (for a scriptlet which can be
used from regsvr32).
used from regsvr32). You can also specify the -v switch to output a VBA file which
should work in Office Macros.
Finally by default the tool will output to stdout, you can output direct to a file
using the -o switch.