
مزيل تعتيم للشيفرة المصدرية (source-to-source) للغتَي VBScript وVBA مع تقييم جزئي (partial-evaluation)
vbSparkle هي أداة متعددة المنصات لإزالة التعتيم عن Visual Basic من المصدر إلى المصدر، مبنية على التقييم الجزئي، ومخصّصة بشكل أساسي لتحليل التعليمات البرمجية الخبيثة المكتوبة بلغة VBScript وVBA (ماكرو أوفيس).
هي مكتوبة بلغة C# الأصلية وتوفّر مكتبة .Net Standard، وتعمل على Windows وLinux وMacOS وغيرها.
يتم تحليل VBScript وVBA من خلال استخدام قواعد ومحللات lexer الخاصة بـ ANTLR.
Install-Package vbSparkle
dotnet add package vbSparkle
بمجرد إضافة مكتبة vbSparkle إلى مشروعك، كل ما عليك فعله هو استدعاء الدالة PrettifyEncoded من الصنف الثابت VbPartialEvaluator:
string obfuscatedVB = "msgbox(chr(65)&chr(66)&(chr(67))"
string deobfuscated = VbPartialEvaluator.PrettifyEncoded(obfuscatedVB);
... سيعيد MsgBox("ABC").
المشروع المرفق vbSparkle.CLI هو مثال على استخدام vbSparkle كواجهة أوامر.
المثال الحالي يأخذ إمّا مسارًا كوسيط أو بيانات ثنائية كاملة عبر StdIn، ويعيد النتيجة بعد إزالة التعتيم.

المشروع المرفق vbSparkle.Web هو مثال على استخدام vbSparkle داخل واجهة ويب.

تعتيم تعليمات VBScript وVBA البرمجية شائع بين المهاجمين، ويتيح لهم تجاوز إجراءات الكشف، ومضادات الفيروسات، وجدران الحماية، وأنظمة كشف الاستجابة للنقاط الطرفية (EDRs)، ويجعل تحليل البرمجيات الخبيثة أكثر صعوبة.
لذلك من الشائع رؤية VBScript معتّم في المرحلة الأولى جدًا من اختراق جهاز.
من تقنيات تعتيم أكواد VBScript النموذجية تغليف السكربت في سلسلة نصية معتّمة ثم تنفيذه عبر دالة Execute("...code..." ) بعد فك ترميزه / تقييمه.
لننظر إلى مثال:
Execute(chr( 456127/3833 ) & chr( 817650/7110 ) & chr( 759429/7671 ) & chr( 834138/7317 ) & chr( 57960/552 ) & chr( 2549-2437 ) & chr( 1078568/9298 ) & chr( 5143-5097 ) & chr( -8682+8783 ) & chr( 529-430 ) & chr( 673088/6472 ) & chr( 761682/6862 ) & chr( 302336/9448 ) & chr( 9427-9393 ) & chr( 410176/3944 ) & chr( -5271+5372 ) & chr( 796608/7376 ) & chr( 2896-2788 ) & chr( 318792/2872 ) & chr( 1076-1042 ) & vbcrlf )
هنا، استخدم المهاجم:
chr(CHAR_CODE) لتقييم حرف من السلسلة النصية (مثل chr(65) = "A")456127/3833) لتقييم قيمة الحرف& لتجميع جميع الأحرف المفكوكةExecute(..) لتنفيذ السكربت المفكوك.إذا تابعنا تنفيذ السكربت:
456127/3833 => 119chr(119) => "w"817650/7110 => 115chr(115) => "s""w" & "s" => "ws"Execute("ws.....هنا السكربت الأصلي:
wscript.echo "hello"
إزالة التعتيم اليدوية تصبح صعبة جدًا وتستغرق وقتًا طويلًا على السكربتات الكبيرة.
من تقنيات إزالة التعتيم الشائعة مع VBScript ببساطة استبدال دالة Execute() بـ WScript.Echo(، ثم تشغيل السكربت للحصول على المخرجات.
هذه التقنية الأخيرة بسيطة لكن لها بعض العيوب:
وفي الوقت الذي بدأ فيه هذا المشروع، لم تكن هناك بديل لهاتين التقنيتين.
التقييم الجزئي ليس مفهومًا جديدًا، فهو يُستخدم غالبًا في المترجمات لتحسين الكود أثناء عملية ما قبل الترجمة (مثل: https://en.wikipedia.org/wiki/Partial_evaluation).
يقرأ المُقيّم الجزئي برنامجًا (يسمى البرنامج الموضوع) مع بعض المدخلات لذلك البرنامج، ويُقيّم فقط الأجزاء من البرنامج التي تعتمد على المدخلات المقدَّمة. بمجرد تقييم جميع هذه الأجزاء، يتم إخراج البرنامج المتبقي (ويسمى البرنامج المتبقّي) كمخرجات.
على سبيل المثال، عملية ما قبل الترجمة ستحوّل الكود الموضوع التالي:
var1 = 3*3
var2 = sin(arg1)
var3 = var1 + func_xx(2 * var1 + var2)
إلى الكود المتبقّي التالي:
var2 = sin(arg1)
var3 = 9 + func_xx(18 + var2)
تم تقييم الكود جزئيًا قبل الترجمة لتحسين الأداء.
نرى أن هذه العملية تبسّط الكود وتزيل العمليات غير الضرورية. هذا مثير للاهتمام لأن التعتيم يخفي الكود بإضافة كود وعمليات غير ضرورية لتنفيذ الكود.
إذا فحصنا عيّنتنا الأولية خطوة بخطوة:
456127/3833 يمكن تقييمها جزئيًا: لا تأثير جانبيchr(119) يمكن تقييمها جزئيًا: 119 ثابت، وهذه دالة أصلية، لا تأثير جانبي"w" & "s" يمكن تقييمها جزئيًا: هي تسلسل أحرف ثابت، لا تأثير جانبي"Execute(" لها تأثير جانبي معروف (تنفيذ السكربت)، ولن يتم تقييمها، إلا إذا أمكن إدارة/محاكاة التأثيرات الجانبية للدوالالتقييم الجزئي لتلك العينة سينتج:
Execute("wscript.echo ""hello""" & vbCrLf)
وخمّن ماذا؟ لقد أزلنا التعتيم عن عيّنتنا للتو.
حسنًا، في التطبيق العملي، الأمر أكثر تعقيدًا بكثير:
لحسن الحظ، ANTLR هو مولد محللات قوي لقراءة ومعالجة وتنفيذ أو ترجمة النصوص المنظمة أو الملفات الثنائية. وهو معروف باستخدامه الواسع لبناء اللغات والأدوات والأطر. من القواعد النحوية، يولد ANTLR محللًا يمكنه بناء أشجار تحليل (parse trees) ويولّد أيضًا واجهة مستمع (listener) أو زائر (visitor) تسهّل الاستجابة للتعرف على العبارات المهمة.
يمكن لـ ANTLR توليد محللات معجمية، ومحللات، ومحللات أشجار، ومحللات معجمية-نحوية مدمجة. يمكن للمحللات أن تولّد تلقائيًا أشجار تحليل أو أشجار نحوية مجردة (abstract syntax trees) يمكن معالجتها أكثر بمحللات الأشجار. يوفّر ANTLR تدوينًا واحدًا متناسقًا لتحديد المحللات المعجمية والمحللات ومحللات الأشجار.
بشكل افتراضي، يقرأ ANTLR قواعد نحوية ويولّد أداة تعرّف (recognizer) للغة المحددة بواسطة تلك القواعد (أي برنامج يقرأ تدفق إدخال ويولّد خطأ إذا كان تدفق الإدخال لا يتوافق مع البنية المحددة بالقواعد).
من أجل القيام بشيء مفيد مع اللغة، يمكن إرفاق إجراءات (actions) بعناصر القواعد داخل القواعد النحوية. هذه الإجراءات مكتوبة بلغة البرمجة التي يتم توليد أداة التعرّف بها. عند توليد أداة التعرّف، تُضمَّن الإجراءات في الكود المصدري لأداة التعرّف في النقاط المناسبة.
بفضل ANTLR، كان توليد نسخة أولى من شجرة النحو المجردة (AST)، والمحلل المعجمي، والمحلل سريعًا وسهلًا بفضل قواعد نحوية موجودة كتبها Ulrich Wolffgang.
مثال سكة حديدية من قواعد Visual Basic النحوية:

هذه القواعد لم تكن تعمل بشكل كامل مع بعض صيغ Visual Basic المحددة (الكود المضمّن، بعض مشاكل المعاملات) وكانت هناك حاجة لبعض التعديلات لتوفير قواعد متوافقة تمامًا مع VBS وVBA. يمكن العثور على القواعد المعدّلة هنا: Visual Basic Grammar
فيما يلي مثال لشجرة تحليل مرئية لقطعة الكود التالية:
Msgbox("Hello world " & Chr(33), vbCritical, "The Title")

بمجرد توليد المحلل وشجرة النحو المجردة، يمكننا البدء في استخدامه واستكشاف الكود الخاص بنا:
using Antlr4.Runtime;
using System.Linq;
namespace vbSparkle
{
public class PartialEvaluator
{
public static string Prettify(string script)
{
var inputStream = new AntlrInputStream(script);
// Create the lexer
var lexer = new VBScriptLexer(inputStream);
// Instanciate of common token from lexer
var commonTokenStream = new CommonTokenStream(lexer);
// Create the parser
var parser = new VBScriptParser(commonTokenStream);
// Get the start rule (root element of our AST)
VBScriptParser.StartRuleContext stContext = parser.startRule();
// Instanciate the visitor
VbAnalyser analyser = new VbAnalyser();
// Visit the AST
analyser.Visit(stContext);
// Root AST element of a vbScript being a "Module", we get it
var module = analyser.Modules.FirstOrDefault();
// Then we partially evaluate our module
string result = module.Prettify(partialEvaluation: true);
return result;
}
}
}
الزائر (visitor) مسؤول عن عبور شجرة النحو المجردة، وإنشاء كل عقدة كمقيّم جزئي/مجمّل (prettifier).
هذه الأخيرة ستكون مسؤولة عن التقييم بشكل متكرر:

على سبيل المثال، إليك الزائر الخاص بنا، الذي ينشئ مُقيّم VbModule:
public class VbAnalyser
{
public List<VbModule> Modules { get; set; } = new List<VbModule>();
internal void Visit(VBScriptParser.StartRuleContext stContext)
{
var moduleContext = stContext.module();
VbModule module = new VbModule(this, moduleContext);
Modules.Add(module);
}
}
فيما يلي مثال لعقدة تحتوي على دالة Prettify:
public class VbOnGotoStatement : VbStatement<VBScriptParser.OnGoToStmtContext>
{
public VBValueStatement OnValue { get; set; }
public VBValueStatement[] GotoValues { get; set; }
public VbOnGotoStatement(IVBScopeObject context, VBScriptParser.OnGoToStmtContext bloc)
: base(context, bloc)
{
OnValue = VBValueStatement.Get(context, bloc.valueStmt().First());
GotoValues = bloc.valueStmt().Skip(1).Select(v => VBValueStatement.Get(context, v)).ToArray();
}
public override DExpression Prettify(bool partialEvaluation = false)
{
return new DCodeBlock($"On {OnValue.Prettify(partialEvaluation)} Goto {string.Join(", ", GotoValues.Select(v=> v.Prettify(partialEvaluation)))}");
}
}
عندما تتم زيارة العقدة عبر المُنشئ VbOnGotoStatement(، فإنها تزور العقد الفرعية بشكل متكرر وتنشئ مثيلات من الزوار الخاصة بها.
ثم، عند استدعاء دالة Prettify، يتم تجميل العناصر الفرعية بنفس مستوى التقييم الجزئي.