Chrono Mock
اللغةالعربية

كيف تعرف أنه نجح؟

مع أي أداة أخرى من هذا النوع لا تعرف. يبدأ التطبيق ويعرض تاريخًا ما، وأنت تفترض. لكنه قد يقرأ الساعة عبر طريق لم يعترضه شيء - وحينها لا يعني «نجح الاختبار» شيئًا على الإطلاق.

الإخفاق الذي لا عَرَض له

أداة إزاحة الوقت التي تفوّت قناة لا تنهار ولا تحذّر. فالتطبيق يقرأ التاريخ الحقيقي ببساطة عبر الدالة الوحيدة التي لم يربطها أحد، ويتصرف تمامًا كما كان سيتصرف في أي عصر يوم عادي. يصير الاختبار أخضر. ويصدر العطل.

وRunAsDate صريح بشأن هذا في وثائقه هو - إذ يقول إنه «لن يؤثر في التطبيقات التي تأخذ التاريخ/الوقت الحالي من مصدر آخر» - ثم يتركك لتتبيّن هل تطبيقك منها. وهذه الأداة تعامل الجملة نفسها على أنها شيء يُقاس.

ما تُبلّغ عنه الجلسة فعلًا

مخرجات حقيقية، من تشغيل الأداة على أحد مجسّات الاختبار الخاصة بهذا المشروع - مجسّ يفتح اتصالًا بالشبكة عمدًا:

$ chrono run "...\pnet-x64.exe" --at 2038-01-19T03:14:07 --report session.txt

Chrono Mock - session report
  target:   C:\ChronoMock\tools\probes\bin\pnet-x64.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:10
            real elapsed 3.4s, fake elapsed 3.4s
  exited:   the target closed itself with code 0
  covered channels (substituted, with call counts, 4 calls in all):
            - pid 9152: GetSystemTimeAsFileTime (1 call)
            - pid 9152: NtQuerySystemTime (3 calls)
            ... every other channel, including the ones never called
  observed channels (hooked but left real):
            - pid 9152: NtCreateUserProcess (0 calls)
            - pid 9152: connect (1 call)
  warnings:
            - the target opened a network connection - it may read time from a
              server, which no local hook can cover (source.network_at_start)
  requested:  2038-01-19T03:14:07 (zone +02:00 (host default), mode flow)

الكتلتان الأخيرتان هما المقصود. لم يفشل شيء هنا - فالحكم يعمل - ومع ذلك يقول التقرير، من تلقاء نفسه، إن هذا التطبيق تحدّث إلى الشبكة قبل أن ينتهي، فقد لا يكون الخطّاف المحلي القصة كلها. وهذه الجملة هي الفرق بين اختبار يمكنك الوثوق به وآخر لا يمكنك.

شاشة نتيجة Chrono Mock وقسم What the application read مفتوح: جدول بأعمدة Function وRead from وReads. تُعلَّم NtCreateUserProcess بأنها تقرأ من الساعة الحقيقية بحكم التصميم، وتُعلَّم connect بأنها تقرأ من الساعة الحقيقية بحكم التصميم وربطت متأخرة، بينما تقرأ بقية الصفوف - GetSystemTimeAsFileTime وNtQuerySystemTime وGetTimeZoneInformation وغيرها - من الساعة المزيّفة، ولكل منها عدده. وتحت الجدول تحذيران.
الجواب نفسه في النافذة. كل دالة كان في وسع التطبيق أن يسأل عنها، وما قرأه منها فعلًا، وكم مرة - بما فيها ما تُرك على الساعة الحقيقية عمدًا، وهذه تُسمّى ولا تُحسب بصمت نجاحًا.

أربعة أحكام، ولكل منها رمز خروج

يُتوصَّل إلى الحكم من داخل العملية الجارية، قبل أن تبدأ عملك، وهو رمز خروج الأداة نفسها فيستطيع خط الأنابيب أن يتفرّع عليه.

الحكمالخروجالمعنى
يعمل0كل عملية قرأت الوقت رأت ساعة الجلسة
يعمل جزئيًا10بعض القنوات مُغطّاة وبعضها لا - وغير المُغطّاة مدرجة
لا يعمل11لم يصل الاستبدال إلى القنوات الأساسية
غير محدَّد4تعذّر إثبات التغطية - «لا أعرف» صادقة

حين يكون الحكم «لا يعمل» تُوقَف العملية فورًا، قبل أن تتفاعل معها. فالحكم لا يمكن أن يسبق الإطلاق، لأن مصدر الحقيقة الوحيد قراءة تؤخذ من داخل العملية الجارية. والمضمون هو أن تعرف الحقيقة قبل أن تبدأ العمل - لا أن إطلاقًا محكومًا بالفشل لن يحدث. ويمكنك التجاوز والمتابعة، وتُوسم الجلسة حينئذٍ بأنها غير موثوقة في كل موضع تظهر فيه، بما في ذلك أي أدلة تصدّرها.

رموز أخرى تقول إن ما فشل هو التشغيل نفسه لا الاستبدال - خطأ في الاستخدام، أو هدف تعذّر تشغيله، أو تشغيل قُطع في منتصفه، أو جلسة أخرى تعمل بالفعل - ولا شيء منها حكم. الجدول الكامل في مرجع سطر الأوامر.

مُغطّاة، ومرصودة، وغير مُغطّاة

تنقسم القنوات إلى ثلاث فئات، والوسطى هي الأكثر إثارة.

  • مُغطّاة - رُبطت واستُبدلت. سأل التطبيق فحصل على ساعة الجلسة. ويقول عدد الاستدعاءات كم مرة.
  • مُراقَبة - تُعترض وتُعدّ وتُترك عمدًا تقول الحقيقة. هنا انتظارات الكائنات، لأن تقصير مهلتها قد يزيّف انتهاء مهلة على مقبض حقيقي، ومؤقّت الوسائط المتعددة، لأن تحجيمه سيزيح توقيت الصوت وMIDI - وكلاهما يُراقَب عندما تحجّم الجلسة محور الوقت المنقضي. واتصالات الشبكة ومسار واحد لإنشاء العمليات هنا في كل جلسة، لأنها أدلة لا ساعات. أما العدّاد عالي الدقة فليس في هذه المجموعة: يبقى كما هو ما لم تطلبه بـ --scale-qpc، وعندها يُغطّى.
  • غير مُغطّاة - قناة قرأها التطبيق ولم يصل إليها الاستبدال. وهذا ما يحوّل الحكم إلى يعمل جزئيًا، وهي مدرجة بالاسم ولا تُلخَّص حتى تختفي.

والعدد صفر معلومة أيضًا: القناة كانت مربوطة ولم يسأل التطبيق عنها قط. ولهذا يسردها التقرير بدل أن يخفيها.

ما لا يستطيع التدقيق رؤيته

التقرير صادق في شأن التغطية، وهذا يعني أنه صادق في شأن حدوده هو. فهذه تُقرأ دون أي استدعاء دالة يمكن اعتراضه، أو خارج العملية كليًا:

  • القراءات المباشرة لصفحة بيانات وضع المستخدم المشتركة - فالقيمة في الذاكرة، فلا شيء يُربط. نادرة في التطبيقات العادية، وشائعة في شيفرة الترخيص ومقاومة العبث
  • الاستدعاءات المباشرة للنظام التي تتجاوز الدوال المُصدَّرة القياسية - النمط نفسه
  • الوقت المأخوذ من خارج العملية، عبر WMI أو خادم COM خارج العملية أو RPC
  • الوقت القادم من الشبكة - ترويسة HTTP باسم Date، أو NTP، أو استجابة API، أو ساعة خادم قاعدة البيانات نفسه
  • الوقت القادم من برامج تشغيل النواة

وحالة الشبكة هي التي يستطيع التدقيق على الأقل أن ينتبه إليها، ولهذا يوجد التحذير في التقرير أعلاه. أما في بقية الحالات فالحكم يعمل يعني أن القنوات التي تستطيع هذه الأداة رؤيتها قد غُطّيت - لا أنه لا يوجد طريق آخر.

في خط الأنابيب

يُخرج --json الوقائع نفسها بصيغة الآلة، ويكتب --report النسخة المكتوبة للبشر في ملف، والجلسة التي حكمها غير يعمل يبدأ ملفها بشريط تحذير من أدلة غير موثوقة بدل دفنه في الآخر.

chrono run "C:\apps\test.exe" --at 2027-12-31T23:59:59 --json --report evidence.txt

جرّبه