这是一个很久之前的问题了,今天记录一下,以便遇到同样问题的同学能够看到此文章

崩溃环境:

目前仅收到 windows 7 的部分用户反馈,在程序启动时发生闪退

问题分析:

查看用户提供的日志,可以看见崩溃发生在 sentry 内部

堆栈显示,在加载 sentry_get_modules_list 时程序出现闪退,已知 sentry_get_modules_list 是 sentry 源码里可找到的函数

p.s.  sentry_capture_event 是 sentry 内部上传日志所调用的函数,sentry_unwind_stack_from_ucontext 没有在源码中找到,所以姑且跳过这个函数

我们猜测是 sentry 在加载模块时出错了,于是我们便尝试将加载模块的部分代码先注释,再看会不会闪退

sentry_value_t
sentry_get_modules_list(void)
{
sentry__mutex_lock(&g_mutex);
if (!g_initialized) {
// load_modules(); // 这块先注释掉
g_initialized = true;
}
sentry_value_t modules = g_modules;
sentry_value_incref(modules);
sentry__mutex_unlock(&g_mutex);
return modules;
}

发现问题确实是出现 load_modules 里面,继续看 load_modules 内部干了啥,

static void
load_modules(void)
{
HANDLE snapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32W module = { 0 };
module.dwSize = sizeof(MODULEENTRY32W);
g_modules = sentry_value_new_list(); if (Module32FirstW(snapshot, &module)) {
do {
HMODULE handle = LoadLibraryExW(
module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
MEMORY_BASIC_INFORMATION vmem_info = { 0 };
if (handle
&& sizeof(vmem_info)
== VirtualQuery(
module.modBaseAddr, &vmem_info, sizeof(vmem_info))
&& vmem_info.State == MEM_COMMIT) {
sentry_value_t rv = sentry_value_new_object();
sentry_value_set_by_key(
rv, "type", sentry_value_new_string("pe"));
sentry_value_set_by_key(rv, "image_addr",
sentry__value_new_addr((uint64_t)module.modBaseAddr));
sentry_value_set_by_key(rv, "image_size",
sentry_value_new_int32((int32_t)module.modBaseSize));
sentry_value_set_by_key(rv, "code_file",
sentry__value_new_string_from_wstr(module.szExePath));
extract_pdb_info((uintptr_t)module.modBaseAddr, rv);
sentry_value_append(g_modules, rv);
}
FreeLibrary(handle);
} while (Module32NextW(snapshot, &module));
}

结合堆栈显示的 dll 信息,

(No symbol) [nvd3d9wrap.dll 0x000024DD]

问题可能在 sentry 调用 LoadLibraryExW 加载 nvd3d9wrap.dll 时崩溃了,我们尝试在遍历 dll 时跳过它,并在测试机器上测试,发现仍然闪退

那问题出在哪里呢?

google 了一下,看是否有类似案例,查到了一个 bug 反馈

- https://bugzilla.mozilla.org/show_bug.cgi?id=1607574

关键部分:

sentry 加载 detoured.dll 时调到了映射地址,在该地址上继续加载 nvd3d9wrap.dll,从而发生了崩溃

对此,我们尝试跳过 detoured.dll 的加载,程序没闪退

解决方案:

1. 只对 win7 之前的 windows 机器(包括 win7)做判断

2. 如果机器加载到 detoured.dll,则跳过该 dll 的加载

代码:

#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); bool
GetRealOSVersion(RTL_OSVERSIONINFOW *provi)
{
HMODULE hMod = GetModuleHandleW(L"ntdll.dll");
if (hMod) {
RtlGetVersionPtr fxPtr
= (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion");
if (fxPtr) {
if (STATUS_SUCCESS == fxPtr(provi)) {
return true;
}
}
}
return false;
} bool
IsWin8OrGreater()
{
bool is_win7_or_greater = true;
bool is_win8_or_greater = false; OSVERSIONINFOEX ver = {0};
ver.dwOSVersionInfoSize = sizeof(ver); if (VerifyVersionInfo(&ver,
VER_MAJORVERSION | VER_MINORVERSION | VER_PRODUCT_TYPE, NULL)) {
is_win7_or_greater = (ver.dwMajorVersion > 6)
|| ((ver.dwMajorVersion == 6) && (ver.dwMinorVersion >= 1));
is_win8_or_greater = (ver.dwMajorVersion > 6)
|| ((ver.dwMajorVersion == 6) && (ver.dwMinorVersion > 1));
} if (is_win7_or_greater) {
RTL_OSVERSIONINFOW rovi = {0};
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (GetRealOSVersion(&rovi)) {
is_win8_or_greater = (rovi.dwMajorVersion > 6)
|| ((rovi.dwMajorVersion == 6) && (rovi.dwMinorVersion > 1));
}
} return is_win8_or_greater;
} static void
load_modules(void)
{
HANDLE snapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32W module = {0};
module.dwSize = sizeof(MODULEENTRY32W);
g_modules = sentry_value_new_list(); /* 部分 win7 电脑在加载 nvd3d9wrap.dll 会出现闪退,故跳过该部分代码 */
/* https://bugzilla.mozilla.org/show_bug.cgi?id=1607574 */
bool is_win8_or_later = IsWin8OrGreater();
if (Module32FirstW(snapshot, &module)) {
do {
if (!is_win8_or_later) {
wchar_t *wstr_copy;
if (_wcslwr_s(wstr_copy = _wcsdup(module.szModule),
wcslen(module.szModule) + 1)
== 0) {
if (wcscmp(wstr_copy, L"detoured.dll") == 0) {
free(wstr_copy);
continue;
}
free(wstr_copy);
}
} HMODULE handle = LoadLibraryExW(
module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
MEMORY_BASIC_INFORMATION vmem_info = { 0 };
...

sentry 在加载模块时闪退的更多相关文章

  1. 内核加载模块时提示usb_common: exports duplicate symbol of_usb_get_dr_mode

    1.分析: 既然符号重复了,那么说明有一个部分既被编译到内核中也被编译成模块了,因此在加载模块时,内核报符号重复的提示 2.解决 直接配置内核的某一部分编译成模块,例如笔者就直接将USB这一部分编译成 ...

  2. 内核加载模块时出现Unknown symbol等提示

    一.背景 1.更改了内核的配置,重新编译了内核 2.未重新编译内核模块 3.板子上只更新了内核,并未更新文件系统 二.分析 发现是在加载内核模块时出现Unknown symbol等信息,恰逢当时只更新 ...

  3. 在加载模块时出现cannot insert '*.ko': Device or resource busy错误

    制作了一个模块,在加载是出现了cannot insert '*.ko': Device or resource busy错误. 原因: 是由于模块使用的是静态分配设备号,而这个设备号已经被系统中的其他 ...

  4. ios wkwebview 跳转到新的controllerview加载页面 出现闪退问题

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { if(isF ...

  5. 加载驱动模块时Device or resource busy的解决方法

    加载驱动模块时Device or resource busy的解决方法 加载驱动模块时Device or resource busy的解决方法 insmod或modprobe驱动模块时Device o ...

  6. Linux驱动之内核加载模块过程分析

    Linux内核支持动态的加载模块运行:比如insmod first_drv.ko,这样就可以将模块加载到内核所在空间供应用程序调用.现在简单描述下insmod first_drv.ko的过程 1.in ...

  7. [driver]linux内核动态加载模块

    问题: 1. 把编译好的模块放到板子/lib/modules对应文件夹下,并且执行了depmod -a, 比如pl2303.ko, 那么下一次插入pl2303的串口线,是否可以识别,也就是自动加载pl ...

  8. Linux中实现在系统启动时自动加载模块

    下面是以前学习Linux时写的,后来仔细研究rc.sysinit后发现,只需要修改下列地方就可以了,不必这么麻烦的: rc.sysinit中有这样的一段代码: # Load other user-de ...

  9. 嵌入式 Linux 与linux启动时自动加载模块

    一.在ARM linux 下,一般而言,产品在启动的过程中应该加载模块,最简单的方法是修改启动过程的rc脚本(/etc/init.d/rcS),增加ismod /../xxx.ko这个命令.例如:加载 ...

  10. AngularJS中多个ng-app(手动加载模块)

    1.当有多个ng-app时:(首先是要加载angularJS) <div ng-app=""> <p>姓名:<input type="tex ...

随机推荐

  1. [转帖]Zookeeper集群搭建(3个节点为例)

    Zookeeper集群搭建 1.说明 本文用的linux版本:centos6,准备3台centos6虚拟机,给他们安装zookeeper,每一台的安装过程相同,你也可以安装一台,然后克隆出另外两台.主 ...

  2. [转帖]VCSA证书过期问题处理

    1.  故障现象 2022年10月25日,登陆VC报错. 按照报错信息,结合官方文档,判断为STS证书过期导致. vCenter Server Appliance (VCSA) 6.5.x, 6.7. ...

  3. [转帖]redis进程绑定指定的CPU核

    文章系转载,便于分类和归纳,源文地址:https://blog.csdn.net/youlinhuanyan/article/details/99671878 1)查看某服务的pid $ ps -au ...

  4. Nginx 系列 | (转)Nginx 上传文件:client_max_body_size 、client_body_buffer_size

    原文:http://php-note.com/article/detail/488 client_max_body_size client_max_body_size 默认 1M,表示 客户端请求服务 ...

  5. 【转贴】2019.3 学习向SP打造指南 篇一:微软神器Surface产品线全系列详细介绍

    学习向SP打造指南 篇一:微软神器Surface产品线全系列详细介绍 2019-03-01 22:30:00 161点赞 699收藏 141评论 https://post.smzdm.com/p/a5 ...

  6. ELK运维文档

    Logstash 目录 Logstash Monitoring API Node Info API Plugins Info API Node Stats API Hot Threads API lo ...

  7. React中兄弟组件通信和组件跨级传递Context的使用

    React兄弟组件之间的通信 Child2组件需要去更改Child1组件中的数据. 因为Child1和Child2是兄弟组件 所以数据和事件都放在最进的父级组件中去 兄弟组件通信的简单使用 impor ...

  8. Milvus 2.3.功能全面升级,核心组件再升级,超低延迟、高准确度、MMap一触开启数据处理量翻倍、支持GPU使用!

    Milvus 2.3.功能全面升级,核心组件再升级,超低延迟.高准确度.MMap一触开启数据处理量翻倍.支持GPU使用! 1.Milvus 2.3版本全部升级简介 Milvus 2.3.0 不仅包含大 ...

  9. 21.11 Python 使用CRC图片去重

    使用CRC32还可实现图片去重功能,如下FindRepeatFile函数,运行后通过对所有文件做crc校验并将校验值存储至CatalogueDict字典内,接着依次提取CRC特征值并将其存储至Cata ...

  10. LyScript 实现Hook改写MessageBox

    LyScript 可实现自定义汇编指令的替换功能,用户可以自行编写一段汇编指令,将程序中特定的通用函数进行功能改写与转向操作,此功能原理是简单的Hook操作. 插件地址:https://github. ...