程式碼產生器

寫程式也寫了四五年了,每天這樣下來總覺得好像機器人一樣,寫來寫去都差不多一樣的東西,麻痺到自己都感覺自己跟剛畢業的學生的程度差不了多少,回想當初為什麼會開始寫程式,就是想當駭客,於是就冒著不怕死的危險,跑到中國的黑客教學網下載了一堆東西來研究,當然啦結果就像我想像的一樣駭客沒當成反而成了害客,但是也不是全然沒有收穫,其中一項就是我想要介紹的東西【 程式碼產生器】(這個很白話了,但是我還是解釋一下,就是寫一個程式,這個程式的用途是幫人家寫程式) 。
為什麼會想到寫【 程式碼產生器】?就是因為之前有下載一套軟體,那個軟體只要設定一些參數,下一些簡單的dos語法,就會幫你包裝出一個攻擊性的木馬,好了廢話不多說,下面就開始介紹如何簡單的實作出程式碼產生器。

1. 首先介紹的是如何產生Nampspace(專有名詞,在下面的程式都不再另外介紹)

//使用C#程式碼產生器
Microsoft.CSharp.CSharpCodeProvider csprovider = new Microsoft.CSharp.CSharpCodeProvider();

//提供一個CodeDom程式圖形的容器
CodeCompileUnit ccunit = new CodeCompileUnit();

//命名空間的宣告
CodeNamespace cnamespace = new CodeNamespace("MyNamespace");
//加入參考
cnamespace.Imports.Add(new CodeNamespaceImport("System"));
ccunit.Namespaces.Add(cnamespace); 


2. 類別、結構、介面、列舉型別的型別宣告

//類別、結構、介面、列舉型別的型別宣告
CodeTypeDeclaration ctype = new CodeTypeDeclaration("AutoProgram");
//取得型別的基底型別 MyNamespace.CodeDom.MyBase
//ctype.BaseTypes.Add("MyNamespace.CodeDom.MyBase");
//取得型別成員的註解集合
ctype.Comments.Add(new CodeCommentStatement("程式碼產生器自動產生", false));
ccunit.Namespaces[0].Types.Add(ctype);


3. 欄位、屬性與建構子宣告

//欄位宣告
CodeMemberField cmfield = new CodeMemberField("System.String", "_text");
cmfield.Attributes = MemberAttributes.Private;
//cmfield.Type = new CodeTypeReference("System.String");
//cmfield.Name = "_text";
//cmfield.InitExpression = new System.CodeDom.CodeObjectCreateExpression("1", new System.CodeDom.CodeExpression[] { });
ctype.Members.Add(cmfield);


//屬性宣告
CodeMemberProperty cmproperty = new CodeMemberProperty();
cmproperty.Name = "Text";
cmproperty.Attributes = MemberAttributes.Public;
cmproperty.Type = new CodeTypeReference("System.String");
cmproperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_text")));
cmproperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_text"), new CodePropertySetValueReferenceExpression()));
ctype.Members.Add(cmproperty);


//建構子宣告
CodeConstructor cconstructor = new CodeConstructor();
cconstructor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "Text"), new CodeVariableReferenceExpression("\"HelloWord\"")));
cconstructor.Attributes = MemberAttributes.Public;
ctype.Members.Add(cconstructor);


4. 方法宣告(分成有參數與無參數)
    a. 有參數

//表示型別方法的宣告
CodeMemberMethod cmmethod = new CodeMemberMethod();
cmmethod.Name = "HelloWord";
//共用的方法
cmmethod.Attributes = MemberAttributes.Public;
//回傳的資料型態
cmmethod.ReturnType = new CodeTypeReference("System.String");
//參數
cmmethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "text"));
//回傳值
cmmethod.Statements.Add(new CodeMethodReturnStatement(new CodeArgumentReferenceExpression("text")));
ctype.Members.Add(cmmethod);

    b. 無參數

//表示型別方法的宣告
CodeMemberMethod cmmethod = new CodeMemberMethod();
cmmethod.Name = "HelloWord";
//共用的方法
cmmethod.Attributes = MemberAttributes.Public;
//回傳的資料型態
cmmethod.ReturnType = new CodeTypeReference("System.String");
//回傳值
cmmethod.Statements.Add(new CodeMethodReturnStatement(new CodeArgumentReferenceExpression("this._text")));
ctype.Members.Add(cmmethod);


5. 特殊方法(Try ... Catch ... Finally)

//方法宣告(加入Try Catch)
CodeMemberMethod cmmethod = new CodeMemberMethod();
cmmethod.Name = "HelloWord";
cmmethod.Attributes = MemberAttributes.Public;
cmmethod.ReturnType = new CodeTypeReference("System.String");

CodeTryCatchFinallyStatement ctrycatch = new CodeTryCatchFinallyStatement();
CodeConditionStatement ccstatement = new CodeConditionStatement();

//try子句
ccstatement.Condition = new CodeVariableReferenceExpression("this._text != null");
//下面兩種回傳方式都可
ccstatement.TrueStatements.Add(new CodeMethodReturnStatement(new CodeArgumentReferenceExpression("this._text")));
ccstatement.FalseStatements.Add(new CodeVariableReferenceExpression("return \"Error\""));
ctrycatch.TryStatements.Add(ccstatement);

//catch子句
CodeCatchClause ccatch = new CodeCatchClause("ex", new CodeTypeReference("System.ApplicationException"));
ccatch.Statements.Add(new CodeVariableReferenceExpression("throw ex"));
ctrycatch.CatchClauses.Add(ccatch);

//finall子句
//ctrycatch.FinallyStatements.Add(new CodeVariableReferenceExpression(""));

cmmethod.Statements.Add(ctrycatch);
ctype.Members.Add(cmmethod);


6. 轉出Source Code (.cs檔)

//定義產生程式碼的介面
System.CodeDom.Compiler.ICodeGenerator generator = csprovider.CreateGenerator(filename + ".cs");

//提供可以使用定位點字串(String)語彙基元(Token)縮行新排的文字寫入器
System.CodeDom.Compiler.IndentedTextWriter writer = new System.CodeDom.Compiler.IndentedTextWriter(new System.IO.StreamWriter(filename + ".cs"), Environment.NewLine);
generator.GenerateCodeFromCompileUnit(ccunit, writer, new System.CodeDom.Compiler.CodeGeneratorOptions());


7. Build dll檔

System.CodeDom.Compiler.ICodeCompiler compiler = csprovider.CreateCompiler();

//Compiler
System.CodeDom.Compiler.CompilerParameters cparameters = new System.CodeDom.Compiler.CompilerParameters();
cparameters.OutputAssembly = filename + ".dll";
//加入需要的參考
cparameters.ReferencedAssemblies.Add("System.dll");
cparameters.GenerateInMemory = false;
cparameters.TreatWarningsAsErrors = true;
cparameters.WarningLevel = 3;
System.CodeDom.Compiler.CompilerResults cr = compiler.CompileAssemblyFromDom(cparameters, ccunit);


此範例只是一個簡單的範例,已上的語法就可以動態產生出需要的dll檔,如果需要像本文一開始說的產生出一個執行檔讓人加設定,就需要更完整的程式碼進行編譯(如程式進入點,Winform畫面),但這就是一個大工程了,在還沒有這麼多美國時間之前我就先寫到這了,另外下面介紹一下該用另外一支AP使用Reflection(映射)的方式來呼叫此dll檔的方法。

InvokeMember函式說明請參MSDN
public object InvokeMember(string, BindingFlags, Binder, object, object[]);
    string : 要呼叫的函式名稱
    BindingFlags : 要調用的函式屬性,可多選
    Binder : null就會使用默認的Binding對像
    object : 要叫用指定成員的 Object
    object[] : 是否有參數(若無可以為空值)


//呼叫dll
private void button_Call_Click(object sender, EventArgs e)
{
    try
    {
        string filename = this.textBox_Path.Text.Trim() + @"\" + this.textBox_Name.Text.Trim() + ".dll";
        if (System.IO.File.Exists(filename))
        {
            string str = "";

            Assembly assembly = System.Reflection.Assembly.LoadFile(filename);
            Type type = assembly.GetType("MyNamespace.AutoProgram");
            object instance = Activator.CreateInstance(type);

            if (!string.IsNullOrEmpty(this.textBox_CallText.Text.Trim()))
            {
                object[] arguments = new object[] { this.textBox_CallText.Text.Trim() };

                str = type.InvokeMember("HelloWord",
                        BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, 
                        null, 
                        instance,
                        arguments
                    ).ToString();
                MessageBox.Show("您呼叫的是有參數的方法,回傳值 : " + str);
            }
            else
            {
                object[] arguments = new object[0];

                str = type.InvokeMember("HelloWord",
                        BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
                        null,
                        instance,
                        arguments
                    ).ToString(); 
                MessageBox.Show("您呼叫的是沒參數的方法,回傳值 : " + str);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
} 


本文附件 : 

留言