您的位置:首页 > 编程语言 > C#

[原创]C# 与 汇编 的一次亲密接触。

2007-12-29 09:13 471 查看
废话不讲,转入正题!

前不久,有位网友在MSN上问我:如何解除被独占打开文件的锁定?
虽然从ring0层可以做到更加Power的处理,但是相对繁琐。
权衡之后,我决定在ring3层解决这个问题。经过网上的一番搜
索之后,写了一个简单的exe程序。后来略觉"简陋",遂决定加上
GUI。写win32界面不是汇编的强项,于是决定用.Net中的C#来写。

C#的高级语法对于数值运算、字符串、图形界面的处理简单方便;
而汇编对于程序性能优化、体积精简、低层代码可控性又得心应手。
两者结合使用,可以达到扬长避短的作用。和以前我用VB + ASM
的花哨界面不同,这次我力图做到界面最精简(当然还是有点花哨,呵呵)
,先做一个DEMO,由于才学C#不久,加上水平有限,可能有很多纰漏和
错误,以及一些还可以优化的地方,希望各位不吝指出,多谢了。

截图:

























[细节&要点]

由文件句柄得到文件的名称,还可以用内存影射文件的方法,但有局限性。

Assembly code

_GetFileNameByHandle proc uses esi edi ebx _handle,_hProcess,/
_lpFileName,/
_lpProcessName
;_lpstOpenFile

local @stFI:_FileInfo,@hThread:dword

mov eax,_handle
mov @stFI.Handle,eax

invoke RtlZeroMemory,addr @stFI.FInfo.FileNameW,/
MAX_PATH * 2

invoke CreateThread,NULL,0,addr _WorkThread,/
addr @stFI,0,NULL
mov @hThread,eax

invoke WaitForSingleObject,@hThread,100

.if eax == WAIT_TIMEOUT
invoke TerminateThread,@hThread,0
.endif

invoke CloseHandle,@hThread

invoke RtlZeroMemory,addr buf,/
sizeof buf

invoke GetProcessImageFileNameA,_hProcess,addr buf,/
sizeof buf

invoke _GetProcessShortName,addr buf

invoke lstrcpy,_lpProcessName,addr buf

mov eax,@stFI.FInfo.FileNameLength

.if eax != 0
push eax

invoke RtlZeroMemory,addr buf,/
sizeof buf

pop eax
shr eax,1

invoke WideCharToMultiByte,CP_ACP,0,/
addr @stFI.FInfo.FileNameW,eax,/
addr buf,MAX_PATH,NULL,NULL

invoke lstrcpy,_lpFileName,addr buf
.else
invoke lstrcpy,_lpFileName,addr szDefaultFileName
.endif

ret

_GetFileNameByHandle endp

有些"文件"(实际是管道)会造成操作挂起,遂用线程处理之:

Assembly code

_WorkThread proc _lpFileInfo
local IoStatus:IO_STATUS_BLOCK

assume esi:ptr _FileInfo
mov esi,_lpFileInfo

invoke NtQueryInformationFile,[esi].Handle,addr IoStatus,/
addr [esi].FInfo,sizeof(_FileInfo)-sizeof(dword),/
FileNameInformation

assume esi:nothing
ret

_WorkThread endp

关于C#与汇编的接口兼容问题,取出一个结构说明:
在C#中:

C# code

private struct _stOpenFile
{
public uint lpProcessName;
public uint lpFileName;
public uint ProcessID;
public uint Flags;
public uint hFile;
public uint GrantedAccess;
}

在 asm 中:

Assembly code

_stOpenFile struct

lpProcessName dd ?
lpFileName dd ?
ProcessID dd ?
Flags dd ?
hFile dd ?
GrantedAccess dd ?
_stOpenFile ends

在C#中调用方法:

C# code

private void btnFind_Click(object sender, EventArgs e)
{
StringBuilder szTmp = new StringBuilder(256);

string FileName, ProcessName;

_stOpenFile stOP = new _stOpenFile();

this.btnFind.Enabled = false;

this.lstvewFind.Items.Clear();
this.lstvewFind.Refresh();

unsafe
{
byte[] szProcessName = new byte[256];
byte[] szFileName = new byte[256];

fixed (byte* lpPN = szProcessName)
fixed (byte* lpFN = szFileName)
{
stOP.lpProcessName = (uint)lpPN;
stOP.lpFileName = (uint)lpFN;

while (EnumAllOpenFile(ref stOP) == true)
{
ProcessName = Encoding.Default.GetString(szProcessName).Trim('/0');
FileName = Encoding.Default.GetString(szFileName).Trim('/0');

if ((FileName.ToString().Length == 0) && (ProcessName.ToString().Length == 0))
{
Array.Clear(szProcessName, 0, 256);
Array.Clear(szFileName, 0, 256);
continue;
}

if (FileName.ToString().ToLower() == this.txtFind.Text.ToString().ToLower())
{
this.lstvewFind.Items.Add(ProcessName.ToString()).SubItems.AddRange(new string[] {
FileName.ToString(),stOP.hFile.ToString(),stOP.Flags.ToString(),
"0x"+Convert.ToString(stOP.GrantedAccess,16),stOP.ProcessID.ToString()});
this.lstvewFind.Refresh();
}
else
{
szTmp.Remove(0, szTmp.Length);
szTmp.Append(FileName.ToString());
GetShortName(szTmp);
if (szTmp.ToString().ToLower() == this.txtFind.Text.ToString().ToLower())
{
this.lstvewFind.Items.Add(ProcessName.ToString()).SubItems.AddRange(new string[] {
FileName.ToString(),stOP.hFile.ToString(),stOP.Flags.ToString(),
"0x"+Convert.ToString(stOP.GrantedAccess,16),stOP.ProcessID.ToString()});
this.lstvewFind.Refresh();
}
}
Array.Clear(szProcessName, 0, 256);
Array.Clear(szFileName, 0, 256);
}
}
}
MessageBox.Show("Total Find " + lstvewFind.Items.Count.ToString() + " Open Files !");

this.btnFind.Enabled = true;
}

上面应该可以再大幅度优化,请各位不吝指出,多谢。


[细节&要点2] 

要   open   一些特权进程,需要DEBUG权限,否则无法枚举这些进程中 
的文件,遂首先打开DEBUG特权: 

Assembly code 

    _EnableDebugPrivilege proc _isEnable
    local htoken:HANDLE
    local uid:LUID
    local tp:TOKEN_PRIVILEGES
    local isSuccess

    mov isSuccess,FALSE

    invoke GetCurrentProcess
    lea ebx,htoken
    invoke OpenProcessToken,eax,TOKEN_ADJUST_PRIVILEGES,/
    ebx
    invoke LookupPrivilegeValue,NULL,addr SE_DEBUG_NAME0,/
    addr uid

    mov tp.PrivilegeCount,1
    push uid.LowPart
    pop tp.Privileges[0].Luid.LowPart
    push uid.HighPart
    pop tp.Privileges[0].Luid.HighPart
    .if _isEnable
    mov tp.Privileges[0].Attributes,/
    SE_PRIVILEGE_ENABLED
    .else
    mov tp.Privileges[0].Attributes,0
    .endif

    invoke AdjustTokenPrivileges,htoken,FALSE,addr tp,/
    sizeof tp,NULL,NULL

    invoke GetLastError
    .if eax == ERROR_SUCCESS
    mov isSuccess,TRUE
    .endif

    invoke CloseHandle,htoken
    mov eax,isSuccess
    ret

    _EnableDebugPrivilege endp

由于使用远线程(本来想用HOOK,但还是觉得远线简单   :)),所以 
要考虑重定位: 

Assembly code 

    REMOTE_CODE_START equ this byte

    _lpCloseHandle dd ?

    _RemoteThread proc uses ebx edi esi _hFile

    call @F
    @@:
    pop ebx
    sub ebx,offset @B

    push _hFile
    mov eax,[ebx + _lpCloseHandle]
    ;mov eax,07c809b47h
    call eax
    ret

    _RemoteThread endp

    REMOTE_CODE_END equ this byte
    REMOTE_CODE_LENGTH equ offset REMOTE_CODE_END - offset REMOTE_CODE_START
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: