debuggers(system debuggers是什么)

本文目录
system debuggers是什么
***隐藏网址***
很久不见的朋友发来一条短信,给了我一个Skype的号码,叫我网上见。赶紧下载、安装、运行,迎接我是一个冷冰冰的对话框:“Skype is not compatible with system debuggers like SoftICE.”,既然不想让SoftIce下课,自然就要向Skype开刀了。杀鸡焉用牛刀?先用Ollydbg试试,如果不行再请SoftIce出马,更何况在应用级调试中Ollydbg有更良好的操作特性。用Ollydbg调入Skype的执行文件,先偷个懒,不去找MessageBox函数的调用点,直接寻找“Skype is not compatible with system debuggers like SoftICE”。运气不错,在“所有的参考文本字符串”中找到了该字符串,然后双击它,直接跳到了00B7CB62,往下一看正是我们要找的MessageBox!是不是很爽,像精确制导炸弹一样?代码如下: =================================================================================== 00B7CB4D . E8 F6FB89FF call 《jmp.&ole32.OleInitialize》 00B7CB52 . E8 1193A6FF call Skype.005E5E68 00B7CB57 84C0 test al,al 00B7CB59 . 74 1A je short Skype.00B7CB75 00B7CB5B . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL 00B7CB5D . 68 3CDDB700 push Skype.00B7DD3C ; |Title = "Skype" 00B7CB62 . 68 44DDB700 push Skype.00B7DD44 ; |Text = "Skype is not compatible with system debuggers like SoftICE." 00B7CB67 . 6A 00 push 0 ; |hOwner = NULL 00B7CB69 . E8 C2BF88FF call 《jmp.&user32.MessageBoxA》 ; \MessageBoxA =================================================================================== 注意到在00B7CB59处有一个条件转移语句,如果寄存器al等于0则跳过MessagBox,赶紧在此处放置一个断点,按F9执行程序,马上在00B7CB59停了下来,我们可以看到al不等于0,CPU的Z标志位为0,je不执行跳转。要想je跳转很简单,只要在标志位Z的数值0上双击一下,Z就等于1了,可以清楚的看到je发生跳转的红色箭头了,继续往下执行,呵呵~~Skype开始执行了,只是闪了一下主窗口就因为异常而中止执行了,看来跳过了对SoftIce的检测。下面就对Skype动手术,看看能否成功。要想跳过MessageBox可以将je语句改成jmp或者让al=0,我不喜欢硬生生的jmp,还是随着程序的脉络斯文一点,的所以我选择了后者,将“test al,al”改成“xor al,al”即可,也就是将“84c0”改成“32c0”。用十六位器Skype搜索特征信息,我们不妨取长一点,搜索“84 c0 74 1a 6a 00 68”,结果令人满意,该串在全局是唯一的,把“84”改成“32”然后存盘运行,Bingo!搞定!看来对SoftIce的判断确实是由Skype.005E5E68完成的,我们不妨进去看看,到底它是怎么检测的?重新的设置断点,进入该子程序,大步的执行就是了,反正我只是看看而已。一些熟悉的字符串闪过屏幕“\\.\SiwvidSTART”、“\\.\NTICE”、“\\.\Siwvid”、“\\.\SICE”,看来是通过SoftIce的一系列服务来判断是否安装了SoftIce。 (注:拿来做试验Skype是2.0版的,包括原版和Tom版)
spurious thread death event怎么处理
technical ramblings from a wanna-be unix dinosaur
How do debuggers keep track of the threads in your program?
View Comments
If you enjoy this article, subscribe (via RSS or e-mail) and follow me on twitter.
tl;dr
This post describes the relatively undocumented API for debuggers (or other low level programs) that can be used to enumerate the existing threads in a process and receive asynchronous notifications when threads are created or destroyed. This API also provides asynchronous notifications of other interesting thread-related events and feels very similar to the interface exposed by libdl for notifying debuggers when libraries are loaded dynamically at run time.
amd64 and gnu syntax
As usual, everything below refers to amd64 unless otherwise noted. Also, all assembly is in AT&T syntax.
software breakpoints
It’s important to begin first by examining how software breakpoints work. We’ll see shortly why this is important, but for now just trust me.
A debugger sets a software breakpoint by using the ptrace system call to write a special instruction into a target process’ address space. That instruction raises software interrupt #3 which is defined as the Breakpoint Exception in the Intel 64 Architecture Developers Manual.1 When this interrupt is raised, the processor undergoes a privilege level change and calls a function specified by the kernel to handle the exception.
The exception handler in the kernel executes to deliver the SIGTRAP signal to the process. However, if a debugger is attached to a process with ptrace, all signals are first delivered to the debugger. In the case of SIGTRAP, the debugger can examine the list of breakpoints set by the user and take the appropriate action (draw a UI, update the console, or whatever).
The debugger finishes up by masking this signal from the process it is attached to, preventing that process from being killed (most processes will not have a signal handler for SIGTRAP).
In practice most binaries generated by compilers will not have this instruction; it is up to the debugger to write this instruction into the process’ address space during runtime. If you are so inclined, you can raise interrupt #3 via inline assembly or by calling an assembly stub yourself. Many debuggers will catch this signal and trigger an update of some form in the UI.
All that said, this is what the instruction looks like when disassembled:
int 0x03
You may find it useful to check out an earlier and more in-depth article I wrote a while ago about signal handling.
Enumerating threads when first attaching
When a debugger first attaches to a program the program has an unknown number of threads that must be enumerated. glibc exposes a straightforward API for this called td_ta_thr_iter2 found in glibc at nptl_db/td_ta_thr_iter.c. This function takes a callback as one of its arguments. The callback is called once per thread and is passed a handle to an object describing each thread in the process.
We can see the code in GDB3 which uses this API to hand over a callback which will be hit to enumerate the existing threads in a process:
static int
find_new_threads_once (struct thread_db_info *info, int iteration,
td_err_e *errp)
{
volatile struct gdb_exception except;
struct callback_data data;
td_err_e err = TD_ERR;
data.info = info;
data.new_threads = 0;
TRY_CATCH (except, RETURN_MASK_ERROR)
{
/* Iterate over all user-space threads to discover new threads. */
err = info-》td_ta_thr_iter_p (info-》thread_agent,
find_new_threads_callback,
&data,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
}
/* ... */
That’s pretty straightforward, but there are some hairy race conditions, as we can see in this code snippet from thread_db_find_new_threads_2 which calls find_new_threads_once:
if (until_no_new)
{
/* Require 4 successive iterations which do not find any new threads.
The 4 is a heuristic: there is an inherent race here, and I have
seen that 2 iterations in a row are not always sufficient to
"capture" all threads. */
for (i = 0, loop = 0; loop 《 4; ++i, ++loop)
if (find_new_threads_once (info, i, NULL) != 0)
/* Found some new threads. Restart the loop from beginning.»·*/
loop = -1;
}
It’s fiiiiiiiiiinnnneeee.
Now, on to the more interesting interface that is, IMHO, much less straightforward.
Notification of thread create and destroy
A debugger can also gather thread create and destroy events through an interesting asynchronous interface. Let’s go step by step and see how a debugger can listen for create and destroy events.
Enable event notification
First, process wide event notification has to be enabled. This API looks very much like some pieces of the signal API. First we have to create a set of events of we care about (from GDB4 ):
static void
enable_thread_event_reporting (void)
{
td_thr_events_t events;
td_err_e err;
/* ... */
/* Set the process wide mask saying which events we’re interested in. */
td_event_emptyset (&events);
td_event_addset (&events, TD_CREATE);
/* ... */
td_event_addset (&events, TD_DEATH);
/* NB: the following is just a pointer to the function td_ta_set_event on linux */
err = info-》td_ta_set_event_p (info-》thread_agent, &events);
The above code adds TD_CREATE and TD_DEATH to the (empty) set of events that GDB wants to get notifications about. Then the event mask is handed over to glibc with a call to the function td_ta_set_event, which just happens to be stored in a function pointer named td_ta_set_event_p in GDB.
Set asynchronous notification breakpoints
The next step is interesting.
The debugger must use an API to get the addresses of a functions that will be called whenever a thread is created or destroyed. The debugger will then set a software breakpoint at those addresses. When the program creates a thread or a thread is killed the breakpoint will be triggered and the debugger can walk the thread list and update its internal state that describes the threads in the process.
This API is td_ta_event_addr. Let’s check out how GDB uses this API. This code is from the same function as above, but happens after the code shown above:
static void
enable_thread_event_reporting (void)
{
/* ... code above here ... */
/* Delete previous thread event breakpoints, if any. */
remove_thread_event_breakpoints ();
info-》td_create_bp_addr = 0;
info-》td_death_bp_addr = 0;
/* Set up the thread creation event. */
err = enable_thread_event (TD_CREATE, &info-》td_create_bp_addr);
/* ... */
/* Set up the thread death event. */
err = enable_thread_event (TD_DEATH, &info-》td_death_bp_addr);
GDB’s helper function enable_thread_event is pretty straightforward:
static td_err_e
enable_thread_event (int event, CORE_ADDR *bp)
{
td_notify_t notify;
td_err_e err;
struct thread_db_info *info;
info = get_thread_db_info (GET_PID (inferior_ptid));
/* Access an lwp we know is stopped. */
info-》proc_handle.ptid = inferior_ptid;
/* Get the breakpoint address for thread EVENT. */
err = info-》td_ta_event_addr_p (info-》thread_agent, event, ¬ify);
/* ... */
/* Set up the breakpoint. */
gdb_assert (exec_bfd);
(*bp) = (gdbarch_convert_from_func_ptr_addr
(target_gdbarch,
/* Do proper sign extension for the target. */
(bfd_get_sign_extend_vma (exec_bfd) 》 0
? (CORE_ADDR) (intptr_t) notify.u.bptaddr
: (CORE_ADDR) (uintptr_t) notify.u.bptaddr),
¤t_target));
create_thread_event_breakpoint (target_gdbarch, *bp);
return TD_OK;
}
So, GDB stores the addresses of the functions that get called on TD_CREATE and TD_DEATH in td_create_bp_addr and td_death_bp_addr, respectively and sets breakpoints on these addresses in enable_thread_event.
Check if the event has been triggered and drain the event queue
Next time a thread is stopped because a breakpoint has been hit, the debugger needs to check if the breakpoint occurred on an address that is associated with the registered events. If so, the thread event queue needs to be drained with a call to td_ta_event_getmsg and the thread’s information can be retrieved with a call to td_thr_get_info .
GDB does all this in a function called check_event:
/* Check if PID is currently stopped at the location of a thread event
breakpoint location. If it is, read the event message and act upon
the event. */
static void
check_event (ptid_t ptid)
{
/* ... */
td_event_msg_t msg;
td_thrinfo_t ti;
td_err_e err;
CORE_ADDR stop_pc;
int loop = 0;
struct thread_db_info *info;
info = get_thread_db_info (GET_PID (ptid));
/* Bail out early if we’re not at a thread event breakpoint. */
stop_pc = /* ... */
if (stop_pc != info-》td_create_bp_addr
&& stop_pc != info-》td_death_bp_addr)
return;
/* Access an lwp we know is stopped. */
info-》proc_handle.ptid = ptid;
/* ... */
/* If we are at a create breakpoint, we do not know what new lwp
was created and cannot specifically locate the event message for it.
We have to call td_ta_event_getmsg() to get
the latest message. Since we have no way of correlating whether
the event message we get back corresponds to our breakpoint, we must
loop and read all event messages, processing them appropriately.
This guarantees we will process the correct message before continuing
from the breakpoint.
Currently, death events are not enabled. If they are enabled,
the death event can use the td_thr_event_getmsg() inter

更多文章:
excel+条件格式设置好第一行批量(excel条件格式批量)
2026年8月31日 07:00
国内外贸电商有没有什么好系统可以建站的?做电商软件的企业有哪些
2026年7月3日 07:30
获取request对象(java怎么获取request对象)
2025年5月31日 06:15
工作流技术有哪些(翼发云OA办公系统用的什么技术来实现可视化工作流的)
2025年8月2日 22:15
index第二个参数的含义(如何使用index和match函数:)
2025年12月18日 13:00
alter table rename to是什么命令(如何给列重命名 SQL)
2026年8月28日 21:30
oracle19c32位客户端下载(oracle32位客户端以64位运行怎么解决)
2026年5月16日 14:00
get the actors positioned(高中英语作文:落下的书本 The Left Books)
2026年7月23日 16:15














