memoryjs
عبارة عن حزمة NPM لقراءة وكتابة ذاكرة العملية!
الميزات • البدء • الاستخدام • التوثيق • التصحيح
المهام:
هذه وظيفة إضافية للعقدة (تم اختبارها آخر مرة للعمل على v14.15.0
) وبالتالي تتطلب استخدام Node-gyp.
قد تحتاج أيضًا إلى اتباع هذه الخطوات لتثبيت node-gyp
وإعداده.
npm install memoryjs
عند استخدام Memoryjs، يجب أن تتطابق العملية المستهدفة مع بنية النظام الأساسي لإصدار Node قيد التشغيل. على سبيل المثال، إذا كنت تريد استهداف عملية 64 بت، فيجب عليك تجربة استخدام إصدار 64 بت من Node.
تحتاج أيضًا إلى إعادة ترجمة المكتبة واستهداف النظام الأساسي الذي تريده. توجه إلى دليل الوحدة النمطية لعقدة Memoryjs، وافتح الوحدة الطرفية وقم بتشغيل أحد البرامج النصية التالية للتجميع:
# will automatically compile based on the detected Node architecture
npm run build
# compile to target 32 bit processes
npm run build32
# compile to target 64 bit processes
npm run build64
إذا كنت تخطط لاستخدام هذه الوحدة مع Node Webkit أو Electron، فقم بإلقاء نظرة على ملاحظات البناء الخاصة بـ Liam Mitchell هنا.
const memoryjs = require('memoryjs');
const processName = "csgo.exe";
// sync: open a process
const processObject = memoryjs.openProcess(processName);
// async: open a process
memoryjs.openProcess(processName, (error, processObject) => {});
// sync: get all processes
const processes = memoryjs.getProcesses();
// async: get all processes
memoryjs.getProcesses((error, processes) => {});
// close a process (release handle)
memoryjs.closeHandle(handle);
راجع قسم التوثيق في هذا الملف التمهيدي لمعرفة كيف يبدو كائن العملية.
// sync: find a module
const moduleObject = memoryjs.findModule(moduleName, processId);
// async: find a module
memoryjs.findModule(moduleName, processId, (error, moduleObject) => {});
// sync: get all modules
const modules = memoryjs.getModules(processId);
// async: get all modules
memoryjs.getModules(processId, (error, modules) => {});
راجع قسم التوثيق في هذا الملف التمهيدي لمعرفة كيف يبدو كائن الوحدة النمطية.
// sync: read data type from memory
const value = memoryjs.readMemory(handle, address, dataType);
// async: read data type from memory
memoryjs.readMemory(handle, address, dataType, (error, value) => {});
// sync: read buffer from memory
const buffer = memoryjs.readBuffer(handle, address, size);
// async: read buffer from memory
memoryjs.readBuffer(handle, address, size, (error, buffer) => {});
// sync: write data type to memory
memoryjs.writeMemory(handle, address, value, dataType);
// sync: write buffer to memory
memoryjs.writeBuffer(handle, address, buffer);
// sync: fetch memory regions
const regions = memoryjs.getRegions(handle);
// async: fetch memory regions
memoryjs.getRegions(handle, (regions) => {});
راجع قسم الوثائق في الملف التمهيدي هذا لمعرفة القيم التي يمكن أن تكون عليها dataType
.
// sync: open a named file mapping object
const fileHandle = memoryjs.openFileMapping(fileName);
// sync: map entire file into a specified process
const baseAddress = memoryjs.mapViewOfFile(processHandle, fileName);
// sync: map portion of a file into a specified process
const baseAddress = memoryjs.mapViewOfFile(processHandle, fileName, offset, viewSize, pageProtection);
// sync: close handle to a file mapping object
const success = memoryjs.closeHandle(fileHandle);
راجع قسم الوثائق في الملف التمهيدي هذا للاطلاع على تفاصيل حول المعلمات وقيم الإرجاع لهذه الوظائف.
// sync: change/set the protection on a region of memory
const oldProtection = memoryjs.virtualProtectEx(handle, address, size, protection);
راجع قسم التوثيق في هذا الملف التمهيدي لمعرفة القيم التي يمكن أن تكون عليها protection
.
// sync: pattern scan all modules and memory regions
const address = memoryjs.findPattern(handle, pattern, flags, patternOffset);
// async: pattern scan all modules and memory regions
memoryjs.findPattern(handle, pattern, flags, patternOffset, (error, address) => {});
// sync: pattern scan a given module
const address = memoryjs.findPattern(handle, moduleName, pattern, flags, patternOffset);
// async: pattern scan a given module
memoryjs.findPattern(handle, moduleName, pattern, flags, patternOffset, (error, address) => {});
// sync: pattern scan a memory region or module at the given base address
const address = memoryjs.findPattern(handle, baseAddress, pattern, flags, patternOffset);
// async: pattern scan a memory region or module at the given base address
memoryjs.findPattern(handle, baseAddress, pattern, flags, patternOffset, (error, address) => {});
// sync: execute a function in a remote process
const result = memoryjs.callFunction(handle, args, returnType, address);
// async: execute a function in a remote process
memoryjs.callFunction(handle, args, returnType, address, (error, result) => {});
انقر هنا لترى كيف يبدو الكائن الناتج.
انقر هنا للحصول على تفاصيل حول كيفية تنسيق الوسائط ونوع الإرجاع.
// sync: inject a DLL
const success = memoryjs.injectDll(handle, dllPath);
// async: inject a DLL
memoryjs.injectDll(handle, dllPath, (error, success) => {});
// sync: unload a DLL by module base address
const success = memoryjs.unloadDll(handle, moduleBaseAddress);
// async: unload a DLL by module base address
memoryjs.unloadDll(handle, moduleBaseAddress, (error, success) => {});
// sync: unload a DLL by module name
const success = memoryjs.unloadDll(handle, moduleName);
// async: unload a DLL by module name
memoryjs.unloadDll(handle, moduleName, (error, success) => {});
// sync: attach debugger
const success = memoryjs.attachDebugger(processId, exitOnDetach);
// sync: detach debugger
const success = memoryjs.detachDebugger(processId);
// sync: wait for debug event
const success = memoryjs.awaitDebugEvent(hardwareRegister, millisTimeout);
// sync: handle debug event
const success = memoryjs.handleDebugEvent(processId, threadId);
// sync: set hardware breakpoint
const success = memoryjs.setHardwareBreakpoint(processId, address, hardwareRegister, trigger, length);
// sync: remove hardware breakpoint
const success = memoryjs.removeHardwareBreakpoint(processId, hardwareRegister);
ملاحظة: يتم حاليًا تحديث هذه الوثائق، راجع Wiki لمزيد من المعلومات.
{ dwSize: 304,
th32ProcessID: 10316,
cntThreads: 47,
th32ParentProcessID: 7804,
pcPriClassBase: 8,
szExeFile: "csgo.exe",
modBaseAddr: 1673789440,
handle: 808 }
تتوفر خصائص handle
و modBaseAddr
فقط عند فتح عملية وليس عند إدراج العمليات.
{ modBaseAddr: 468123648,
modBaseSize: 80302080,
szExePath: 'c:\program files (x86)\steam\steamapps\common\counter-strike global offensive\csgo\bin\client.dll',
szModule: 'client.dll',
th32ProcessID: 10316,
GlblcntUsage: 2 }
{ returnValue: 1.23,
exitCode: 2 }
يتم إرجاع هذا الكائن عند تنفيذ دالة في عملية عن بعد:
returnValue
هي القيمة التي تم إرجاعها من الدالة التي تم استدعاؤهاexitCode
هو حالة إنهاء مؤشر الترابط عند استخدام وظائف الكتابة أو القراءة، يجب أن تشير معلمة نوع البيانات (dataType) إلى ثابت من داخل المكتبة:
ثابت | بايت | الأسماء المستعارة | يتراوح |
---|---|---|---|
memoryjs.BOOL | 1 | memoryjs.BOOLEAN | 0 إلى 1 |
memoryjs.INT8 | 1 | memoryjs.BYTE ، memoryjs.CHAR | -128 إلى 127 |
memoryjs.UINT8 | 1 | memoryjs.UBYTE ، memoryjs.UCHAR | 0 إلى 255 |
memoryjs.INT16 | 2 | memoryjs.SHORT | -32,768 إلى 32,767 |
memoryjs.UINT16 | 2 | memoryjs.USHORT ، memoryjs.WORD | 0 إلى 65,535 |
memoryjs.INT32 | 4 | memoryjs.INT ، memoryjs.LONG | -2,147,483,648 إلى 2,147,483,647 |
memoryjs.UINT32 | 4 | memoryjs.UINT ، memoryjs.ULONG ، memoryjs.DWORD | من 0 إلى 4,294,967,295 |
memoryjs.INT64 | 8 | غير متوفر | -9,223,372,036,854,775,808 إلى 9,223,372,036,854,775,807 |
memoryjs.UINT64 | 8 | غير متوفر | 0 إلى 18,446,744,073,709,551,615 |
memoryjs.FLOAT | 4 | غير متوفر | 3.4E +/- 38 (7 أرقام) |
memoryjs.DOUBLE | 8 | غير متوفر | 1.7E +/- 308 (15 رقمًا) |
memoryjs.PTR | 4/8 | memoryjs.POINTER | غير متوفر |
memoryjs.UPTR | 4/8 | memoryjs.UPOINTER | غير متوفر |
memoryjs.STR | غير متوفر | memoryjs.STRING | غير متوفر |
memoryjs.VEC3 | 12 | memoryjs.VECTOR3 | غير متوفر |
memoryjs.VEC4 | 16 | memoryjs.VECTOR4 | غير متوفر |
ملحوظات:
_BE
بنوع البيانات. على سبيل المثال: memoryjs.DOUBLE_BE
.INT64
, UINT64
, INT64_BE
, UINT64_BE
) سوف تحتاج إلى توفير BigInt. عند قراءة عدد صحيح 64 بت، سوف تتلقى BigInt.يتم استخدام أنواع البيانات هذه للإشارة إلى نوع البيانات التي تتم قراءتها أو كتابتها.
مثال لعدد صحيح 64 بت:
const value = memoryjs.readMemory(handle, address, memoryjs.INT64);
console.log(typeof value); // bigint
memoryjs.writeMemory(handle, address, value + 1n, memoryjs.INT64);
Vector3 عبارة عن بنية بيانات مكونة من ثلاثة عوامات:
const vector3 = { x: 0.0, y: 0.0, z: 0.0 };
memoryjs.writeMemory(handle, address, vector3, memoryjs.VEC3);
Vector4 عبارة عن بنية بيانات مكونة من أربعة عوامات:
const vector4 = { w: 0.0, x: 0.0, y: 0.0, z: 0.0 };
memoryjs.writeMemory(handle, address, vector4, memoryjs.VEC4);
إذا كان لديك بنية تريد كتابتها إلى الذاكرة، فيمكنك استخدام المخازن المؤقتة. للحصول على مثال حول كيفية القيام بذلك، قم بعرض مثال المخازن المؤقتة.
لكتابة/قراءة بنية من/إلى الذاكرة، يمكنك استخدام structron لتعريف بنياتك واستخدامها لكتابة أو تحليل المخازن المؤقتة.
إذا كنت تريد قراءة std::string
باستخدام structron
، فإن المكتبة تعرض نوعًا مخصصًا يمكن استخدامه لقراءة/كتابة السلاسل:
// To create the type, we need to pass the process handle, base address of the
// structure, and the target process architecture (either "32" or "64").
const stringType = memoryjs.STRUCTRON_TYPE_STRING(processObject.handle, structAddress, '64');
// Create a custom structure using the custom type, full example in /examples/buffers.js
const Struct = require('structron');
const Player = new Struct()
.addMember(string, 'name');
وبدلاً من ذلك، يمكنك استخدام التركيز وحل المكتبات لتحقيق نفس الشيء. مثال قديم على ذلك هنا .
نوع الحماية هو قيمة DWORD ذات إشارة بت.
يجب أن تشير هذه المعلمة إلى ثابت من المكتبة:
memoryjs.PAGE_NOACCESS, memoryjs.PAGE_READONLY, memoryjs.PAGE_READWRITE, memoryjs.PAGE_WRITECOPY, memoryjs.PAGE_EXECUTE, memoryjs.PAGE_EXECUTE_READ, memoryjs.PAGE_EXECUTE_READWRITE, memoryjs.PAGE_EXECUTE_WRITECOPY, memoryjs.PAGE_GUARD, memoryjs.PAGE_NOCACHE, memoryjs.PAGE_WRITECOMBINE, memoryjs.PAGE_ENCLAVE_THREAD_CONTROL, memoryjs.PAGE_TARGETS_NO_UPDATE, memoryjs.PAGE_TARGETS_INVALID, memoryjs.PAGE_ENCLAVE_UNVALIDATED
راجع ثوابت حماية الذاكرة الخاصة بـ MSDN لمزيد من المعلومات.
نوع تخصيص الذاكرة هو قيمة DWORD ذات إشارة بت.
يجب أن تشير هذه المعلمة إلى إحصائية من المكتبة:
memoryjs.MEM_COMMIT, memoryjs.MEM_RESERVE, memoryjs.MEM_RESET, memoryjs.MEM_RESET_UNDO
راجع وثائق VirtualAllocEx الخاصة بـ MSDN للحصول على مزيد من المعلومات.
يمكنك استخدام هذه المكتبة لقراءة "سلسلة" أو "char*" وكتابة سلسلة.
في كلتا الحالتين تريد الحصول على عنوان مصفوفة char:
std::string str1 = "hello";
std::cout << "Address: 0x" << hex << (DWORD) str1.c_str() << dec << std::endl;
char* str2 = "hello";
std::cout << "Address: 0x" << hex << (DWORD) str2 << dec << std::endl;
من هنا يمكنك ببساطة استخدام هذا العنوان لكتابة الذاكرة وقراءتها.
هناك تحذير واحد عند قراءة سلسلة في الذاكرة، ومع ذلك، نظرًا لأن المكتبة لا تعرف طول السلسلة، فسوف تستمر في القراءة حتى تجد أول فاصل فارغ. لمنع تكرار حلقة لا نهائية، سيتوقف عن القراءة إذا لم يعثر على فاصل فارغ بعد مليون حرف.
إحدى الطرق لتجاوز هذا القيد في المستقبل هي السماح لمعلمة للسماح للمستخدمين بتعيين الحد الأقصى لعدد الأحرف.
عند مسح الأنماط، يجب رفع الأعلام لأنواع التوقيع. يجب أن تكون معلمة نوع التوقيع واحدة مما يلي:
0x0
أو memoryjs.NORMAL
الذي يشير إلى التوقيع العادي.
0x1
أو memoryjs.READ
الذي سيقرأ الذاكرة على العنوان.
0x2
أو memoryjs.SUBSTRACT
والذي سيطرح قاعدة الصورة من العنوان.
لرفع إشارات متعددة، استخدم عامل التشغيل OR ثنائي الاتجاه: memoryjs.READ | memoryjs.SUBTRACT
.
تعرض المكتبة وظائف لتعيينها للحصول على مؤشر وقراءة ملف معين للذاكرة.
openFileMapping (اسم الملف)
راجع وثائق OpenFileMappingA الخاصة بـ MSDN لمزيد من المعلومات.
MapViewOfFile (معالج العملية، اسم الملف)
memoryjs.openFileMapping
constants.PAGE_READONLY
.MapViewOfFile (معالج العملية، اسم الملف، الإزاحة، حجم العرض، حماية الصفحة)
memoryjs.openFileMapping
number
أو bigint
): الإزاحة من بداية الملف (يجب أن تكون من مضاعفات 64 كيلو بايت)number
أو bigint
): عدد البايتات المراد تعيينها (إذا كانت 0
، فسيتم قراءة الملف بأكمله، بغض النظر عن الإزاحة)راجع وثائق MapViewOfFile2 الخاصة بـ MSDN للحصول على مزيد من المعلومات.
راجع نوع الحماية للتعرف على أنواع حماية الصفحة.
لدينا عملية تقوم بإنشاء تعيين ملف:
HANDLE fileHandle = CreateFileA("C:\foo.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE fileMappingHandle = CreateFileMappingA(fileHandle, NULL, PAGE_READONLY, 0, 0, "MappedFooFile");
يمكننا تعيين الملف إلى عملية مستهدفة محددة وقراءة الملف باستخدام memoryjs
:
const processObject = memoryjs.openProcess("example.exe");
const fileHandle = memoryjs.openFileMapping("MappedFooFile");
// read entire file
const baseAddress = memoryjs.mapViewOfFile(processObject.handle, fileHandle.handle);
const data = memoryjs.readMemory(processObject.handle, baseAddress, memoryjs.STR);
// read 10 bytes after 64KB
const baseAddress = memoryjs.mapViewOfFile(processObject.handle, fileHandle.handle, 65536, 10, constants.PAGE_READONLY);
const buffer = memoryjs.readBuffer(processObject.handle, baseAddress, 10);
const data = buffer.toString();
const success = memoryjs.closeHandle(fileHandle);
إذا كنت تريد قراءة ملف معين للذاكرة دون الحاجة إلى عملية مستهدفة لتعيين الملف إليها، فيمكنك تعيينه إلى عملية Node الحالية باستخدام المتغير العام process.pid
:
const processObject = memoryjs.openProcess(process.pid);
يعمل تنفيذ الوظيفة عن بعد عن طريق إنشاء مجموعة من الوسائط وإنشاء كود القشرة الذي يتم إدخاله في العملية المستهدفة وتنفيذه ديناميكيًا، ولهذا السبب قد تحدث أعطال.
لاستدعاء دالة في عملية ما، يمكن استخدام الدالة callFunction
. تدعم المكتبة تمرير الوسائط إلى الوظيفة ويجب أن تكون بالتنسيق التالي:
[{ type: T_INT, value: 4 }]
تتوقع المكتبة أن تكون الوسائط عبارة عن مصفوفة من الكائنات حيث يحتوي كل كائن على type
يشير إلى نوع بيانات الوسيطة، value
تمثل القيمة الفعلية للوسيطة. يمكن العثور على أنواع البيانات المدعومة المختلفة أدناه.
memoryjs.T_VOID = 0x0,
memoryjs.T_STRING = 0x1,
memoryjs.T_CHAR = 0x2,
memoryjs.T_BOOL = 0x3,
memoryjs.T_INT = 0x4,
memoryjs.T_DOUBLE = 0x5,
memoryjs.T_FLOAT = 0x6,
عند استخدام callFunction
، تحتاج أيضًا إلى توفير نوع الإرجاع للدالة، والذي يجب أن يكون مرة أخرى إحدى القيم المذكورة أعلاه.
على سبيل المثال، بالنظر إلى وظيفة C++ التالية:
int add(int a, int b) {
return a + b;
}
يمكنك استدعاء هذه الوظيفة على النحو التالي:
const args = [{
type: memoryjs.T_INT,
value: 2,
}, {
type: memoryjs.T_INT,
value: 5,
}];
const returnType = T_INT;
> memoryjs.callFunction(handle, args, returnType, address);
{ returnValue: 7, exitCode: 7 }
راجع وثائق الكائن الناتج للحصول على تفاصيل حول ما تُرجعه callFunction
.
ملاحظات: تمرير double
كوسيطة غير مدعوم حاليًا، لكن إرجاع واحد هو ذلك.
شكرًا جزيلاً للمساهمين المختلفين الذين جعلوا هذه الميزة ممكنة.
تعمل نقاط توقف الأجهزة عن طريق إرفاق مصحح أخطاء بالعملية، وتعيين نقطة توقف على عنوان معين والإعلان عن نوع المشغل (على سبيل المثال، نقطة توقف عند الكتابة إلى العنوان) ثم الانتظار المستمر لظهور حدث تصحيح (ومن ثم التعامل معه بالتالي).
تعرض هذه المكتبة الوظائف الرئيسية، ولكنها تتضمن أيضًا فئة مجمعة لتبسيط العملية. للحصول على مثال كامل للتعليمات البرمجية، راجع مثال تصحيح الأخطاء الخاص بنا.
عند تعيين نقطة توقف، يُطلب منك تمرير نوع المشغل:
memoryjs.TRIGGER_ACCESS
- تحدث نقطة التوقف عند الوصول إلى العنوانmemoryjs.TRIGGER_WRITE
- تحدث نقطة التوقف عند كتابة العنوان لاحظ أنه عند مراقبة عنوان يحتوي على سلسلة، يجب أن يكون معلمة size
لوظيفة setHardwareBreakpoint
هي طول السلسلة. عند استخدام فئة مجمّع Debugger
، سيحدد المجمّع حجم السلسلة تلقائيًا عن طريق محاولة قراءتها.
لتلخيص:
عند استخدام فئة Debugger
:
size
إلى setHardwareBreakpoint
setHardwareBreakpoint
بإرجاع السجل الذي تم استخدامه لنقطة التوقفعند استخدام وظائف مصحح الأخطاء يدويًا:
size
هي حجم المتغير في الذاكرة (على سبيل المثال int32 = 4 بايت). بالنسبة للسلسلة، هذه المعلمة هي طول السلسلةmemoryjs.DR0
عبر memoryhs.DR3
). تتوفر 4 سجلات للأجهزة فقط (قد تحتوي بعض وحدات المعالجة المركزية على أقل من 4 سجلات متاحة). وهذا يعني أنه يمكن تعيين 4 نقاط توقف فقط في أي وقتsetHardwareBreakpoint
قيمة منطقية توضح ما إذا كانت العملية ناجحة أم لالمزيد من القراءة حول تصحيح الأخطاء ونقاط توقف الأجهزة، قم بمراجعة الروابط التالية:
يحتوي برنامج Debugger على هذه الوظائف التي يجب عليك استخدامها:
class Debugger {
attach(processId, killOnDetach = false);
detach(processId);
setHardwareBreakpoint(processId, address, trigger, dataType);
removeHardwareBreakpoint(processId, register);
}
const hardwareDebugger = memoryjs.Debugger;
hardwareDebugger.attach(processId);
const address = 0xDEADBEEF;
const trigger = memoryjs.TRIGGER_ACCESS;
const dataType = memoryjs.INT;
const register = hardwareDebugger.setHardwareBreakpoint(processId, address, trigger, dataType);
// `debugEvent` event emission catches debug events from all registers
hardwareDebugger.on('debugEvent', ({ register, event }) => {
console.log(`Hardware Register ${register} breakpoint`);
console.log(event);
});
// You can listen to debug events from specific hardware registers
// by listening to whatever register was returned from `setHardwareBreakpoint`
hardwareDebugger.on(register, (event) => {
console.log(event);
});
const hardwareDebugger = memoryjs.Debugger;
hardwareDebugger.attach(processId);
// available registers: DR0 through DR3
const register = memoryjs.DR0;
// int = 4 bytes
const size = 4;
const address = 0xDEADBEEF;
const trigger = memoryjs.TRIGGER_ACCESS;
const dataType = memoryjs.INT;
const success = memoryjs.setHardwareBreakpoint(processId, address, register, trigger, size);
const timeout = 100;
setInterval(() => {
// `debugEvent` can be null if no event occurred
const debugEvent = memoryjs.awaitDebugEvent(register, timeout);
// If a breakpoint occurred, handle it
if (debugEvent) {
memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId);
}
}, timeout);
ملاحظة: لا يلزم وجود حلقة، على سبيل المثال لا توجد حاجة إلى حلقة إذا كنت تريد ببساطة الانتظار حتى يتم الكشف الأول عن العنوان الذي يتم الوصول إليه أو الكتابة إليه.
انتقل إلى الدليل الجذر للوحدة وقم بتشغيل أحد الأوامر التالية:
# will automatically compile based on the detected Node architecture
npm run debug
# compile to target 32 bit processes
npm run debug32
# compile to target 64 bit processes
npm run debug64
index.js
ليتطلب وحدة التصحيح انتقل إلى الدليل الجذر وقم بتغيير السطر في index.js
من:
const memoryjs = require('./build/Release/memoryjs');
إلى ما يلي:
const memoryjs = require('./build/Debug/memoryjs');
افتح حل binding.sln
في Visual Studio، الموجود في مجلد build
في الدليل الجذر للمشروع.
node.exe
الخاص بك (على سبيل المثال C:nodejsnode.exe
)C:projecttest.js
) استكشف ملفات المشروع في Visual Studio (من خلال توسيع ..
ثم lib
في Solution Explorer). يمكن عرض ملفات الرأس بالضغط على Alt
والنقر على أسماء ملفات الرأس أعلى ملفات التعليمات البرمجية المصدر.
يتم تعيين نقاط التوقف عن طريق النقر على يسار رقم السطر.
ابدأ تصحيح الأخطاء بالضغط على F5
، أو بالنقر فوق "Debug" في شريط الأدوات ثم "Start Debugging"، أو بالنقر فوق "Local Windows Debugger".
سيتم تشغيل البرنامج النصي الذي قمت بتعيينه كوسيطة أمر في الخطوة 4، وسيتوقف Visual Studio مؤقتًا عند مجموعة نقاط التوقف ويسمح لك بالتنقل خلال التعليمات البرمجية سطرًا تلو الآخر وفحص المتغيرات.