InternetExplorer: проблемы с получением аргументов из функций при отладке дочерних процессов

Apr 29, 2014 at 12:01 PM
startProcess(path_to_iexplore.exe+url, debugChildren=True)

ставлю бряки на NtCreateFile и NtOpenFile
могу получить параметры этих функций из контекста родительского процесса, а от дочерних процессов - постоянные ошибки чтения по несуществующим адресам
функции-обработчики бряков точно написаны корректно - при отладке приложений из MS Office всегда возвращаются корректные значения аргументов
Coordinator
Apr 29, 2014 at 12:52 PM
1) А каким образом получаются параметры? Можно небольшой примерчик?
2) Система 64 битная?
Apr 30, 2014 at 12:02 PM
Система:
windows 7 x64 sp1
python 2.7.6 x86
pykd 0.2.0.26
windbg x86
internet explorer 8, 9, 10

analyzer.py

call-back function

def NtUseFile(id): #NtCreateFile or NtOpenFile
pObjectAttributes = ptrDWord(reg('esp') + 4 * 3)
punicode_string = ptrDWord(pObjectAttributes + 8)
usingFileName = loadWStr(ptrDWord(punicode_string + 4))
print usingFileName
return True

set breakpoints

class DllHandler(eventHandler):
def onModuleLoad(self, arg, imagePathName):
    print 'onModuleLoad: ' + str(imagePathName) + '   ' + str(hex(reg('esp')))

    if str(imagePathName) == 'ntdll':
        nt = module('ntdll')
        try:
            print 'Set break on NtCreateFile'
            setBp(nt.offset('NtCreateFile'), NtUseFile)
        except:
            print 'Can\'t break on NtCreateFile'

        try:
            print 'Set break on NtOpenFile'
            setBp(nt.offset('NtOpenFile'), NtUseFile)
        except:
            print 'Can\'t break on NtOpenFile'
получаю корректные результаты с загружаемыми DLL-ками, папками и т.д.
периодически выскакивают такие ошибки:

Traceback (most recent call last):

File "Analyzer.py", line 344, in NtUseFile
usingFileName = loadWStr(ptrDWord(punicode_string + 4))
MemoryException: Memory exception at 0x0 target virtual address
Coordinator
May 5, 2014 at 8:21 AM
Не могу воспроизвести. Скрипт запускал в виде отдельно приложения.

Система: windows win7 x64, IE9
python 2.6 + pykd 0.2.0.29 OK
python 2.7 + pykd 0.3.0.3 OK

Хочу заметить, что в принципе, есть кооректный случая, который даст такой Exception: переоткрытие директории по хендлу. В этом случае в OBJECT_ATTRIBUTES будет задано поле RootDirectory. Если при этом в поле ObjectName будет передана структура { 0, 0, NULL }, т.е пустая строка - для системы это корректное поведение. Выловить ее можноп проверив поле Length.

Попробуйте переписать callback функцию следующим образом:
def NtUseFile(id):
     nt = module("ntdll")
     oa = nt.typedVar( "_OBJECT_ATTRIBUTES",  ptrDWord(reg('esp') + 4 * 3) )
     print loadUnicodeString( oa.ObjectName )
Тут есть промах по производительности: каждый раз будет происходит поиск информации о типе. Этого можно избежать, если заранее инициализировать глобальную переменную
OBJECT_ATTRIBUTES = nt.typeInfo(""_OBJECT_ATTRIBUTES")

def NtUseFile(id):
     oa = typedVar( OBJECT_ATTRIBUTES ,  ptrDWord(reg('esp') + 4 * 3) )
     print loadUnicodeString( oa.ObjectName )
May 26, 2014 at 1:01 PM
import os
import pykd
import sys
import threading
import time


debug = True
result = 'nothing'
writing_files_set = set([])

using_files_dict = {}
writing_files_dict = {}

p_using_file_handle = 0

nt_key_str = ''

using_file_name = ''

bp_nt_use_file_retn = 0


def exception_func(func):
    print 'in exception_func', func


def NtUseFile(id):              #NtCreateFile or NtOpenFile
    #print_disasm('ntdll', 'NtCreateFile')
    p_object_attributes = pykd.ptrDWord(pykd.reg('esp') + 4 * 3)
    p_unicode_string = pykd.ptrDWord(p_object_attributes + 8)
    global using_file_name
    using_file_name = pykd.loadWStr(pykd.ptrDWord(p_unicode_string + 4))
    if not os.path.isdir(using_file_name):
        #print ''
        #print using_file_name
        #writing_files_set.add(unicode(using_file_name))

        global p_using_file_handle
        p_using_file_handle = pykd.ptrDWord(pykd.reg('esp') + 4)
        #print p_using_file_handle

        return_addr = pykd.ptrDWord(pykd.reg('esp'))
        #print '0x%x' % return_addr
        global bp_nt_use_file_retn
        bp_nt_use_file_retn = pykd.setBp(return_addr, NtUseFile_retn)
        #print bp_nt_use_file_retn

    return True


def NtUseFile_retn(id):
    global using_file_name
    if not os.path.isdir(using_file_name):
        #print 'NtUseFile_retn'
        #print using_file_name
        #global p_using_file_handle
        #print p_using_file_handle
        #global bp_nt_use_file_retn

        try:
            file_handle = pykd.ptrDWord(p_using_file_handle)
            #print file_handle
            if not file_handle == 0:
                using_files_dict[str(file_handle)] = using_file_name
        except:
            #print 'ERROR!!! Invalid file_handle'
            pass
    try:
        global bp_nt_use_file_retn
        pykd.removeBp(bp_nt_use_file_retn)
    except:
        pass
    return True


#------------------------------------------------
class DllHandler(pykd.eventHandler):
    def onModuleLoad(self, arg, image_path_name):
        print 'onModuleLoad: %s    %s' % (str(image_path_name), str(hex(pykd.reg('esp'))))

        if str(image_path_name) == 'ntdll':
            nt = pykd.module('ntdll')
            try:
                print 'Set break on NtCreateFile'
                pykd.setBp(nt.offset('NtCreateFile'), NtUseFile)
            except:
                print 'Can\'t break on NtCreateFile'

            try:
                print 'Set break on NtOpenFile'
                pykd.setBp(nt.offset('NtOpenFile'), NtUseFile)
            except:
                print 'Can\'t break on NtOpenFile'

        return pykd.eventResult.NoChange

    def onException(self, arg):
        if arg.ExceptionCode == 0xc0000005:     # 0xc0000005
            print 'Access Violation !!!'
            if arg.FirstChance is True:
                pass
            else:
                exception_func('Access Violation')

    def onExecutionStatusChange(self, arg):
        if arg == 7:
            exception_func('No Debuggee')


class BreakinThread(threading.Thread):
    def run(self):
        print str(time.asctime(time.gmtime(time.time())))
        time.sleep(150)
        print 'breakin ', time.asctime(time.gmtime(time.time()))
        global debug
        debug = False
        pykd.breakin()
        return


if not pykd.isWindbgExt():
    print ':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::'
    print 'Analyzer began to work'

    # sys.argv[1] - path to exe-file
    # sys.argv[2] - local_path

    dll_handler = DllHandler()
    debug = True

    str_process = '"%s" "%s"' % (sys.argv[1], sys.argv[2])
    print str_process

    writing_files_set.clear()

    pykd.startProcess(str_process, debugChildren=True)
    #attachProcess(2788)

    breakin_thread = BreakinThread()
    breakin_thread.start()
    time.sleep(1)
    print pykd.getProcessThreads()
    print threading.enumerate()
    while debug:
        try:
            pykd.go()
        except:
            exception_func('go')
            break
    print 'EXIT '
    breakin_thread.join()
    if result == 'nothing':
        print 'result: nothing'
    time.sleep(1)

    print 'FINISHED: '

else:
    pykd.dprintln( 'The debugger must be connected to live usermode process' )
May 26, 2014 at 1:06 PM
это кусок моего скрипта, обрабатывающий NtCreateFile и NtOpenFile
запускается через командную строку, получает 2 аргумента: "путь к ехе-файлу" "путь к файлу или ссылка"
на ИЕ 8 и 9 и хроме выскакивают ошибки обращения к памяти, на ИЕ 10 статус No Debuggee

Система:
windows 7 x64 sp1
python 2.7.6 x86
pykd 0.2.0.26
windbg x86
internet explorer 8, 9, 10
Coordinator
Jun 2, 2014 at 6:58 AM
Запустил ваш скрипт, в местах где возникали ошибки обращения к памяти выставил обработку MemoryException и вывел значение переменной OBJECT_ATTRIBUTES:
struct/class: _OBJECT_ATTRIBUTES at 0x24de3c
   +0000 Length                  : ULong   0x18 (24)
   +0004 RootDirectory           : Void*   0x3dc
   +0008 ObjectName              : _UNICODE_STRING*   0x24de5c
   +000c Attributes              : ULong   0x40 (64)
   +0010 SecurityDescriptor      : Void*   0x0
   +0014 SecurityQualityOfService: Void*   0x0

struct/class: _UNICODE_STRING at 0x24de5c
   +0000 Length                  : UInt2B   0x0 (0)
   +0002 MaximumLength           : UInt2B   0x0 (0)
   +0004 Buffer                  : UInt2B*   0x0
Обратите внимание:
строка имеет нулевую длину. Это абсолютно легально - это просто пустая строка. Попытка чтения из поля Buffer - приведет к ошибке. Собственно, об этом я писал постом выше, вы видимо невнимательно читали.

Почему строка пустая: в данном случае просто открывается директория относительно рутовой директории с дескриптором 3DC, Пустая строка означает, что происходит просто переоткрытие этой директории. Вам нужно добавить в скрипт дополнительную проверкую или заменить строку:
 #using_file_name = pykd.loadWStr(pykd.ptrDWord(p_unicode_string + 4))
using_file_name = pykd.loadUnicodeString( p_unicode_string )
ф. loadUnicodeString сама произведет все необходимые проверки