PDA

View Full Version : بدست آوردن سریال هارد دیسک بدون استفاده از WMI



saber67
یک شنبه 27 بهمن 1392, 21:33 عصر
سلام دوستان
من بعد از کلی زیر و رو کردن اینترنت یه کد پیدا کردم که توی دات نت بشه بدون استفاده از WMI سریال هارد دیسک رو بدست آورد (WMI بعضی وقتا درست کار نمی کنه!)
اما متاسفانه کدهاش به زبان VB.Net بود، من با مبدل کد تبدیلش کردم به C#‎‎ حالا موقع اجرا یه خطا می گیره
کد تبدیل شده رو می نویسم، اگه کسی تونست مشکلش رو حل کنه، فکر کنم این کد به درد خیلی های دیگه هم بخوره
// VBConversions Note: VB project level imports
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using Microsoft.VisualBasic;
using System.Linq;
using System;
using System.Collections;
using System.Xml.Linq;
using System.Windows.Forms;
// End of VB project level imports

using System.Runtime.InteropServices;



namespace Get_HDD_Serial
{
public class HDDInfo
{

#region Declatrations
[DllImport("kernel32",EntryPoint="CreateFileA", ExactSpelling=true, CharSet=CharSet.Ansi, SetLastError=true)]
private static extern int CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]private static extern int CloseHandle(int hObject);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]private static extern int DeviceIoControl(int hDevice, int dwIoControlCode, SENDCMDINPARAMS lpInBuffer, int lpInBufferSize, SENDCMDOUTPARAMS lpOutBuffer, int lpOutBufferSize, ref int lpBytesReturned,
int lpOverlapped);
private const short FILE_SHARE_READ = 0x1;
private const short FILE_SHARE_WRITE = 0x2;
private const int GENERIC_READ = unchecked ((int) 0x80000000);
private const int GENERIC_WRITE = 0x40000000;
private const short OPEN_EXISTING = 3;
private const short CREATE_NEW = 1;
private const int VER_PLATFORM_WIN32_NT = 2;
private const int DFP_RECEIVE_DRIVE_DATA = 0x7C088;
private const int INVALID_HANDLE_VALUE = -1;
#endregion

#region Classes
[StructLayout(LayoutKind.Sequential, Size = 8)]private class IDEREGS
{
public byte Features;
public byte SectorCount;
public byte SectorNumber;
public byte CylinderLow;
public byte CylinderHigh;
public byte DriveHead;
public byte Command;
public byte Reserved;
}
[StructLayout(LayoutKind.Sequential, Size = 32)]private class SENDCMDINPARAMS
{
public int BufferSize;
public IDEREGS DriveRegs;
public byte DriveNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]public byte[] Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]public int[] Reserved2;
public SENDCMDINPARAMS()
{
DriveRegs = new IDEREGS();
Reserved = new byte[3];
Reserved2 = new int[4];
}
}
[StructLayout(LayoutKind.Sequential, Size = 12)]private class DRIVERSTATUS
{
public byte DriveError;
public byte IDEStatus;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]public byte[] Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]public int[] Reserved2;
public DRIVERSTATUS()
{
Reserved = new byte[2];
Reserved2 = new int[2];
}
}
[StructLayout(LayoutKind.Sequential)]private class IDSECTOR
{
public short GenConfig;
public short NumberCylinders;
public short Reserved;
public short NumberHeads;
public short BytesPerTrack;
public short BytesPerSector;
public short SectorsPerTrack;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]public short[] VendorUnique;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]public char[] SerialNumber;
public short BufferClass;
public short BufferSize;
public short ECCSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]public char[] FirmwareRevision;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]public char[] ModelNumber;
public short MoreVendorUnique;
public short DoubleWordIO;
public short Capabilities;
public short Reserved1;
public short PIOTiming;
public short DMATiming;
public short BS;
public short NumberCurrentCyls;
public short NumberCurrentHeads;
public short NumberCurrentSectorsPerTrack;
public int CurrentSectorCapacity;
public short MultipleSectorCapacity;
public short MultipleSectorStuff;
public int TotalAddressableSectors;
public short SingleWordDMA;
public short MultiWordDMA;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 382)]public byte[] Reserved2;
}
[StructLayout(LayoutKind.Sequential)]private class SENDCMDOUTPARAMS
{
public int BufferSize;
public DRIVERSTATUS Status;
public IDSECTOR IDS;
public SENDCMDOUTPARAMS()
{
Status = new DRIVERSTATUS();
IDS = new IDSECTOR();
}
}
#endregion

#region Methods and Functions
private static string SwapChars(char[] chars)
{
for (int i = 0; i <= (chars.Length - 2); i += 2)
{
char t = (char) 0;
t = chars[i];
chars[i] = chars[i + 1];
chars[i + 1] = t;
}
string s = new string(chars);
return s;
}
public static string GetHDDInfoString()
{
string serialNumber = " ";
string model = " ";
string firmware = " ";
int handle = 0;
int returnSize = 0;
int driveNumber = 0;
SENDCMDINPARAMS sci = new SENDCMDINPARAMS();
SENDCMDOUTPARAMS sco = new SENDCMDOUTPARAMS();

if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
handle = System.Convert.ToInt32(CreateFile("\\\\.\\PhysicalDrive" + "0", GENERIC_READ + GENERIC_WRITE, FILE_SHARE_READ + FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0));
}
else
{
handle = System.Convert.ToInt32(CreateFile("\\\\.\\Smartvsd", 0, 0, 0, CREATE_NEW, 0, 0));
}
if (handle != INVALID_HANDLE_VALUE)
{
sci.DriveNumber = (byte) driveNumber;
sci.BufferSize = Marshal.SizeOf(sco);
sci.DriveRegs.DriveHead = System.Convert.ToByte(0xA0 | driveNumber << 4);
sci.DriveRegs.Command = (byte) (0xEC);
sci.DriveRegs.SectorCount = (byte) 1;
sci.DriveRegs.SectorNumber = (byte) 1;
if (DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, sci, Marshal.SizeOf(sci), sco, Marshal.SizeOf(sco), ref
returnSize, 0) != 0)
{
serialNumber = SwapChars(sco.IDS.SerialNumber);
model = SwapChars(sco.IDS.ModelNumber);
firmware = SwapChars(sco.IDS.FirmwareRevision);
}
CloseHandle(handle);
}
return serialNumber.Trim();
}
#endregion
}

}


اینم نحوه تست
TextBox1.Text = HDDInfo.GetHDDInfoString();

shahryari
دوشنبه 28 بهمن 1392, 09:58 صبح
کدهاش به زبان VB.Net بود، من با مبدل کد تبدیلش کردم به C#‎‎‎ حالا موقع اجرا یه خطا می گیره
سلام
خب چه کاریه تبدیلش کردی به سی شارپ
تو همون وی بی اگه درست کار میکنه ، تبدیلش کن به dll و بیار تو سی شارپ

saber67
دوشنبه 28 بهمن 1392, 10:50 صبح
اتفاقا تبدیل به DLL کردم، اما گفتم چه کاریه خب توی خود C# کدش رو استفاده کنیم و نتیجه رو بگیریم که تمیزتر و بهتره!
دیگه نیاز نیست یه DLL اضافه کنار برنامه باشه!
مشکل کد هم فکر کنم مربوط میشه به متغیر sco توی تابع آخر (GetHDDInfoString) ظاهرا موقع فراخوانی مقدار دهی اولیه نشده، باعث خطا میشه!

saber67
دوشنبه 28 بهمن 1392, 13:10 عصر
یه کد VB.Net دیگه پیدا کردم، اونم تبدیل کردم به C#‎ و بدون خطا جواب داد
اینجا کد C#‎ رو می زارم شاید به کار کسی اومد:
// VBConversions Note: VB project level imports
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using Microsoft.VisualBasic;
using System.Linq;
using System;
using System.Collections;
using System.Xml.Linq;
using System.Windows.Forms;
// End of VB project level imports

using System.Runtime.InteropServices;
using System.Text;
using System.ComponentModel;
using Microsoft.Win32.SafeHandles;

// PhysicalDrive.vb


namespace Get_HDD_Serial_2
{
public partial class Form1
{
public Form1()
{
InitializeComponent();

//Added to support default instance behavour in C#‎
if (defaultInstance == null)
defaultInstance = this;
}

#region Default Instance

private static Form1 defaultInstance;

/// <summary>
/// Added by the VB.Net to C#‎ Converter to support default instance behavour in C#‎
/// </summary>
public static Form1 Default
{
get
{
if (defaultInstance == null)
{
defaultInstance = new Form1();
defaultInstance.FormClosed += new FormClosedEventHandler(defaultInstance_FormClosed) ;
}

return defaultInstance;
}
}

static void defaultInstance_FormClosed(object sender, FormClosedEventArgs e)
{
defaultInstance = null;
}

#endregion


#region Win32 Definitions
[StructLayout(LayoutKind.Sequential)]private struct IDEREGS
{
public byte bFeaturesReg;
public byte bSectorCountReg;
public byte bSectorNumberReg;
public byte bCylLowReg;
public byte bCylHighReg;
public byte bDriveHeadReg;
public byte bCommandReg;
public byte bReserved;
}

[StructLayout(LayoutKind.Sequential)]private struct SENDCMDINPARAMS
{
public int cBufferSize;
public IDEREGS irDriveRegs;
public byte bDriveNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]public byte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]public int[] dwReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]public byte[] bBuffer;
}

[StructLayout(LayoutKind.Sequential)]private struct DRIVERSTATUS
{
public byte bDriverError;
public byte bIDEError;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]public byte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]public int[] dwReserved;
}

[StructLayout(LayoutKind.Sequential)]private struct SENDCMDOUTPARAMS
{
public int cBufferSize;
public DRIVERSTATUS DriverStatus;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IDENTIFY_BUFFER_SIZE)]public byte[] bBuffer;
}

[StructLayout(LayoutKind.Sequential)]private struct GETVERSIONINPARAMS
{
public byte bVersion;
public byte bRevision;
public byte bReserved;
public byte bIDEDeviceMap;
public int fCapabilities;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]public int[] dwReserved;
}

[StructLayout(LayoutKind.Sequential)]private struct STORAGE_PROPERTY_QUERY
{
public int PropertyId;
public int QueryType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]public byte[] AdditionalParameters;
}

[StructLayout(LayoutKind.Sequential)]private struct STORAGE_DEVICE_DESCRIPTOR
{
public int Version;
public int Size;
public byte DeviceType;
public byte DeviceTypeModifier;
public byte RemovableMedia;
public byte CommandQueueing;
public int VendorIdOffset;
public int ProductIdOffset;
public int ProductRevisionOffset;
public int SerialNumberOffset;
public byte BusType;
public int RawPropertiesLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)]public byte[] RawDeviceProperties;
}

[StructLayout(LayoutKind.Sequential)]private struct SCSI_PASS_THROUGH
{
public short Length;
public byte ScsiStatus;
public byte PathId;
public byte TargetId;
public byte Lun;
public byte CdbLength;
public byte SenseInfoLength;
public byte DataIn;
public int DataTransferLength;
public int TimeOutValue;
public IntPtr DataBufferOffset;
public int SenseInfoOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]public byte[] Cdb;
}

[StructLayout(LayoutKind.Sequential)]private struct SCSI_PASS_THROUGH_WITH_BUFFER
{
public SCSI_PASS_THROUGH Spt;
public int Filler;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]public byte[] Buffer;
}

[DllImport("kernel32.dll", SetLastError = true)]private static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]private static extern int DeviceIoControl(SafeFileHandle hDevice, int dwIoControlCode, [In()]ref SENDCMDINPARAMS lpInBuffer, int nInBufferSize, [In(), Out()]ref SENDCMDOUTPARAMS lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, int lpOverlapped);

[DllImport("kernel32.dll", SetLastError = true)]private static extern int DeviceIoControl(SafeFileHandle hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, [In(), Out()]ref GETVERSIONINPARAMS lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, int lpOverlapped);

[DllImport("kernel32.dll", SetLastError = true)]private static extern int DeviceIoControl(SafeFileHandle hDevice, int dwIoControlCode, [In()]ref STORAGE_PROPERTY_QUERY lpInBuffer, int nInBufferSize, [In(), Out()]ref STORAGE_DEVICE_DESCRIPTOR lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, int lpOverlapped);

[DllImport("kernel32.dll", SetLastError = true)]private static extern int DeviceIoControl(SafeFileHandle hDevice, int dwIoControlCode, [In()]ref SCSI_PASS_THROUGH_WITH_BUFFER lpInBuffer, int nInBufferSize, [In(), Out()]ref SCSI_PASS_THROUGH_WITH_BUFFER lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, int lpOverlapped);

private const int OPEN_EXISTING = 3;
private const int GENERIC_READ = unchecked ((int) 0x80000000);
private const int GENERIC_WRITE = 0x40000000;
private const int FILE_SHARE_READ = 0x1;
private const int FILE_SHARE_WRITE = 0x2;
private const int FILE_SHARE_DELETE = 0x4;
private const int SMART_GET_VERSION = 0x74080;
private const int SMART_RCV_DRIVE_DATA = 0x7C088;
private const int ID_CMD = 0xEC;
private const int IDENTIFY_BUFFER_SIZE = 512;
private const int CAP_SMART_CMD = 0x4;
private const int IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;
private const int IOCTL_SCSI_PASS_THROUGH = 0x4D004;
private const int SCSI_IOCTL_DATA_IN = 0x1;
private const int PropertyStandardQuery = 0;
private const int StorageDeviceProperty = 0;
private const int ERROR_INVALID_FUNCTION = 0x1;
#endregion

public static string GetSerialNumberUsingStorageQuery(int diskNumber)
{
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
{
int iBytesReturned = 0;
STORAGE_PROPERTY_QUERY spq = new STORAGE_PROPERTY_QUERY();
STORAGE_DEVICE_DESCRIPTOR sdd = new STORAGE_DEVICE_DESCRIPTOR();
spq.PropertyId = StorageDeviceProperty;
spq.QueryType = PropertyStandardQuery;

if (DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, ref spq, Marshal.SizeOf(spq), ref sdd, Marshal.SizeOf(sdd), ref iBytesReturned, 0) == 0)
{
throw (CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)"));
}

StringBuilder result = new StringBuilder();
if (sdd.SerialNumberOffset > 0)
{
int rawDevicePropertiesOffset = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length;
int pos = sdd.SerialNumberOffset - rawDevicePropertiesOffset;
while (pos < iBytesReturned && sdd.RawDeviceProperties[pos] != 0)
{
result.Append(Encoding.ASCII.GetString(sdd.RawDevi ceProperties, pos, 1));
pos++;
}
}
return result.ToString().Trim();
}

}

public static string GetSerialNumberUsingSmart(int diskNumber)
{
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
{
if (IsSmartSupported(hDisk))
{
int iBytesReturned = 0;
SENDCMDINPARAMS sci = new SENDCMDINPARAMS();
SENDCMDOUTPARAMS sco = new SENDCMDOUTPARAMS();
sci.irDriveRegs.bCommandReg = (byte) ID_CMD;
sci.bDriveNumber = (byte) diskNumber;
sci.cBufferSize = IDENTIFY_BUFFER_SIZE;
if (DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, ref sci, Marshal.SizeOf(sci), ref sco, Marshal.SizeOf(sco), ref iBytesReturned, 0) == 0)
{
throw (CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)"));
}
StringBuilder result = new StringBuilder();
for (int index = 20; index <= 39; index += 2)
{
result.Append(Encoding.ASCII.GetString(sco.bBuffer , index + 1, 1));
result.Append(Encoding.ASCII.GetString(sco.bBuffer , index, 1));
}
return result.ToString().Trim();
}
else
{
return string.Empty;
}
}

}

public static string GetSerialNumberUsingScsiPassThrough(int diskNumber)
{
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
{
int iBytesReturned = 0;
SCSI_PASS_THROUGH_WITH_BUFFER spt = new SCSI_PASS_THROUGH_WITH_BUFFER();
spt.Spt.Length = (short) (Marshal.SizeOf(spt.Spt));
spt.Spt.CdbLength = (byte) 16;
spt.Spt.DataIn = (byte) SCSI_IOCTL_DATA_IN;
spt.Spt.DataTransferLength = 64;
spt.Spt.DataBufferOffset = new IntPtr(Marshal.SizeOf(spt) - 64);
spt.Spt.TimeOutValue = 60;
byte[] cdb = new byte[16];
cdb[0] = (byte) (0x12); // INQUIRY
cdb[1] = (byte) (0x1); // EVPD bit
cdb[2] = (byte) (0x80); // Page code (indicates Serial Number)
cdb[4] = (byte) 64; // Allocation length
spt.Spt.Cdb = cdb;
if (DeviceIoControl(hDisk, IOCTL_SCSI_PASS_THROUGH, ref spt, Marshal.SizeOf(spt), ref spt, Marshal.SizeOf(spt), ref iBytesReturned, 0) == 0)
{
int iErrorCode = Marshal.GetLastWin32Error();
if (iErrorCode != ERROR_INVALID_FUNCTION)
{
throw (CreateWin32Exception(iErrorCode, "DeviceIoControl(IOCTL_SCSI_PASS_THROUGH)"));
}
}
StringBuilder result = new StringBuilder();
int pos = IntPtr.Size;
while (pos < spt.Spt.DataTransferLength && spt.Buffer[pos] != 0)
{
result.Append(Encoding.ASCII.GetString(spt.Buffer, pos, 1));
pos++;
}
return result.ToString().Trim();
}

}

private static Win32Exception CreateWin32Exception(int errorCode, string context)
{
Win32Exception win32Exception = new Win32Exception(errorCode);
win32Exception.Data["Context"] = context;
return win32Exception;
}

private static SafeFileHandle OpenDisk(int diskNumber)
{
SafeFileHandle hDevice = CreateFile(string.Format("\\\\.\\PhysicalDrive{0}", diskNumber), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (!hDevice.IsInvalid)
{
return hDevice;
}
else
{
throw (CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile"));
}
}

private static bool IsSmartSupported(SafeFileHandle hDisk)
{
int iBytesReturned = 0;
GETVERSIONINPARAMS gvi = new GETVERSIONINPARAMS();
if (DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, ref gvi, Marshal.SizeOf(gvi), ref iBytesReturned, 0) == 0)
{
return false;
}
return (gvi.fCapabilities & CAP_SMART_CMD) > 0;
}


public void Form1_Load(System.Object sender, System.EventArgs e)
{
TextBox1.Text = GetSerialNumberUsingSmart(0);
}
}

}

samiasoft
دوشنبه 22 آبان 1396, 23:49 عصر
میدونم تاپیک قدیمی هست...اگر از دوستان کسی میتواند کدهایی که در پست اول هست رو به سی شارپ تبدیل کنه کار بی نظیری میکنند...من همین سورس رو به زبان vb6 و vb.net دارم ولی در تبدیل به سی شارپ دچار مشکل میشیم

چرا که فقط همین کد هست که میتونه بدون wmi شماره سریال و مدل هارد رو استخراج کند...

کدهایی که در پست چهارم قرار داده شده فقط شماره سریال رو میده...مدل هارد رو به ما نمیده

Mahmoud.Afrad
چهارشنبه 24 آبان 1396, 00:24 صبح
میدونم تاپیک قدیمی هست...اگر از دوستان کسی میتواند کدهایی که در پست اول هست رو به سی شارپ تبدیل کنه کار بی نظیری میکنند...من همین سورس رو به زبان vb6 و vb.net دارم ولی در تبدیل به سی شارپ دچار مشکل میشیم

چرا که فقط همین کد هست که میتونه بدون wmi شماره سریال و مدل هارد رو استخراج کند...

کدهایی که در پست چهارم قرار داده شده فقط شماره سریال رو میده...مدل هارد رو به ما نمیده

پروژه vb.net را قرار بدید.

samiasoft
چهارشنبه 24 آبان 1396, 02:41 صبح
پروژه vb.net را قرار بدید.

سپاس از توجهتون...پروژه ضمیمه شد.

فقط جهت استخراج شماره سریال بایستی پروژه دسترسی ادمین داشته باشد.

Mahmoud.Afrad
پنج شنبه 25 آبان 1396, 00:04 صبح
تبدیل شده به سی شارپ.

samiasoft
پنج شنبه 25 آبان 1396, 05:54 صبح
تبدیل شده به سی شارپ.

واقعا سپاسگزارم از شما بخاطر وقتی که صرف کردید...:قلب:

چون من خیلی وقت هست در این باره جستجو میکردم و این تنها روشی بود که بطور 100 در100 شماره سریال رو استخراج میکرد.

به جز این 2 تا کتابخانه دیگرم هستند که اونام بطور 100 در 100 شماره سریال رو استخراج میکنند که البته پولی هستند و قابلیت های بیشتری هم دارند.

AzSDK HardwareID 5.0

GetDiskSerial DLL 5.1

:لبخند:اما چون کدهایی که شما تبدیل کردید سورسش دراختیار خودمون هست بیشتر روش میتونیم مانور بدیم.