gpt4 book ai didi

c - 线程入口和线程启动之间的确切区别是什么?

转载 作者:行者123 更新时间:2023-12-03 12:48:46 28 4
gpt4 key购买 nike

  • thread entry and thread start之间的确切区别是什么?和
  • RIP(在动态分析中执行前沿在哪里)是否总是以相同的可预测顺序到达它们?
  • 线程条目是否正在动态更改(在动态分析中,我认为我看到它在寄存器和堆栈中报告)?

  • 到目前为止,我了解 thread start是从某个角度定义的,例如,在Windows中,它始终是 ntdll.RtlUserThreadStart+21(用户),但在程序库级别,它可以是任何函数。但是在创建线程 thread start(系统)之前不会调用 ntdll.NtCreateThreadEx+14thread entry是作为 thread create函数的参数给出的(库,即导出的或私有(private)的)函数。
    一个带有x64dbg的线程(线程ID,地址,到,从,大小,注释,参与方)的调用堆栈的示例:
    4200                                                                                                 
    00000076EBDFF9A8 00007FFEC900A34E 00007FFECB4EC034 A0 ntdll.NtWaitForSingleObject+14 System
    00000076EBDFFA48 00007FF7987B48A1 00007FFEC900A34E 30 kernelbase.WaitForSingleObjectEx+8E User
    00000076EBDFFA78 00007FF7988961A0 00007FF7987B48A1 30 mylibrarydll0.00007FF7987B48A1 User
    00000076EBDFFAA8 00007FF7987B13DF 00007FF7988961A0 30 mylibrarydll0.00007FF7988961A0 User
    00000076EBDFFAD8 00007FF798B4A175 00007FF7987B13DF 30 mylibrarydll0.00007FF7987B13DF User
    00000076EBDFFB08 00007FFECA637034 00007FF798B4A175 30 mylibrarydll0.sub_7FF798B4A0B4+C1 System
    00000076EBDFFB38 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EBDFFBB8 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    2736
    00000076EB5FF648 00007FFECB4623D7 00007FFECB4EFA04 300 ntdll.NtWaitForWorkViaWorkerFactory+14 System
    00000076EB5FF948 00007FFECA637034 00007FFECB4623D7 30 ntdll.TppWorkerThread+2F7 System
    00000076EB5FF978 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EB5FF9F8 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    2468
    00000076EBBFFB78 00007FFEC900A34E 00007FFECB4EC034 A0 ntdll.NtWaitForSingleObject+14 System
    00000076EBBFFC18 00007FF7987B48A1 00007FFEC900A34E 30 kernelbase.WaitForSingleObjectEx+8E User
    00000076EBBFFC48 00007FF7988961A0 00007FF7987B48A1 30 mylibrarydll0.00007FF7987B48A1 User
    00000076EBBFFC78 00007FF7987B13DF 00007FF7988961A0 30 mylibrarydll0.00007FF7988961A0 User
    00000076EBBFFCA8 00007FF798B4A175 00007FF7987B13DF 30 mylibrarydll0.00007FF7987B13DF User
    00000076EBBFFCD8 00007FFECA637034 00007FF798B4A175 30 mylibrarydll0.sub_7FF798B4A0B4+C1 System
    00000076EBBFFD08 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EBBFFD88 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    3784
    00000076EB6FFB88 00007FFECB4623D7 00007FFECB4EFA04 300 ntdll.NtWaitForWorkViaWorkerFactory+14 System
    00000076EB6FFE88 00007FFECA637034 00007FFECB4623D7 30 ntdll.TppWorkerThread+2F7 System
    00000076EB6FFEB8 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EB6FFF38 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    1928
    00000076EB7FFA48 00007FFEC900A34E 00007FFECB4EC034 A0 ntdll.NtWaitForSingleObject+14 System
    00000076EB7FFAE8 00007FF7987B48A1 00007FFEC900A34E 30 kernelbase.WaitForSingleObjectEx+8E User
    00000076EB7FFB18 00007FF7988961A0 00007FF7987B48A1 30 mylibrarydll0.00007FF7987B48A1 User
    00000076EB7FFB48 00007FF7987B13DF 00007FF7988961A0 30 mylibrarydll0.00007FF7988961A0 User
    00000076EB7FFB78 00007FF798B4A175 00007FF7987B13DF 30 mylibrarydll0.00007FF7987B13DF User
    00000076EB7FFBA8 00007FFECA637034 00007FF798B4A175 30 mylibrarydll0.sub_7FF798B4A0B4+C1 System
    00000076EB7FFBD8 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EB7FFC58 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    2276
    00000076EB8FF7C8 00007FFEC900A34E 00007FFECB4EC034 A0 ntdll.NtWaitForSingleObject+14 System
    00000076EB8FF868 00007FF7987B48A1 00007FFEC900A34E 30 kernelbase.WaitForSingleObjectEx+8E User
    00000076EB8FF898 00007FF7988961A0 00007FF7987B48A1 30 mylibrarydll0.00007FF7987B48A1 User
    00000076EB8FF8C8 00007FF7987B13DF 00007FF7988961A0 30 mylibrarydll0.00007FF7988961A0 User
    00000076EB8FF8F8 00007FF798B4A175 00007FF7987B13DF 30 mylibrarydll0.00007FF7987B13DF User
    00000076EB8FF928 00007FFECA637034 00007FF798B4A175 30 mylibrarydll0.sub_7FF798B4A0B4+C1 System
    00000076EB8FF958 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EB8FF9D8 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    12168
    00000076EB9FF6E8 00007FFECB4623D7 00007FFECB4EFA04 300 ntdll.NtWaitForWorkViaWorkerFactory+14 System
    00000076EB9FF9E8 00007FFECA637034 00007FFECB4623D7 30 ntdll.TppWorkerThread+2F7 System
    00000076EB9FFA18 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EB9FFA98 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User
    2428
    00000076EBAFF5D8 00007FFECB4623D7 00007FFECB4EFA04 300 ntdll.NtWaitForWorkViaWorkerFactory+14 System
    00000076EBAFF8D8 00007FFECA637034 00007FFECB4623D7 30 ntdll.TppWorkerThread+2F7 System
    00000076EBAFF908 00007FFECB49D0D1 00007FFECA637034 80 kernel32.BaseThreadInitThunk+14 System
    00000076EBAFF988 0000000000000000 00007FFECB49D0D1 ntdll.RtlUserThreadStart+21 User

    最佳答案

    Windows向调试器发送了一组特定的事件,您可以在WaitForDebugEvent文档中找到它们。
    这些事件之一是CREATE_THREAD_DEBUG_INFO,在Windows创建但尚未启动线程时发送。
    在Windows中,进程和线程的创建在内核中进行,但是它们的最终初始化步骤在用户空间中进行(除非它是一个picoprocess,我们在此不做介绍)。 DLL ntdll.dll在创建后立即映射到线程中,并且线程上下文的RIP设置为指向该DLL的功能之一。
    该函数将执行必要的初始化,然后跳转到CreateThread或类似名称中给出的地址。此函数有点像线程的包装器。
    可以肯定的是,线程初始化会在初始化函数的第一条指令即将执行时发生(就像Windows在此设置断点一样)。
    相反,线程条目只是分配给线程创建API的地址。这很重要,因为它是调用者要执行的实际代码。实际上,出于调试或RE的目的,您几乎可以(如果不是始终如此)忽略线程启动事件。

    让我们做一个例子。考虑这个简单的64位程序。

    BITS 64

    EXTERN CreateThread
    GLOBAL _start

    SECTION .text

    _start:
    and rsp, -16

    push 0
    push 0
    sub rsp, 20h
    xor r9, r9
    lea r8, [REL _thread1]
    xor edx, edx
    xor ecx, ecx
    call CreateThread

    .loop:
    TIMES 1000 pause
    jmp .loop

    _thread1:
    TIMES 1000 pause
    jmp _thread1
    它所做的只是创建一个线程,该线程指向在循环中执行的 pause指令雪橇。主线程还将执行类似但不同的循环。
    循环的目的是使线程的 RIP更改,但仍不在Windows API中。只要没有错误,循环中的任何指令都可以。我选择了 pause,因为:)
    组装并链接程序。
    打开x64dbg,打开程序,然后设置“线程开始”和“线程入口”事件。
    Debug event for x96dbg
    现在按F9到达程序入口点,然后再次按F9放开它。调试器将收到有关新线程创建的通知。
    New thread created
    请注意,执行在 RtlUserThreadStart的开头停止。对于我的Windows版本(Windows 7之类的),情况总是如此。鉴于此答案开头的介绍,这是有道理的。
    另请注意,线程入口点位于 rcx中,这意味着它是 RtlUserThreadStart的第一个参数。
    现在,这是Windows发送到调试器的事件,因此很自然,执行在这里停止了。
    但是线程进入事件 不存在,x64dbg在这里做什么?
    您可以通过查看“断点”选项卡来揭开这个谜底。
    Breakpoints
    您会看到调试器在线程入口点设置了一个断点(即调试器会自动将其自动删除)断点。
    因此,尽管Windows在线程首次开始执行其用户代码时不提供生成调试事件的支持,但是调试器可以通过在线程实际开始之前在该处放置一个断点来轻松地对其进行仿真。
    请注意,这意味着调试器 总是对线程启动事件使用react,在选项中禁用后,它不会停止,显示并等待您执行某些操作。

    暂停和恢复线程不会更改线程入口点,该点在创建线程时固定。
    x64dbg有一个“线程”选项卡,允许用户挂起和恢复线程。使用它不会更改线程入口点,只是 RIP仍然指向两个循环中的某个位置(为了简化此测试而存在)。

    如果线程是使用suspend标志创建的,则在线程恢复之前,线程启动事件将不会触发。
    但是,如果在恢复线程之前完成一对 Get/SetThreadContext的调用来更改线程的 RIP,则 RtlUserStartThread将永远不会执行(IDK该函数的确切作用,但是线程可以不执行此操作),而 则线程开始事件永远不会触发
    执行将直接进入更改后的 RIP
    我不确定这是否是Windows调试界面的遗留错误,可以通过在线程的第一个调度之前设置 TF(并在捕获到相关异常后立即将其删除)来生成线程启动事件。
    在调试/REing线程时,我通常要做的是在线程入口点(很容易获得)或被劫持的 RIP(也很容易获得,因为这种线程被创建为暂停状态)中放置了一个断点。知道有些东西可疑)。
    如果程序令人讨厌,并且线程 RIP上的代码尚不清楚(例如仍然被混淆),请使用硬件断点。
    注意这也完全相同于创建进程(完全相同)(仅使用PE入口点而不是线程入口点)。

    关于c - 线程入口和线程启动之间的确切区别是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65926429/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com