首页
社区
课程
招聘
91
[原创]安卓中so的加载
发表于: 2025-3-13 16:00 35398

[原创]安卓中so的加载

2025-3-13 16:00
35398

so?what can i do?

首先在java层肯定是要去调用so层文件,本篇重点讲解native层的加载流程,此处不再赘述。

“Dynamic Link” 动态装载库,我们会想到windows系统中,存在加载dll文件的动态加载类型,在linux中,类似的文件就是.so文件了,而加载文件的重要函数就是dlopen

dlopen:该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。这种机制使得在系统中添加或者删除一个模块时,都不需要重新进行编译。

函数原型

第一个参数是头文件所在的文件名,也就是so文件,第二个标志参数有很多,例如有RTLD_NOWRTLD_LAZY立即计算和需要时计算,以及RTLD_GLOBAL使得那些在以后才加载的库可以获得其中的符号。方式就是dlopen返回的句柄作为dlsym()的第一个参数,获取符号在库中的地址。

此函数作为安卓平台上特有的函数 是dlopen函数的拓展版本,进一步增强了dlopen函数的功能

函数原型

可以看到在原函数基础上增加了ext_data参数

参照源码可以大致了解增强的功能,其中大部分功能与第二个参数flag中的一些功能标志位相关联

此版本为安卓4源码分析,最新版本的安卓号的加载流程在代码量上有进一步的提升,但是基础原理类似。

上面的dlopen函数只是一个引子,真正的核心功能代码实现在do_dlopen函数中

此函数的类型为soinfo指针,soinfo代表的含义是“进程加载的so链”,其中包含了已经被加载的so 的信息。这里所返回的值也是si——进程加载的so链。

其中涉及的主要两步函数为find_libraryCallConstructor下面会继续介绍。

find_library函数传入参数后也会进行进一步的函数调用,流程见注释

涉及知识点:elf文件格式,文件分区的加载

此函数根据上面的soinfo链接映像函数分析的section动态节区中的信息,获取共享库依赖的所有的so文件名,所有的依赖库初始化完成后,执行init_func、init_array方法初始化该动态库。

这两个函数是so文件在被加载或者卸载时自动执行的函数,用于初始化的操作,其中init函数优先于init_array函数。作为so层加载很早的函数,可以通过实现hook他来绕过一些关键的检测点。

也正是因为加载过早且初始化后只加载一次,我们如果直接去hook是无法get到的,通过上面的流程我们知道了这两个函数是在call_constructors中进行加载,我们就可以通过逆向hook相关的native函数进行加载hook。通过在android_dlopen_ext加载过程中进行hook操作。

这里有一步很关键的操作,关于call_constructors 函数,call_constructors 是在共享库加载时被调用的函数。意思就是他是存储在安卓本机中的本地链接库函数,而他的位置文件具体就在/system/bin/linker64中,我们将他从手机上pull下来进行反编译并查找相关函数可以看到。

反编译的结果与我们上文看到的函数功能接近。

通过hookandroid_dlopen_ext定位我们要hook的so文件,通过hook linker64中的 call_constructors函数可以修改init和init_array的流程。

这里需要注意的是关于偏移地址的查找,可以通过pie,ida等工具 或者在linux环境中执行命令

通过 JNIEXPORT 和 JNICALL 两个宏定义声明,在虚拟机加载 so 时发现上面两个宏定义的函数时就会链接到对应的 native 方法。

通过 RegisterNatives 方法手动完成 native 方法和 so 中的方法的绑定,这样虚拟机就可以通过这个函数映射表直接找到相应的方法了。

流程如下。

通过RegisterNatives(JNI *env ,jclass clazz ,const JNINativeMethord *methord ,jint nmethods)函数,参数分别代表:java环境,java类的描述符,待注册的方法集合,待注册方法数量。

其中“待注册方法集合”是名为JNINativeMethord的结构体,内容如下

这里的第二个参数为方法签名需要注意,类型为字符串,由一对小括号和若干签名符号组成,其中括号内写传入参数的签名符号,没有参数则不写,括号外写返回参数的签名符号。

实例:Java层函数String getText(int a,byte[] b)就是(I[B)Ljava/lang/String;

可以看出,在动态加载的过程中,很关键的方法就是流程图中的RegisterNatives在这其中的参数有很多设置方法名的敏感信息。

《安卓逆向这档事》十七、你的RPCvs佬的RPC - 吾爱破解 - 52pojie.cn

[原创] 细说So动态库的加载流程-Android安全-看雪-安全社区|安全招聘|kanxue.com

android so加载 - vendanner - 博客园

https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/include/android/dlext.h;l=174;drc=69e2b7c426ed5bef6be369b7e4d300e370dc39cd;bpv=0;bpt=1?hl=zh-cn

so逆向筑基-hook init init_array 和JNI_OnLoad

void *dlopen(const char *filename, int flag);
void *dlopen(const char *filename, int flag);
void* android_dlopen_ext(const char* path, int flag, const void* ext_data);
void* android_dlopen_ext(const char* path, int flag, const void* ext_data);
typedef struct {
  /** A bitmask of `ANDROID_DLEXT_` enum values. */
  uint64_t flags;
 
  /** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
  void*   _Nullable reserved_addr;
  /** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
  size_t  reserved_size;
 
  /** Used by `ANDROID_DLEXT_WRITE_RELRO` and `ANDROID_DLEXT_USE_RELRO`. */
  int     relro_fd;
 
  /** Used by `ANDROID_DLEXT_USE_LIBRARY_FD`. */
  int     library_fd;
  /** Used by `ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET` */
  off64_t library_fd_offset;
 
  /** Used by `ANDROID_DLEXT_USE_NAMESPACE`. */
  struct android_namespace_t* _Nullable library_namespace;
} android_dlextinfo;
typedef struct {
  /** A bitmask of `ANDROID_DLEXT_` enum values. */
  uint64_t flags;
 
  /** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
  void*   _Nullable reserved_addr;
  /** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
  size_t  reserved_size;
 
  /** Used by `ANDROID_DLEXT_WRITE_RELRO` and `ANDROID_DLEXT_USE_RELRO`. */
  int     relro_fd;
 
  /** Used by `ANDROID_DLEXT_USE_LIBRARY_FD`. */
  int     library_fd;
  /** Used by `ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET` */
  off64_t library_fd_offset;
 
  /** Used by `ANDROID_DLEXT_USE_NAMESPACE`. */
  struct android_namespace_t* _Nullable library_namespace;
} android_dlextinfo;
void* dlopen(const char* filename, int flags) {
  ScopedPthreadMutexLocker locker(&gDlMutex);
  soinfo* result = do_dlopen(filename, flags);
  if (result == NULL) {
    __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
    return NULL;
  }
  return result;
}
 
soinfo* do_dlopen(const char* name, int flags) {
    //判断传入标志的类型
  if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL)) != 0) {
    DL_ERR("invalid flags to dlopen: %x", flags);
    return NULL;
  }
    //内存保护权限设置 此处为可读可写,目的是在加载find_library函数后可以对soinfo结构体的内容进行修改
  set_soinfo_pool_protection(PROT_READ | PROT_WRITE);
  soinfo* si = find_library(name); //这里返回了so信息链
  if (si != NULL) {
    si->CallConstructors(); //这里执行了此构造方法
  }
    //在这里就没有可写权限了
  set_soinfo_pool_protection(PROT_READ);
  return si;
}
void* dlopen(const char* filename, int flags) {
  ScopedPthreadMutexLocker locker(&gDlMutex);
  soinfo* result = do_dlopen(filename, flags);
  if (result == NULL) {
    __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
    return NULL;
  }
  return result;
}
 
soinfo* do_dlopen(const char* name, int flags) {
    //判断传入标志的类型
  if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL)) != 0) {
    DL_ERR("invalid flags to dlopen: %x", flags);
    return NULL;
  }
    //内存保护权限设置 此处为可读可写,目的是在加载find_library函数后可以对soinfo结构体的内容进行修改
  set_soinfo_pool_protection(PROT_READ | PROT_WRITE);
  soinfo* si = find_library(name); //这里返回了so信息链
  if (si != NULL) {
    si->CallConstructors(); //这里执行了此构造方法
  }
    //在这里就没有可写权限了
  set_soinfo_pool_protection(PROT_READ);
  return si;
}
static soinfo *find_loaded_library(const char *name)
{
    soinfo *si;
    const char *bname;
 
    // TODO: don't use basename only for determining libraries
    // http://code.google.com/p/android/issues/detail?id=6670
 
    bname = strrchr(name, '/'); //分割so文件的名称
    bname = bname ? bname + 1 : name;
 
    for (si = solist; si != NULL; si = si->next) {//递归查找是否存在so文件名称
        if (!strcmp(bname, si->name)) {
            return si;
        }
    }
    return NULL;
}
 
static soinfo* find_library_internal(const char* name) {
  if (name == NULL) {
    return somain;  //返回共享库
  }
 
  soinfo* si = find_loaded_library(name);
  if (si != NULL) {
    if (si->flags & FLAG_LINKED) {// 前者检查是否有flag标志字段,后者检查是否被链接
      return si; //如果被链接就直接加载
    }
    DL_ERR("OOPS: recursive link to \"%s\"", si->name);
      //报错递归链接错误。
      //【递归链接:在动态库的加载过程中,如果同一个库被多次请求加载,可能会发生递归链接。通常这是不希望发生的情况,因为这会导致循环依赖或重复加载的错误。】
    return NULL;
  }
 
  TRACE("[ '%s' has not been loaded yet.  Locating...]", name);
    //发现未被加载后会通过load_library重新加载
  si = load_library(name); //load_library的函数在下面介绍
  if (si == NULL) { //如果再次加载仍为null 则返回null
    return NULL;
  }
 
  // At this point we know that whatever is loaded @ base is a valid ELF
  // shared library whose segments are properly mapped in.
    //返回了基址,大小和名称
  TRACE("[ init_library base=0x%08x sz=0x%08x name='%s' ]",
        si->base, si->size, si->name);
 
    //通过此函数
  if (!soinfo_link_image(si)) {//此函数实现了动态链接库中section信息解析。
    munmap(reinterpret_cast<void*>(si->base), si->size);
    soinfo_free(si);
    return NULL;
  }
 
  return si;
}
 
static soinfo* find_library(const char* name) {
  soinfo* si = find_library_internal(name);
  if (si != NULL) {
    si->ref_count++;
  }
  return si;
}
static soinfo *find_loaded_library(const char *name)
{
    soinfo *si;
    const char *bname;
 
    // TODO: don't use basename only for determining libraries
    // http://code.google.com/p/android/issues/detail?id=6670
 
    bname = strrchr(name, '/'); //分割so文件的名称
    bname = bname ? bname + 1 : name;
 
    for (si = solist; si != NULL; si = si->next) {//递归查找是否存在so文件名称
        if (!strcmp(bname, si->name)) {
            return si;
        }
    }
    return NULL;
}
 
static soinfo* find_library_internal(const char* name) {
  if (name == NULL) {
    return somain;  //返回共享库
  }
 
  soinfo* si = find_loaded_library(name);
  if (si != NULL) {
    if (si->flags & FLAG_LINKED) {// 前者检查是否有flag标志字段,后者检查是否被链接
      return si; //如果被链接就直接加载
    }
    DL_ERR("OOPS: recursive link to \"%s\"", si->name);
      //报错递归链接错误。
      //【递归链接:在动态库的加载过程中,如果同一个库被多次请求加载,可能会发生递归链接。通常这是不希望发生的情况,因为这会导致循环依赖或重复加载的错误。】
    return NULL;
  }
 
  TRACE("[ '%s' has not been loaded yet.  Locating...]", name);
    //发现未被加载后会通过load_library重新加载
  si = load_library(name); //load_library的函数在下面介绍
  if (si == NULL) { //如果再次加载仍为null 则返回null
    return NULL;
  }
 
  // At this point we know that whatever is loaded @ base is a valid ELF
  // shared library whose segments are properly mapped in.
    //返回了基址,大小和名称
  TRACE("[ init_library base=0x%08x sz=0x%08x name='%s' ]",
        si->base, si->size, si->name);
 
    //通过此函数
  if (!soinfo_link_image(si)) {//此函数实现了动态链接库中section信息解析。
    munmap(reinterpret_cast<void*>(si->base), si->size);
    soinfo_free(si);
    return NULL;
  }
 
  return si;
}
 
static soinfo* find_library(const char* name) {
  soinfo* si = find_library_internal(name);
  if (si != NULL) {
    si->ref_count++;
  }
  return si;
}
void soinfo::CallConstructors() {
if (constructors_called) {
 return;
}
 
// We set constructors_called before actually calling the constructors, otherwise it doesn't
// protect against recursive constructor calls. One simple example of constructor recursion
// is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
// 1. The program depends on libc, so libc's constructor is called here.
// 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
// 3. dlopen() calls the constructors on the newly created
//    soinfo for libc_malloc_debug_leak.so.
// 4. The debug .so depends on libc, so CallConstructors is
//    called again with the libc soinfo. If it doesn't trigger the early-
//    out above, the libc constructor will be called again (recursively!).
constructors_called = true;
 
if ((flags & FLAG_EXE) == 0 && preinit_array != NULL) {
 // The GNU dynamic linker silently ignores these, but we warn the developer.
 PRINT("\"%s\": ignoring %d-entry DT_PREINIT_ARRAY in shared library!",
       name, preinit_array_count);
}
 
    //确保库已被初始化加载
if (dynamic != NULL) {
 for (Elf32_Dyn* d = dynamic; d->d_tag != DT_NULL; ++d) {
   if (d->d_tag == DT_NEEDED) {
     const char* library_name = strtab + d->d_un.d_val;
     TRACE("\"%s\": calling constructors in DT_NEEDED \"%s\"", name, library_name);
     find_loaded_library(library_name)->CallConstructors();
   }
 }
}
 
TRACE("\"%s\": calling constructors", name);
 
    //最后进行初始化函数的执行
// DT_INIT should be called before DT_INIT_ARRAY if both are present.
CallFunction("DT_INIT", init_func);
CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
}
void soinfo::CallConstructors() {
if (constructors_called) {
 return;
}
 
// We set constructors_called before actually calling the constructors, otherwise it doesn't
// protect against recursive constructor calls. One simple example of constructor recursion
// is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
// 1. The program depends on libc, so libc's constructor is called here.
// 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
// 3. dlopen() calls the constructors on the newly created
//    soinfo for libc_malloc_debug_leak.so.
// 4. The debug .so depends on libc, so CallConstructors is
//    called again with the libc soinfo. If it doesn't trigger the early-
//    out above, the libc constructor will be called again (recursively!).
constructors_called = true;
 
if ((flags & FLAG_EXE) == 0 && preinit_array != NULL) {
 // The GNU dynamic linker silently ignores these, but we warn the developer.
 PRINT("\"%s\": ignoring %d-entry DT_PREINIT_ARRAY in shared library!",
       name, preinit_array_count);
}
 
    //确保库已被初始化加载
if (dynamic != NULL) {
 for (Elf32_Dyn* d = dynamic; d->d_tag != DT_NULL; ++d) {
   if (d->d_tag == DT_NEEDED) {
     const char* library_name = strtab + d->d_un.d_val;
     TRACE("\"%s\": calling constructors in DT_NEEDED \"%s\"", name, library_name);
     find_loaded_library(library_name)->CallConstructors();
   }
 }
}
 
TRACE("\"%s\": calling constructors", name);
 
    //最后进行初始化函数的执行
// DT_INIT should be called before DT_INIT_ARRAY if both are present.
CallFunction("DT_INIT", init_func);
CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
}
__int64 __fastcall _dl__ZN6soinfo17call_constructorsEv(__int64 result)
{
  char v1; // w8
  __int64 v2; // x19
  const char *v3; // x2
  _QWORD *i; // x20
  __int64 v5; // x1
  __int64 v6; // x0
  __int64 v7; // x8
  char *v8; // x0
  __int64 v9; // x2
  __int64 v10; // x3
  char v11[16]; // [xsp+8h] [xbp-38h] BYREF
  __int64 v12; // [xsp+18h] [xbp-28h]
  __int128 v13; // [xsp+20h] [xbp-20h] BYREF
  char *v14; // [xsp+30h] [xbp-10h]
 
  if ( !(_dl_g_is_ldd | *(result + 248)) )
  {
    v1 = *(result + 48);
    v2 = result;
    *(result + 248) = 1;
    if ( (v1 & 4) == 0 && *(result + 136) && (_dl_g_ld_debug_verbosity & 0x80000000) == 0 )
    {
      if ( (*(result + 432) & 1) != 0 )
        v3 = *(result + 448);
      else
        v3 = (result + 433);
      _dl__Z10linker_logiPKcz(0xFFFFFFFFLL, "\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", v3);
    }
    for ( i = *(v2 + 288); i; i = *i )
      _dl__ZN6soinfo17call_constructorsEv(i[1]);
    if ( (*(v2 + 48) & 0x10) == 0 )
    {
      _dl__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2IDnEEPKc(v11, "calling constructors: ");
      if ( (*(v2 + 432) & 1) != 0 )
        v5 = *(v2 + 448);
      else
        v5 = v2 + 433;
      v6 = _dl__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc(v11, v5);
      v7 = *(v6 + 16);
      v13 = *v6;
      v14 = v7;
      *(v6 + 8) = 0LL;
      *(v6 + 16) = 0LL;
      *v6 = 0LL;
      if ( (v13 & 1) != 0 )
        v8 = v14;
      else
        v8 = &v13 + 1;
      _dl__Z18bionic_trace_beginPKc(v8);
      if ( (v13 & 1) != 0 )
        _dl__ZdlPv(v14);
      if ( (v11[0] & 1) != 0 )
        _dl__ZdlPv(v12);
    }
    if ( (*(v2 + 432) & 1) != 0 )
      v9 = *(v2 + 448);
    else
      v9 = v2 + 433;
    _dl__ZL13call_functionPKcPFviPPcS2_ES0_("DT_INIT", *(v2 + 184), v9);
    if ( (*(v2 + 432) & 1) != 0 )
      v10 = *(v2 + 448);
    else
      v10 = v2 + 433;
    result = _dl__ZL10call_arrayIPFviPPcS1_EEvPKcPT_mbS5_("DT_INIT_ARRAY", *(v2 + 152), *(v2 + 160), v10);
    if ( (*(v2 + 48) & 0x10) == 0 )
      return _dl__Z16bionic_trace_endv(result);
  }
  return result;
}
__int64 __fastcall _dl__ZN6soinfo17call_constructorsEv(__int64 result)
{
  char v1; // w8
  __int64 v2; // x19
  const char *v3; // x2
  _QWORD *i; // x20
  __int64 v5; // x1
  __int64 v6; // x0
  __int64 v7; // x8
  char *v8; // x0
  __int64 v9; // x2
  __int64 v10; // x3
  char v11[16]; // [xsp+8h] [xbp-38h] BYREF
  __int64 v12; // [xsp+18h] [xbp-28h]
  __int128 v13; // [xsp+20h] [xbp-20h] BYREF
  char *v14; // [xsp+30h] [xbp-10h]
 
  if ( !(_dl_g_is_ldd | *(result + 248)) )
  {
    v1 = *(result + 48);
    v2 = result;
    *(result + 248) = 1;
    if ( (v1 & 4) == 0 && *(result + 136) && (_dl_g_ld_debug_verbosity & 0x80000000) == 0 )
    {
      if ( (*(result + 432) & 1) != 0 )
        v3 = *(result + 448);
      else
        v3 = (result + 433);
      _dl__Z10linker_logiPKcz(0xFFFFFFFFLL, "\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", v3);
    }
    for ( i = *(v2 + 288); i; i = *i )
      _dl__ZN6soinfo17call_constructorsEv(i[1]);
    if ( (*(v2 + 48) & 0x10) == 0 )
    {
      _dl__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2IDnEEPKc(v11, "calling constructors: ");
      if ( (*(v2 + 432) & 1) != 0 )
        v5 = *(v2 + 448);
      else
        v5 = v2 + 433;
      v6 = _dl__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc(v11, v5);
      v7 = *(v6 + 16);
      v13 = *v6;
      v14 = v7;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2025-3-13 17:07 被xianyuuuan编辑 ,原因:
收藏
免费 91
支持
分享
赞赏记录
参与人
雪币
留言
时间
浮夸进进
这个讨论对我很有帮助,谢谢!
56分钟前
小秃秃驴
谢谢你的细致分析,受益匪浅!
4小时前
mb_wyiavgqy
这个讨论对我很有帮助,谢谢!
18小时前
白发青丝
你的分享对大家帮助很大,非常感谢!
5天前
mb_nymhrhcn
非常支持你的观点!
2025-4-18 15:20
BeanSpy
这个讨论对我很有帮助,谢谢!
2025-4-18 15:19
方北七
谢谢你的细致分析,受益匪浅!
2025-4-17 15:15
xxRea
为你点赞!
2025-4-16 18:08
wx_ㅤ_683
谢谢你的细致分析,受益匪浅!
2025-4-15 11:02
leiusurely
你的分享对大家帮助很大,非常感谢!
2025-4-15 06:57
螺丝兔
非常支持你的观点!
2025-4-13 17:27
六尘之幻
非常支持你的观点!
2025-4-12 02:12
看雪高研
感谢你的贡献,论坛因你而更加精彩!
2025-4-11 13:51
wake0p
非常支持你的观点!
2025-4-10 22:04
拾海
感谢你分享这么好的资源!
2025-4-9 10:04
AA_cups
你的分享对大家帮助很大,非常感谢!
2025-4-8 13:35
mb_qecctkwn
期待更多优质内容的分享,论坛有你更精彩!
2025-4-6 14:44
wx_Kiyose
感谢你的贡献,论坛因你而更加精彩!
2025-4-1 21:19
wx_Kj
感谢你的积极参与,期待更多精彩内容!
2025-4-1 10:23
mb_rqgxdcwg
这个讨论对我很有帮助,谢谢!
2025-3-31 18:50
道友请留步
感谢你的贡献,论坛因你而更加精彩!
2025-3-31 11:17
轻快笑着行
你的分享对大家帮助很大,非常感谢!
2025-3-31 09:46
despacido~~
为你点赞!
2025-3-30 00:42
mb_qqnashwi
感谢你的积极参与,期待更多精彩内容!
2025-3-29 18:42
小向i小葵
谢谢你的细致分析,受益匪浅!
2025-3-26 13:11
toy乐
感谢你分享这么好的资源!
2025-3-26 11:10
U3D_Modder
感谢你分享这么好的资源!
2025-3-25 22:18
MGo
期待更多优质内容的分享,论坛有你更精彩!
2025-3-24 16:16
mb_lxhkuqnh
谢谢你的细致分析,受益匪浅!
2025-3-24 14:02
mb_bivtnjdh
为你点赞!
2025-3-24 10:03
mb_nwjlihsi
这个讨论对我很有帮助,谢谢!
2025-3-22 09:47
叮叮当_468844
这个讨论对我很有帮助,谢谢!
2025-3-22 09:04
黎明与黄昏
+1
感谢你的贡献,论坛因你而更加精彩!
2025-3-21 10:48
祝今朝
+1
为你点赞!
2025-3-20 16:29
江左梅郎
感谢你的贡献,论坛因你而更加精彩!
2025-3-20 15:44
wx_小小的太阳_701
为你点赞!
2025-3-20 13:42
qqizai
谢谢你的细致分析,受益匪浅!
2025-3-20 09:25
git_51636elvi7major
非常支持你的观点!
2025-3-20 05:47
vmoveq
+1
这个讨论对我很有帮助,谢谢!
2025-3-19 22:13
mb_hdiusdyf
感谢你的贡献,论坛因你而更加精彩!
2025-3-19 20:31
mb_fvjroydy
为你点赞!
2025-3-19 18:47
kinglinzi
非常支持你的观点!
2025-3-19 16:25
mb_lnbtnasu
这个讨论对我很有帮助,谢谢!
2025-3-19 16:14
GEKEZYX
为你点赞!
2025-3-19 15:28
mb_ukgikikw
期待更多优质内容的分享,论坛有你更精彩!
2025-3-18 22:14
Ally Switch
+1
谢谢你的细致分析,受益匪浅!
2025-3-18 20:49
养只猫不好么
为你点赞!
2025-3-18 19:10
mb_ematwvte
这个讨论对我很有帮助,谢谢!
2025-3-18 18:35
优质胖虎
非常支持你的观点!
2025-3-18 15:55
逆向小玖
期待更多优质内容的分享,论坛有你更精彩!
2025-3-18 09:27
霸王茶几
非常支持你的观点!
2025-3-18 09:27
NPC2000
期待更多优质内容的分享,论坛有你更精彩!
2025-3-18 09:00
月清晖
你的帖子非常有用,感谢分享!
2025-3-18 08:44
mb_yczxfahc
期待更多优质内容的分享,论坛有你更精彩!
2025-3-18 04:33
mb_izfxicrp
感谢你的积极参与,期待更多精彩内容!
2025-3-18 04:21
Roxer
非常支持你的观点!
2025-3-18 02:02
neilwu
期待更多优质内容的分享,论坛有你更精彩!
2025-3-17 16:44
mb_whjlnevj
为你点赞!
2025-3-17 09:47
mb_gjttoonf
非常支持你的观点!
2025-3-17 09:27
JaniQuiz
你的分享对大家帮助很大,非常感谢!
2025-3-16 23:44
老小白
感谢你的积极参与,期待更多精彩内容!
2025-3-16 21:32
大肠刺身
期待更多优质内容的分享,论坛有你更精彩!
2025-3-16 12:58
nonovo
你的帖子非常有用,感谢分享!
2025-3-16 12:33
vVv一
你的分享对大家帮助很大,非常感谢!
2025-3-16 11:26
Je2em1ah
为你点赞!
2025-3-16 00:04
风兮木晓
你的帖子非常有用,感谢分享!
2025-3-15 20:59
mb_tgixxspz
感谢你分享这么好的资源!
2025-3-15 20:30
陈某人
这个讨论对我很有帮助,谢谢!
2025-3-15 16:38
mb_itubdqgo
为你点赞!
2025-3-15 16:38
mb_ooliergk
谢谢你的细致分析,受益匪浅!
2025-3-15 00:13
Our_OT
为你点赞!
2025-3-14 23:01
sinker_
为你点赞!
2025-3-14 18:47
酒仙桥之虎
非常支持你的观点!
2025-3-14 16:57
mb_bppcorlj
你的分享对大家帮助很大,非常感谢!
2025-3-14 16:32
残月_374878
感谢你分享这么好的资源!
2025-3-14 15:18
Python成长路
谢谢你的细致分析,受益匪浅!
2025-3-14 13:35
晨曦遇晓
期待更多优质内容的分享,论坛有你更精彩!
2025-3-14 11:51
cppasm
为你点赞!
2025-3-14 10:39
令狐双
感谢你分享这么好的资源!
2025-3-14 10:28
孤独的街
谢谢你的细致分析,受益匪浅!
2025-3-14 10:10
wuzhouzcx
为你点赞!
2025-3-14 09:35
mb_ioszxyua
你的帖子非常有用,感谢分享!
2025-3-14 09:12
mb_eamodmrp
为你点赞!
2025-3-14 09:05
null0bj
感谢你的贡献,论坛因你而更加精彩!
2025-3-14 07:52
dream的梦
为你点赞!
2025-3-14 05:27
绿雪羚羊
为你点赞!
2025-3-13 22:03
mb_fefksfsl
你的帖子非常有用,感谢分享!
2025-3-13 21:29
huangyalei
非常支持你的观点!
2025-3-13 21:07
哆啦噩梦
感谢你的积极参与,期待更多精彩内容!
2025-3-13 19:52
SomeMx
+1
非常支持你的观点!
2025-3-13 19:40
XCCCC
为你点赞!
2025-3-13 17:20
最新回复 (68)
雪    币: 41
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
学习一下
2025-3-13 18:45
1
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
u
2025-3-14 03:17
0
雪    币: 434
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
6666
2025-3-14 08:46
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
谢谢分享
2025-3-14 08:59
0
雪    币: 375
活跃值: (2586)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
666
2025-3-14 10:17
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
感谢
2025-3-14 10:44
0
雪    币: 1203
活跃值: (1240)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
学习一下
2025-3-14 13:23
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
Thank you for sharing
2025-3-14 13:37
0
雪    币: 104
活跃值: (5403)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
66666666666
2025-3-14 13:58
0
雪    币: 286
活跃值: (201)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
啊大苏打
2025-3-14 15:42
0
雪    币: 212
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
666666666666666666666666666666666
2025-3-14 16:37
0
雪    币: 14
活跃值: (320)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
2025-3-15 22:59
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
2025-3-17 09:08
0
雪    币: 292
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
这文件强呀
2025-3-17 15:11
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
1
2025-3-17 15:21
0
雪    币: 2534
活跃值: (2390)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
为你点赞!
2025-3-17 15:23
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
2025-3-17 15:55
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
666
2025-3-17 15:56
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
666
2025-3-17 16:49
0
雪    币: 8390
活跃值: (4446)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
学习一下
2025-3-17 18:09
0
雪    币: 254
活跃值: (455)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
1
2025-3-17 22:52
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
66
2025-3-18 08:27
0
雪    币: 3613
活跃值: (835)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
已读
2025-3-18 09:10
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
66
2025-3-18 09:52
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册