News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Disassembler lite

Started by donkey, March 14, 2011, 04:05:33 PM

Previous topic - Next topic

donkey

I have been playing with the 64 bit version of Dbgeng.dll (the engine for Windbg) in order to be able to create tools that can disassemble code without having to add 100's of Kb of lib functions to my programs. Using IDebugControl::Disassemble works fine to disassemble and is fairly quick. The following sample code does a line by line disassembly from a given address however you can also create an output sink (its static so its very easy) and have it dump multiple lines. You will need the latest headers that I uploaded today, they have the corrected CoInvoke macro for WIN64 that handles calls with more than 4 parameters as well as the dbgeng.h file for the definitions. I haven't bothered to attach any files since this is just supposed to be a snippet.

The snippet is in GoAsm syntax because I can't get MASM64 to do anything useful at all so I didn't dare try to make COM calls using that piece of junk.

Note that this snippet should assemble as a 32 bit snippet with the x86 switch.

PrintStringByAddr is from my 64 bit vKim implementation for RadAsm, 3.x you can use anything you like to output the results.

#DEFINE WINVER NTDDI_WINXP
#DEFINE FILTERAPI
#DEFINE WIN64
#DEFINE LINKFILES
#DEFINE LINKVCRT

#include "WINDOWS.H"
#include "macros.h"
#include "DbgEng.h"

CONSTANT SECTION
IID_IDebugClient GUID GUID_IID_IDebugClient
IID_IDebugControl GUID GUID_IID_IDebugControl

DATA SECTION
pIDebugClient PTR ?
pIDebugControl PTR ?

CODE SECTION

DisAssemble FRAME pTarget, nLines
uses rbx
LOCAL status:Q
LOCAL EndOffset:Q
LOCAL pBuffer:Q
LOCAL DisassemblySize:Q

invoke CoTaskMemAlloc,1024
mov [pBuffer],rax

invoke DebugCreate,offset IID_IDebugClient, offset pIDebugClient
test rax,rax
jnz >>.DBGCFAIL
CoInvoke(pIDebugClient,IDebugClient.QueryInterface,offset IID_IDebugControl,offset pIDebugControl)
test rax,rax
jnz >>.QIFAIL

invoke GetCurrentProcessId
CoInvoke(pIDebugClient,IDebugClient.AttachProcess,0,rax,DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND)
CoInvoke(pIDebugControl,IDebugControl.WaitForEvent,DEBUG_WAIT_DEFAULT,INFINITE)

mov rbx,[nLines]
mov rdx,[pTarget]
:
CoInvoke(pIDebugControl,IDebugControl.Disassemble,rdx,DEBUG_DISASM_EFFECTIVE_ADDRESS,[pBuffer],1024,offset DisassemblySize,offset EndOffset)
PrintStringByAddr([pBuffer])
mov rdx,[EndOffset]
dec rbx
jnz <<

CoInvoke(pIDebugControl,IDebugControl.Release)
CoInvoke(pIDebugClient,IDebugClient.Release)
invoke CoTaskMemFree,[pBuffer]
ret

.DBGCFAIL
invoke MessageBox,NULL,"DebugCreate failed",0,0
ret
.QIFAIL
invoke MessageBox,NULL,"QueryInterface failed",0,0
CoInvoke(pIDebugClient,IDebugClient.Release)
ret
endf


Output:

Line 70: [pBuffer] = 00000000`004010ad 80e40f          and     ah,0Fh
Line 70: [pBuffer] = 00000000`004010b0 4831c0          xor     rax,rax
Line 70: [pBuffer] = 00000000`004010b3 54              push    rsp
Line 70: [pBuffer] = 00000000`004010b4 ff3424          push    qword ptr [rsp] ss:00000000`0012ebc8=0000000077452f4a
Line 70: [pBuffer] = 00000000`004010b7 4080e4f0        and     spl,0F0h
Line 70: [pBuffer] = 00000000`004010bb 31c9            xor     ecx,ecx
Line 70: [pBuffer] = 00000000`004010bd 4883ec20        sub     rsp,20h
Line 70: [pBuffer] = 00000000`004010c1 e8405f0000      call    IDebugClient+0x7006 (00000000`00407006)
Line 70: [pBuffer] = 00000000`004010c6 4883c428        add     rsp,28h
Line 70: [pBuffer] = 00000000`004010ca 5c              pop     rsp
Line 70: [pBuffer] = 00000000`004010cb 48894c2408      mov     qword ptr [rsp+8],rcx ss:00000000`0012ebd0=000007fef0f6e99d


Hope you find this fun. Note that as far as I know Dbgeng.dll is shipped with Windows, however if it is not you can get it with the debugging tools. It is not redistributable so I can't attach it here.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

I've attached a Radasm 3 project with exe and source. It is obviously assembled for WIN64 and will not run on WIN32, you can try to reassemble it for 32 bit if you like, it might require fixing up the address pushes for the COM calls. Please report any problems in this thread, no PM's please.

"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

drizz

Nice. Here is jwasm translation (jwasm + win32inc v2) tested on 32bit xp(with 64bit compatibility in mind), I have xp psdk lib and old dbghelp.dll - debugging own process didn't work so I hard-coded some other pid, also guids aren't in my dbghelp.lib so I had to do a workaround.


if @WordSize eq 8
.x64p
option frame:auto
option win64:1
size_t typedef qword
else;if @WordSize eq 4
.686p
.xmm
.model flat, c
FRAME equ <>
size_t typedef dword
rdi equ edi
rbx equ ebx
rsi equ esi
rax equ eax
rdx equ edx
rcx equ ecx
rsp equ esp
rbp equ ebp
endif

option casemap:none

include windows.inc; http://www.japheth.de/WinInc.html
include stdio.inc
include DbgEng.inc

T MACRO __qstr:VARARG
LOCAL __sym,__seg
__seg EQU <.code>
%IFIDNI <@CurSeg>,<_DATA>
__seg EQU <.data>
ENDIF
.const
ALIGN 4
__sym LABEL BYTE
IFDIFI <__qstr>,<>
DB __qstr
ENDIF
DB 0
__seg
EXITM <OFFSET __sym>
ENDM


_DEFINE_GUID MACRO name:REQ, l:REQ, w1:REQ, w2:REQ, b1:REQ, b2:REQ, b3:REQ, b4:REQ, b5:REQ, b6:REQ, b7:REQ, b8:REQ

local segn

segn TEXTEQU @CurSeg

.const
ALIGN 4
name dd l
dw w1
dw w2
db b1
db b2
db b3
db b4
db b5
db b6
db b7
db b8

@CurSeg ENDS
segn SEGMENT

ENDM

.const
_DEFINE_GUID _IID_IDebugClient, 27fe5639h , 8407h , 4f47h , 83h , 64h , 0eeh , 11h , 8fh , 0b0h , 8ah , 0c8h
_DEFINE_GUID _IID_IDebugControl, 5182e668h , 105eh , 416eh , 0adh , 92h , 24h , 0efh , 80h , 04h , 24h , 0bah

.data?
pIDebugClient LPVOID ?
pIDebugControl LPVOID ?

.code

DisAssemble proc FRAME uses rbx rsi rdi pTarget, nLines:dword
LOCAL status
LOCAL EndOffset:Qword
LOCAL pBuffer
LOCAL DisassemblySize

invoke DebugCreate,offset _IID_IDebugClient, offset pIDebugClient
.if eax != S_OK
invoke puts,T("error: DebugCreate")
ret
.endif

invoke vf( pIDebugClient, IDebugClient, QueryInterface), offset _IID_IDebugControl,offset pIDebugControl
.if eax != S_OK
invoke puts,T("error: QueryInterface")
invoke vf( pIDebugClient, IDebugClient, Release)
ret
.endif

invoke CoTaskMemAlloc,1024
mov pBuffer,rax

; invoke GetCurrentProcessId
mov eax,2a4h
xor ecx,ecx
mov dword ptr EndOffset,ecx
mov dword ptr EndOffset+4,ecx

DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND equ 000000004h

invoke vf( pIDebugClient, IDebugClient, AttachProcess), EndOffset, eax, DEBUG_ATTACH_NONINVASIVE;DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND or DEBUG_ATTACH_NONINVASIVE
.if eax != S_OK
invoke puts,T("error: AttachProcess")
jmp err
.endif

invoke vf( pIDebugControl, IDebugControl, WaitForEvent), DEBUG_WAIT_DEFAULT, INFINITE
.if eax != S_OK
invoke puts,T("error: WaitForEvent")
jmp err
.endif

mov ebx,nLines
mov rsi,401000h;pTarget
mov size_t ptr EndOffset,rsi

.repeat
invoke vf(pIDebugControl,IDebugControl,Disassemble),EndOffset,DEBUG_DISASM_EFFECTIVE_ADDRESS,pBuffer,1024,addr DisassemblySize,addr EndOffset
.if eax != S_OK
invoke puts,T("error: Disassemble")
.break
.endif
invoke printf,pBuffer
dec ebx
.until zero?

err:
invoke vf(pIDebugControl,IDebugControl,Release)
invoke vf(pIDebugClient,IDebugClient,Release)

invoke CoTaskMemFree,pBuffer

ret
DisAssemble endp



main proc C FRAME uses rbx rsi rdi argc:size_t, argv:size_t

invoke DisAssemble,addr main,10
invoke getchar
xor eax,eax
ret

main endp

end
; link with msvcrt.lib ole32.lib dbghelp.lib DbgEng.Lib
The truth cannot be learned ... it can only be recognized.

donkey

Nice Drizz,

I see I was right in guessing that the address pushes would have to be fixed up, they are 64 bit even in 32 bit Windows and require 2 pushes. JWASM looks nice, I've never tried it, too many libs and things to find to build a simple program when compared to GoAsm. MASM64 is a real piece of junk, tried it, laughed and forgot it.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

Hi Drizz,

I rewrote my proggy for Win32 and tried it, it would not self-attach in Windows XP (virtual machine) but worked fine in Windows 7 X64 as a 32 bit process. The new code with more error checking is:

DisAssemble FRAME hOuput, pTarget, nLines
// 32/64 bit code. Use X64/#define WIN64 for 64 bit, X86 for 32 bit
uses rbx
LOCAL status:%UINT_PTR
LOCAL EndOffset:Q
LOCAL pBuffer:%UINT_PTR
LOCAL DisassemblySize:%UINT_PTR

invoke CoTaskMemAlloc,1024
mov [pBuffer],rax

invoke DebugCreate,offset IID_IDebugClient, offset pIDebugClient
test rax,rax
jnz >>.DBGCFAIL
CoInvoke(pIDebugClient,IDebugClient.QueryInterface,offset IID_IDebugControl,offset pIDebugControl)
test rax,rax
jnz >>.QIFAIL

invoke GetCurrentProcessId
#IF X64
CoInvoke(pIDebugClient,IDebugClient.AttachProcess,0,rax,DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND)
#ELSE
// note that Server is a qword so it requires 2 pushes
CoInvoke(pIDebugClient,IDebugClient.AttachProcess,0,0,eax,DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND)
#ENDIF
test rax,rax
jnz >>.ATTACHFAIL
CoInvoke(pIDebugControl,IDebugControl.WaitForEvent,DEBUG_WAIT_DEFAULT,INFINITE)

mov rbx,[nLines]
mov rdx,[pTarget]
:
#IF X64
CoInvoke(pIDebugControl,IDebugControl.Disassemble,rdx,DEBUG_DISASM_EFFECTIVE_ADDRESS,[pBuffer],1024,offset DisassemblySize,offset EndOffset)
#ELSE
CoInvoke(pIDebugControl,IDebugControl.Disassemble,edx,0,DEBUG_DISASM_EFFECTIVE_ADDRESS,[pBuffer],1024,offset DisassemblySize,offset EndOffset)
#ENDIF
test rax,rax
jnz >>.DISASMFAIL
invoke SendMessage, [hOuput], EM_SETSEL,-1,-1
invoke SendMessage, [hOuput], EM_REPLACESEL,TRUE,[pBuffer]
mov rdx,[EndOffset]
dec rbx
jnz <<

CoInvoke(pIDebugControl,IDebugControl.Release)
CoInvoke(pIDebugClient,IDebugClient.Release)
invoke CoTaskMemFree,[pBuffer]
ret

.DBGCFAIL
invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,NULL,rax,NULL,[pBuffer],1024,NULL
invoke MessageBox,NULL,[pBuffer],"DebugCreate failed",0
ret
.QIFAIL
invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,NULL,rax,NULL,[pBuffer],1024,NULL
invoke MessageBox,NULL,[pBuffer],"QueryInterface failed",0
CoInvoke(pIDebugClient,IDebugClient.Release)
ret
.ATTACHFAIL
invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,NULL,rax,NULL,[pBuffer],1024,NULL
invoke MessageBox,NULL,[pBuffer],"Attach to process failed",0
CoInvoke(pIDebugControl,IDebugControl.Release)
CoInvoke(pIDebugClient,IDebugClient.Release)
ret
.DISASMFAIL
invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,NULL,rax,NULL,[pBuffer],1024,NULL
invoke MessageBox,0,[pBuffer],"Disassemble failed",0
CoInvoke(pIDebugControl,IDebugControl.Release)
CoInvoke(pIDebugClient,IDebugClient.Release)
ret
endf


There must be an issue with self attaching in XP, I didn't try with another process as you say that works, funny that there would be such a fundamental difference between 32 and 64 bit implementations of DbgEng.DLL. I will continue to try to get it working, there has to be a reason it won't self-attach. The error returned is "The parameter is incorrect"

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

drizz

It's the version. With newer version and lib from "Debugging Tools for Windows" sdk it works

Extension Api v6.1.6
Imagehlp Api v4.0.5
Dbghelp file v6.11.0001.404 (debuggers(dbg).090225-1745)


DbghelpFileVersion proc pszBuffer,cchBufferSize

local dwHandle,dwVerSize,dwLen,pInfo
local pVerInf:PBYTE
local szQuery[64]:BYTE;

xor eax,eax
mov dwLen,eax
invoke GetFileVersionInfoSize,T("dbghelp.dll"),addr dwHandle
.if (eax)
mov dwVerSize,eax
invoke GlobalAlloc,GPTR,eax
mov pVerInf,eax
invoke GetFileVersionInfo,T("dbghelp.dll"),addr dwHandle,dwVerSize,pVerInf
invoke VerQueryValue,pVerInf,T("\VarFileInfo\Translation\"),addr pInfo,addr dwVerSize
mov edx,pInfo
mov edx,[edx]
rol edx,16
invoke wsprintf,addr szQuery,T("\StringFileInfo\%.8X\FileVersion\"),edx
invoke VerQueryValue,pVerInf,addr szQuery,addr pInfo,addr dwLen
mov edx,pszBuffer
mov [edx], byte ptr 0
.if (dwLen)
invoke strncpy,pszBuffer,pInfo,cchBufferSize
.endif
invoke GlobalFree,pVerInf
.endif
mov eax,dwLen
ret

DbghelpFileVersion endp

...

LOCAL buffer[100h]:byte

EXT_API_VERSION struct
MajorVersion USHORT ?
MinorVersion USHORT ?
Revision USHORT ?
Reserved USHORT ?
EXT_API_VERSION ends
LPEXT_API_VERSION typedef ptr EXT_API_VERSION

ExtensionApiVersion proto stdcall
ImagehlpApiVersion proto stdcall

invoke ExtensionApiVersion
assume eax:LPEXT_API_VERSION
invoke printf,T("Extension Api v%u.%u.%u",13,10),[eax].MajorVersion,[eax].MinorVersion,[eax].Revision
invoke ImagehlpApiVersion
invoke printf,T("Imagehlp Api v%u.%u.%u",13,10),[eax].MajorVersion,[eax].MinorVersion,[eax].Revision
assume eax:nothing
invoke DbghelpFileVersion,addr buffer,sizeof buffer
invoke printf,T("Dbghelp file v%s",13,10),addr buffer


this is from system dll
Extension Api v5.1.6
Imagehlp Api v4.0.5
Dbghelp file v5.1.2600.5512 (xpsp.080413-2105)
error: AttachProcess


So a check for ext api MajorVersion >= ?6? is needed or parse ver.info string. (or distribute dll )

The truth cannot be learned ... it can only be recognized.

donkey

Hi Drizz,

Yeah, it looks like a version issue, unfortunately the dbgeng dll is not redistributable. I have been looking at a few other functions and it appears that the complete WinDbg debugger is available programatically through the various interfaces. It would not be too difficult to write a complete debugger based on this engine. Even the pseudo registers are available ($teb etc...). I may end up using it to write a simple debugger for GoAsm projects in RadAsm, it would make an excellent addition to the 64 bit vKim tools. The original intent was to include a code dump with the Try/EndTry block but I think I could also base a few other projects on this library. Not bad for something I stumbled on by accident :)
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

drizz

Yeah, dbgeng.dll not in redist.txt :(
The truth cannot be learned ... it can only be recognized.

donkey

Setting up an Output callback. An output callback is a static interface that the debugger interfaces call when they want to output a message or other text.

// COM Object definition for the output interface
COMObject  STRUCT
; interface object
lpVtbl PTR 0
; object data
nRefCount UINT_PTR 1
ENDS

CONSTANT SECTION
IID_IUnknown GUID GUID_IID_IUnknown
IID_IDebugOutputCallbacks GUID GUID_IID_IDebugOutputCallbacks

vtOutputCallbacks PTR OC_QueryInterface
PTR OC_AddRef
PTR OC_Release
PTR OC_Output

DATA SECTION
IDbgOC COMObject <offset vtOutputCallbacks,1>

CODE SECTION

CoInvoke(pIDebugClient,IDebugClient.SetOutputCallbacks,offset IDbgOC)

OC_QueryInterface FRAME this, riid, ppv

mov rax,[ppv]
mov S[rax], 0

invoke IsEqualGUID, [riid], offset IID_IUnknown
test rax,rax
jz >
mov RAX, [this]
mov RDX, [ppv]
mov [RDX],RAX
mov RAX, 0
RET
:
invoke IsEqualGUID, [riid], offset IID_IDebugOutputCallbacks
test rax,rax
jz >
mov RAX, [this]
mov RDX, [ppv]
mov [RDX],RAX
mov RAX, 0
RET
:
mov rax,E_NOINTERFACE
ret
ENDF

OC_AddRef FRAME this
// This is a static interface it should not need to track instances
mov rax,1
ret
ENDF

OC_Release FRAME this
// This is a static interface it should not need to track instances
mov rax,0
ret
ENDF

OC_Output FRAME this, mask, text
// This is the actual proc that outputs the text
invoke SendDlgItemMessage, [hDlg], 1003, EM_SETSEL,-1,-1
invoke SendDlgItemMessage, [hDlg], 1003, EM_REPLACESEL,TRUE,[text]

ret
endf
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable