首页
社区
课程
招聘
从inlinehook角度检测frida
发表于: 2021-10-19 14:54 39335

从inlinehook角度检测frida

2021-10-19 14:54
39335

在使用frida的过程中,在我的认知里其实frida只做了2件事情,一件是注入,一件是hook,作为注入的特征我们可以通过ptrace(PTRACE_TRACEME,NULL,0,0),或者从文件里面索引有关frida字符串这样的方式来检测frida,那么作为hook特征是否也能够检测frida呢?

答案是肯定的,既然frida是能够定向跳转从而更改内存中的代码,那么一定是用到了inlinehook的技术,那么它是怎么使用的inlinehook呢,我们可以使用ida(当然直接看frida源码,但是那样真的很麻烦就连我自己写的inlinehook都很难快速的分析清楚)去看一下,我随便hook了一个函数比如art::ClassLinker::LoadMethod,注意要先附加frida然后再使用ida链接,否则frida无法附加已经被ptrace附加的进程,然后,结果如下图


可以看到frida的hook原理就是将函数的开头改成这样的一串16进制,0xd61f020058000050和我之前写的inlinehook差不多,只是它用了x16寄存器我用了x17寄存器,那么这里就有一个思路了,我们能否通过so中是否有这一段来判断用户使用了frida呢?答案是肯定的,但是需要我们稍作调整,就是在每个函数的开头检测是否有0xd61f020058000050这样的一段代码,创建线程即可,那么下面就开始实现,首先试一下单个函数能不能成功,我使用了上篇文章提到的,提取符号首地址的方式findsym

可以看到当我以attach的方式链接上去的时候,成功的检测到了frida,那么其实我们就可以遍历符号表来获得一个so中所有的函数首地址来检测是否使用了frida,由于之前也说过,某些函数不在导出表中,而在符号表中,所以可能没有办法通过程序头获取,所以系统so这方面用节头获取,自身so方面用程序头获取(自身so路径不太好确定,所以直接用内存中的就好),这里以libart.so和libnative-lib.so来做一个例子。首先就是libart.so,这里我封装了2个函数一个是获得所有符号的首地址,一个是获得大小都是通过节头表索引得到的,其中enumsym这个函数用到了2个int,第一个size是最大值,第二个start1是最小值,通过外部传入,因为有些符号表的值大于文件所以容易溢出

之后把这两个函数调用写入线程函数就好了,我这里直接用0x25000了通过libart.so的程序头可以解析出来,每10秒检测一次

看一下结果,效果不错就是检测的慢一点但是也达到了预定的目标

接着我们继续搞libnative-lib,这个由于目录特殊性所以我们很难找到它的目录从节头里面搜索符号地址,所以我们只能用程序头搜索它的导出函数,这个之前的文章也讲过,这里就直接贴代码了,这里注意一点,如何确定符号表的大小,仔细观察就知道符号表后面就是字符串表,那么用符号表偏移减去字符串表偏移就是符号表的大小

使用frida脚本hook Java_com_r0ysue_antifrida_MainActivity_stringFromJNI,看一下效果

上篇文章说了Java hook,的做法是将Java函数转化为Native函数,所以我们可以在关键函数上检测是否是Native的方法来检测frida,当然也可以用解析dex的方式来获得所有的类表和所有的函数名,我这里就先不搞了,有机会下篇文章再搞,我这里只提供一个最简单的方式,就是通过Methid来获得ArtMethod的方法检测是否是Native函数,具体做法可以参考上一篇文章,我这里直接贴代码了,我这里使用反射,但是注意由于线程不同所以env不同,不能将主线程的env传入检测线程使用。

以attach的方式附加检测效果如下

我这里提供的frida检测方法还是比较简单的,当然可以加上各种混淆和自实现线程创建函数,又或者是fork子进程,今天的内容就到这里,感谢大家观看。

function main() {
    var libart = Module.enumerateSymbols("libart.so");
    var addr = NULL;
    for (var n in libart) {
        if (libart[n].name.indexOf("lassLinker10LoadMethodERKNS") >= 0) {
            addr = libart[n].address;
            break;
        }
    }
    Interceptor.attach(addr, {
        onEnter: function (arg) {
 
        }
 
    })
 
}
function main() {
    var libart = Module.enumerateSymbols("libart.so");
    var addr = NULL;
    for (var n in libart) {
        if (libart[n].name.indexOf("lassLinker10LoadMethodERKNS") >= 0) {
            addr = libart[n].address;
            break;
        }
    }
    Interceptor.attach(addr, {
        onEnter: function (arg) {
 
        }
 
    })
 
}
void anti1(){
while (1) {
    sleep(1);
    int so = findsym("/system/lib64/libart.so",
                     "_ZN3art11ClassLinker10LoadMethodERKNS_7DexFileERKNS_21ClassDataItemIteratorENS_6HandleINS_6mirror5ClassEEEPNS_9ArtMethodE");
 
    long long as = *(long long *) reinterpret_cast<long>((char *) startr - 0x25000 + so);
 
    if(as==0xd61f020058000050) {
        __android_log_print(6, "r0ysue", "i find frida %p",(long long *) reinterpret_cast<long>((char *) startr - 0x25000 + so));
    }
}
 
}
    pthread_create(&thread, nullptr, reinterpret_cast<void *(*)(void *)>(anti1), nullptr);
void anti1(){
while (1) {
    sleep(1);
    int so = findsym("/system/lib64/libart.so",
                     "_ZN3art11ClassLinker10LoadMethodERKNS_7DexFileERKNS_21ClassDataItemIteratorENS_6HandleINS_6mirror5ClassEEEPNS_9ArtMethodE");
 
    long long as = *(long long *) reinterpret_cast<long>((char *) startr - 0x25000 + so);
 
    if(as==0xd61f020058000050) {
        __android_log_print(6, "r0ysue", "i find frida %p",(long long *) reinterpret_cast<long>((char *) startr - 0x25000 + so));
    }
}
 
}
    pthread_create(&thread, nullptr, reinterpret_cast<void *(*)(void *)>(anti1), nullptr);
int* enumsym(const char* lib,int size,int start1){
    int fd;
    void *start;
    struct stat sb;
    fd = open(lib, O_RDONLY);
    fstat(fd, &sb);
    start = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    Elf64_Ehdr header;
    memcpy(&header, start, sizeof(Elf64_Ehdr));
    int secoff = header.e_shoff;
    int secsize = header.e_shentsize;
    int secnum = header.e_shnum;
    int secstr = header.e_shstrndx;
    Elf64_Shdr strtab;
    memcpy(&strtab, (char *) start + secoff + secstr * secsize, sizeof(Elf64_Shdr));
    int strtaboff = strtab.sh_offset;
    char strtabchar[strtab.sh_size];
    memcpy(&strtabchar, (char *) start + strtaboff, strtab.sh_size);
    Elf64_Shdr enumsec;
    int gotoff = 0;
    int gotsize = 0;
    int strtabsize = 0;
    int stroff = 0;
    for (int n = 0; n < secnum; n++) {
        memcpy(&enumsec, (char *) start + secoff + n * secsize, sizeof(Elf64_Shdr));
        if (strcmp(&strtabchar[enumsec.sh_name], ".symtab") == 0) {
            gotoff = enumsec.sh_offset;
            gotsize = enumsec.sh_size;
        }
        if (strcmp(&strtabchar[enumsec.sh_name], ".strtab") == 0) {
            stroff = enumsec.sh_offset;
            strtabsize = enumsec.sh_size;
 
        }
    }
    int realoff=0;
    char relstr[strtabsize];
    Elf64_Sym tmp;
    memcpy(&relstr, (char *) start + stroff, strtabsize);
int* sdc= static_cast<int *>(malloc(gotsize / sizeof(Elf64_Sym)*sizeof(int *)));//存储返回值数组
    for (int n = 0; n < gotsize; n = n + sizeof(Elf64_Sym)) {
        memcpy(&tmp, (char *)start + gotoff+n, sizeof(Elf64_Sym));
//        __android_log_print(6, "r0ysue", "%x",gotoff+n);
            sdc[n/sizeof(Elf64_Sym)]=tmp.st_value;
            if(tmp.st_value>size||tmp.st_value<start1)
                sdc[n/sizeof(Elf64_Sym)]=start1;
    }
    return sdc;
}
 
int getsymsize(const char* lib){
    int fd;
    void *start;
    struct stat sb;
    fd = open(lib, O_RDONLY); /*打开/etc/passwd */
    fstat(fd, &sb); /* 取得文件大小 */
    start = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    Elf64_Ehdr header;
    memcpy(&header, start, sizeof(Elf64_Ehdr));
    int secoff = header.e_shoff;
    int secsize = header.e_shentsize;
    int secnum = header.e_shnum;
    int secstr = header.e_shstrndx;
    Elf64_Shdr strtab;
    memcpy(&strtab, (char *) start + secoff + secstr * secsize, sizeof(Elf64_Shdr));
    int strtaboff = strtab.sh_offset;
    char strtabchar[strtab.sh_size];
    memcpy(&strtabchar, (char *) start + strtaboff, strtab.sh_size);
    Elf64_Shdr enumsec;
    int gotoff = 0;
    int gotsize = 0;
    int strtabsize = 0;
    int stroff = 0;
    for (int n = 0; n < secnum; n++) {
 
        memcpy(&enumsec, (char *) start + secoff + n * secsize, sizeof(Elf64_Shdr));
 
 
        if (strcmp(&strtabchar[enumsec.sh_name], ".symtab") == 0) {
            gotoff = enumsec.sh_offset;
            gotsize = enumsec.sh_size;
 
        }
        if (strcmp(&strtabchar[enumsec.sh_name], ".strtab") == 0) {
            stroff = enumsec.sh_offset;
            strtabsize = enumsec.sh_size;
 
        }
 
 
    }
    return gotsize/sizeof(Elf64_Sym);
 
}
int* enumsym(const char* lib,int size,int start1){
    int fd;
    void *start;
    struct stat sb;
    fd = open(lib, O_RDONLY);
    fstat(fd, &sb);
    start = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    Elf64_Ehdr header;
    memcpy(&header, start, sizeof(Elf64_Ehdr));
    int secoff = header.e_shoff;
    int secsize = header.e_shentsize;
    int secnum = header.e_shnum;
    int secstr = header.e_shstrndx;
    Elf64_Shdr strtab;
    memcpy(&strtab, (char *) start + secoff + secstr * secsize, sizeof(Elf64_Shdr));
    int strtaboff = strtab.sh_offset;
    char strtabchar[strtab.sh_size];
    memcpy(&strtabchar, (char *) start + strtaboff, strtab.sh_size);
    Elf64_Shdr enumsec;
    int gotoff = 0;
    int gotsize = 0;
    int strtabsize = 0;
    int stroff = 0;
    for (int n = 0; n < secnum; n++) {
        memcpy(&enumsec, (char *) start + secoff + n * secsize, sizeof(Elf64_Shdr));
        if (strcmp(&strtabchar[enumsec.sh_name], ".symtab") == 0) {
            gotoff = enumsec.sh_offset;
            gotsize = enumsec.sh_size;
        }
        if (strcmp(&strtabchar[enumsec.sh_name], ".strtab") == 0) {
            stroff = enumsec.sh_offset;
            strtabsize = enumsec.sh_size;
 
        }
    }
    int realoff=0;
    char relstr[strtabsize];
    Elf64_Sym tmp;
    memcpy(&relstr, (char *) start + stroff, strtabsize);
int* sdc= static_cast<int *>(malloc(gotsize / sizeof(Elf64_Sym)*sizeof(int *)));//存储返回值数组
    for (int n = 0; n < gotsize; n = n + sizeof(Elf64_Sym)) {
        memcpy(&tmp, (char *)start + gotoff+n, sizeof(Elf64_Sym));
//        __android_log_print(6, "r0ysue", "%x",gotoff+n);
            sdc[n/sizeof(Elf64_Sym)]=tmp.st_value;
            if(tmp.st_value>size||tmp.st_value<start1)
                sdc[n/sizeof(Elf64_Sym)]=start1;
    }
    return sdc;
}
 
int getsymsize(const char* lib){
    int fd;
    void *start;
    struct stat sb;
    fd = open(lib, O_RDONLY); /*打开/etc/passwd */
    fstat(fd, &sb); /* 取得文件大小 */
    start = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    Elf64_Ehdr header;
    memcpy(&header, start, sizeof(Elf64_Ehdr));
    int secoff = header.e_shoff;
    int secsize = header.e_shentsize;
    int secnum = header.e_shnum;
    int secstr = header.e_shstrndx;
    Elf64_Shdr strtab;
    memcpy(&strtab, (char *) start + secoff + secstr * secsize, sizeof(Elf64_Shdr));
    int strtaboff = strtab.sh_offset;
    char strtabchar[strtab.sh_size];
    memcpy(&strtabchar, (char *) start + strtaboff, strtab.sh_size);
    Elf64_Shdr enumsec;
    int gotoff = 0;
    int gotsize = 0;
    int strtabsize = 0;
    int stroff = 0;
    for (int n = 0; n < secnum; n++) {
 
        memcpy(&enumsec, (char *) start + secoff + n * secsize, sizeof(Elf64_Shdr));
 
 
        if (strcmp(&strtabchar[enumsec.sh_name], ".symtab") == 0) {
            gotoff = enumsec.sh_offset;
            gotsize = enumsec.sh_size;
 
        }
        if (strcmp(&strtabchar[enumsec.sh_name], ".strtab") == 0) {
            stroff = enumsec.sh_offset;
            strtabsize = enumsec.sh_size;
 
        }
 
 
    }
    return gotsize/sizeof(Elf64_Sym);
 
}
    int *so ;
    int size ;//写到全局变量里面循环就不会多次调用了
 
void anti1(){
while (1) {
    pthread_mutex_lock(&mutex);
 
 
    for (int n = 0; n < size; n++) {
//    __android_log_print(6, "r0ysue", "i find frida %p %x %x",(long long ) reinterpret_cast<long>((char *) startr - 0x25000 + so[n]),so[n],n);
 
        long long as = *(long long *) reinterpret_cast<long>((char *) startr - 0x25000 + so[n]);
//        __android_log_print(6, "r0ysue", "i find frida %d %x %p",n,so[n],as);
        if (as == 0xd61f020058000050) {
            __android_log_print(6, "r0ysue", "i find frida %p",
                                (long long *) reinterpret_cast<long>((char *) startr - 0x25000 +
                                                                     so[n]));
        }
    }
    sleep(5);
    pthread_mutex_unlock(&mutex);
}
}
void __init(){
 
 
     so = enumsym("/system/lib64/libart.so", (long) end - (long) startr,0x25000);
     size = getsymsize("/system/lib64/libart.so");
}
    int *so ;
    int size ;//写到全局变量里面循环就不会多次调用了
 
void anti1(){
while (1) {
    pthread_mutex_lock(&mutex);
 
 
    for (int n = 0; n < size; n++) {
//    __android_log_print(6, "r0ysue", "i find frida %p %x %x",(long long ) reinterpret_cast<long>((char *) startr - 0x25000 + so[n]),so[n],n);
 
        long long as = *(long long *) reinterpret_cast<long>((char *) startr - 0x25000 + so[n]);
//        __android_log_print(6, "r0ysue", "i find frida %d %x %p",n,so[n],as);
        if (as == 0xd61f020058000050) {
            __android_log_print(6, "r0ysue", "i find frida %p",
                                (long long *) reinterpret_cast<long>((char *) startr - 0x25000 +
                                                                     so[n]));
        }
    }
    sleep(5);
    pthread_mutex_unlock(&mutex);
}
}
void __init(){
 
 
     so = enumsym("/system/lib64/libart.so", (long) end - (long) startr,0x25000);
     size = getsymsize("/system/lib64/libart.so");
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 6
支持
分享
最新回复 (26)
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
可能你会误认为是找茬?但是我还是不得不说一下看法、
以偏概全?
frda使用了0xd61f020058000050跳转代码进行hook,但是不代表所有使用0xd61f020058000050的都是frida。
前面也有人发过用hook代码来检测frida,我同样表达了类似观点。
2021-10-19 17:06
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
3
确实 所以说这里只是一个检测角度  并没有说通杀frida  
这种方式的绕过方法也很简单就是 更改 frida-gum/tests/core/arch-arm64/arm64relocator.c 中的汇编代码 将x16寄存器改为x17  
这两种方式是我在写完前两篇文章之后的 一个突发奇想的检测方法 攻防是不断进步的  我这里只是提出一种思路
2021-10-19 17:31
0
雪    币: 225
活跃值: (1089)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
4
tDasm 可能你会误认为是找茬?但是我还是不得不说一下看法、 以偏概全? frda使用了0xd61f020058000050跳转代码进行hook,但是不代表所有使用0xd61f020058000050的都是 ...
上次你没赢,这次你赢了
2021-10-19 17:32
0
雪    币: 228
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
r0ysue 确实 所以说这里只是一个检测角度 并没有说通杀frida 这种方式的绕过方法也很简单就是 更改 frida-gum/tests/core/arch-arm64/arm64relocator.c ...
我觉得这里主要还是还是那个地址的问题,
LDR X16,=loc_xxx  ,很明显,loc_xxx有可能是frida的函数,而且最主要的是,这个地址并不是写死的,因为so一旦加载顺序不一样或者其他原因,就会导致每个so的函数地址可能不一样。
所以这里hook后,汇编肯定是 LDR X12,=loc_xxx   br x16,但loc_xxx的值很可能不一样。

应该先判断汇编是不是  LDR 某寄存器,=loc_xxx   br 某寄存器,然后判断loc_xxx是不是frida的代码地址。

最后就能确定,感觉还是要针对性去弄,去看frida那个版本怎么hook的。
2021-10-20 01:01
0
雪    币: 29
活跃值: (5647)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
这和frida有啥关系呢......这个叫检测aarch64下inlinehook还差不多......
2021-10-20 04:10
0
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
r0ysue 确实 所以说这里只是一个检测角度 并没有说通杀frida 这种方式的绕过方法也很简单就是 更改 frida-gum/tests/core/arch-arm64/arm64relocator.c ...
本论坛早就发过这种思路。你不是第一个好吧,版主!
2021-10-20 08:12
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
8

1

最后于 2021-10-20 08:56 被r0ysue编辑 ,原因:
2021-10-20 08:56
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
9
mb_hgrbqfun 我觉得这里主要还是还是那个地址的问题, LDR X16,=loc_xxx ,很明显,loc_xxx有可能是frida的函数,而且最主要的是,这个地址并不是写死的,因为so一旦加载顺序不一样或者其他 ...
在frida-gum/tests/core/arch-arm64/arm64relocator.c
中有代码 因为64位操作不了pc  所以跳转代码用的就是 ldr x16,#8是固定的 
2021-10-20 08:57
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
10
tDasm 本论坛早就发过这种思路。你不是第一个好吧,版主!
我也没说我是第一个发现的呀  自己实现过一个inlinehook就自然有通过inlinehook检测frida这个想法
再说你但凡 把我俩的文章一字一句的看完都知道我们的思路有区别
2021-10-20 08:59
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
11
不吃早饭 这和frida有啥关系呢......这个叫检测aarch64下inlinehook还差不多......
因为frida 官方版本中用到了这种方法 所以才想到这么搞  
2021-10-20 09:00
0
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
r0ysue 我也没说我是第一个发现的呀  自己实现过一个inlinehook就自然有通过inlinehook检测frida这个想法 再说你但凡 把我俩的文章一字一句的看完都知道我们的思路有区别

https://bbs.pediy.com/thread-268586.htm
你自己去看,有什么本质区别?区别就是别人只检测某函数入口字节,而你是检测多个函数。既然叫思路就应该与从不同,如果一样那就不是思路而是引申。

最后于 2021-10-20 14:06 被tDasm编辑 ,原因:
2021-10-20 14:05
0
雪    币: 852
活跃值: (9821)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
13
谢谢分享. 思路很重要. 
2021-10-20 14:15
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
14
tDasm r0ysue 我也没说我是第一个发现的呀 &nbsp;自己实现过一个inlinehook就自然有通过inlinehook检测frida这个 ...
咱俩出发点不同 我们的目的就是让每一个小白都能明白能手写这方面的内容能解决遇到的坑 
如果是知识相关的内容我们一定有问必答 像你上面的问题我回答了  
接口都是我自己封装的我也把源码贴出来了你说一样就一样吧 
如果你的出发点就是喷我的话 我也没啥好说的  
2021-10-20 15:09
0
雪    币: 208
活跃值: (1986)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
15
目前我们用的检测也都基于这样的逻辑、但是一般不会去扫内存、而是针对性的扫某些特殊函数头、这就解决了上面这个疑问、特定的函数一般不会出现这样LDR BR模式的。但是事无绝对,万一厂商真的碎片化呢?所以就要分析得到BR的跳转内存地址去、去看内存地址的模块地址空间。
检测并不是100%的看一个点一棍子打死、而是结合更多的特征来综合判断。
2021-10-20 15:15
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
16
大魔头 目前我们用的检测也都基于这样的逻辑、但是一般不会去扫内存、而是针对性的扫某些特殊函数头、这就解决了上面这个疑问、特定的函数一般不会出现这样LDR BR模式的。但是事无绝对,万一厂商真的碎片化呢?所以就 ...
感谢大佬补充
2021-10-20 15:37
0
雪    币: 986
活跃值: (6167)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
17
记笔记记笔记
2021-10-20 16:18
0
雪    币: 1601
活跃值: (5159)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
18
Ssssone 记笔记记笔记
大佬 反正你的文章我看完都记笔记上了 
2021-10-20 17:30
0
雪    币: 0
活跃值: (1510)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
大佬牛逼
2021-10-22 14:36
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
方法挺好,就是写的太乱了
2022-9-7 10:01
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
tDasm 可能你会误认为是找茬?但是我还是不得不说一下看法、 以偏概全? frda使用了0xd61f020058000050跳转代码进行hook,但是不代表所有使用0xd61f020058000050的都是 ...
老哥,你是个杠精吧, 有人分享 我就能学到。大佬不分享,我去哪儿学啊
2024-10-25 20:37
0
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
mb_zfqvurgb 老哥,你是个杠精吧, 有人分享 我就能学到。大佬不分享,我去哪儿学啊
你这样的脑袋是非不分能学到什么?他错了也不能指出来。
2024-10-26 10:36
1
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
tDasm 你这样的脑袋是非不分能学到什么?他错了也不能指出来。
杠精,04年注册的,也没见过发过一个贴,老杠精
2024-10-27 00:49
0
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
mb_zfqvurgb 杠精,04年注册的,也没见过发过一个贴,老杠精
没思维能力还出来杠?哪怪是临时工。看雪不欢迎你就躲远点!
2024-10-27 11:25
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
tDasm 没思维能力还出来杠?哪怪是临时工。看雪不欢迎你就躲远点!
早就听过你这个老杠精的大名了,果然名副其实
2024-10-27 12:53
0
游客
登录 | 注册 方可回帖
返回
//