黑盒测试-动态库劫持-so注入-Dll注入

在一些比较复杂的出程序中,特别是被混淆过的程序,要对这种程序进行静态分析是很困难的,这时候黑盒测试就成了分析函数的不二之选,本分将会讲述Windows如何利用Dll注入进行黑盒测试。

方法

  1. 编写注入dll、so
  2. 进行注入测试

举例

源程序下载,文章最后会给出源码:http://file.eonew.cn/pe/crackme_dll.exe

IDA查看源程序。

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3; // eax
  char v5[32]; // [esp+0h] [ebp-38h]
  char v6[4]; // [esp+20h] [ebp-18h]
  unsigned int i; // [esp+34h] [ebp-4h]

  strcpy(v6, "yougettheflag");
  puts(aInputYourFlag);
  scanf(a30s, v5);
  if ( sub_40179E(v5) == 1 )
  {
    for ( i = 0; ; ++i )
    {
      v3 = strlen(v6);
      if ( i >= v3 )
        break;
      if ( sub_401000((unsigned __int8)v5[i]) != v6[i] - 97 )
      {
        puts(aFailed);
        return 0;
      }
    }
    puts(aSuccess);
  }
  else
  {
    puts(aFailed_0);
  }
  return 0;
}

这里先说明一下sub_40179E函数的功能是将输入的字符串转换成十六进制。而sub_401000函数是经过了混淆,其原本的功能很难分析出来,但是我编写的时候就是将它写成了一个对应的输入输出表,所以我们只需获得这张表,即可以用这张表来还原输入。

sub_401000

signed int __usercall sub_401000@<eax>(int a1@<ebx>, int a2@<ebp>, signed int a3@<edi>, signed int a4)
{
  signed int v4; // edx
  int v5; // ecx
  signed int v6; // esi
  signed int v7; // edi
  signed int v8; // ebx
  signed int v9; // ebp
  int v10; // edx
  signed int v11; // ecx
  int v12; // eax
  bool v13; // sf
  unsigned __int8 v14; // of
  signed int v15; // eax
  bool v16; // al
  signed int v17; // edi
  int v18; // esi
  int v19; // esi
  signed int v20; // eax
  bool v21; // sf
  unsigned __int8 v22; // of
  int v23; // edi
  bool v25; // [esp+3h] [ebp-47Dh]
  int v26; // [esp+4h] [ebp-47Ch]
  int *v27; // [esp+8h] [ebp-478h]
  int v28; // [esp+Ch] [ebp-474h]
  int v29; // [esp+10h] [ebp-470h]
  int v30; // [esp+14h] [ebp-46Ch]
  int v31; // [esp+18h] [ebp-468h]
  int v32; // [esp+1Ch] [ebp-464h]
  int v33; // [esp+20h] [ebp-460h]
  signed int v34; // [esp+24h] [ebp-45Ch]
  signed int v35; // [esp+24h] [ebp-45Ch]
  int v36; // [esp+28h] [ebp-458h]
  int *v37; // [esp+2Ch] [ebp-454h]
  int v38; // [esp+30h] [ebp-450h]
  int v39; // [esp+34h] [ebp-44Ch]
  int *v40; // [esp+3Ch] [ebp-444h]
  int v41; // [esp+40h] [ebp-440h]
  int v42; // [esp+44h] [ebp-43Ch]
  int v43; // [esp+48h] [ebp-438h]
  int v44; // [esp+4Ch] [ebp-434h]
  int v45; // [esp+50h] [ebp-430h]
  int v46; // [esp+54h] [ebp-42Ch]
  signed int v47; // [esp+5Ch] [ebp-424h]
  int v48; // [esp+60h] [ebp-420h]
  int v49; // [esp+64h] [ebp-41Ch]
  int v50; // [esp+68h] [ebp-418h]
  int v51; // [esp+6Ch] [ebp-414h]
  int v52[260]; // [esp+70h] [ebp-410h]

  v4 = a4;
  v5 = 548813401;
  v6 = 1881953091;
  if ( a4 > 255 )
    v5 = -1415659758;
  v33 = a4 - 1;
  v32 = a4 - 1;
  v31 = a4 - 1;
  v30 = a4 - 1;
  v28 = a4 - 1;
  v47 = v5;
  v29 = a4 - 1;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
LABEL_6:
          while ( v6 <= 514714396 )
          {
            if ( v6 > 86368825 )
            {
              if ( v6 == 86368826 )
              {
                v5 = v41;
                v6 = -994522485;
                v29 = v52[v41];
                goto LABEL_69;
              }
              if ( v6 == 190114385 )
              {
                v6 = 1319777738;
                v32 = v52[v4];
                goto LABEL_6;
              }
              if ( v6 != 400573796 )
                goto LABEL_5;
LABEL_4:
              v6 = -1534202243;
              a3 = -1;
              goto LABEL_5;
            }
            if ( v6 == -444507636 )
            {
              a2 = v52[v4];
              v6 = 1660167534;
              goto LABEL_6;
            }
            if ( v6 != -395968525 )
            {
              if ( v6 == 6163799 )
              {
                v6 = -1798225969;
                v31 = v52[v44];
                goto LABEL_69;
              }
              goto LABEL_5;
            }
            v44 = a4 - 1;
            v31 = 0x1000000;
            v6 = (((a4 - 1) >> 31) & 0x94733278) + 6163799;
            if ( v6 <= -444507637 )
              goto LABEL_69;
          }
          if ( v6 > 1319777737 )
            break;
          if ( v6 != 514714397 )
          {
            if ( v6 == 548813401 )
            {
              v6 = -2109195768;
              a1 = 1;
              v40 = v52;
              v52[0] = 0;
              goto LABEL_69;
            }
            if ( v6 != 783469431 )
              goto LABEL_5;
            v48 = a2;
            v34 = a3;
            v7 = 1176118954;
            v8 = -2049448533;
            v9 = -236476695;
            v10 = v30 + 1;
            if ( v49 > v50 )
              v7 = -811602148;
            if ( v50 > v10 )
              v8 = -1594435630;
            v11 = 414635068;
            v12 = -305545985;
            if ( v49 > v10 )
              v11 = -305545985;
            while ( 1 )
            {
              do
              {
LABEL_25:
                while ( v9 > 414635067 )
                {
                  if ( v9 == 414635068 )
                  {
                    v12 = v49;
                    v9 = -1822626534;
                    goto LABEL_35;
                  }
                  if ( v9 != 1176118954 )
                    goto LABEL_24;
                  v9 = v11;
                  if ( v11 <= -305545986 )
                    goto LABEL_35;
                }
                if ( v9 == -305545985 )
                  goto LABEL_23;
                if ( v9 != -236476695 )
                  goto LABEL_24;
                v9 = v7;
              }
              while ( v7 > -305545986 );
              do
              {
                while ( 1 )
                {
LABEL_35:
                  while ( v9 <= -1594435631 )
                  {
                    if ( v9 != -2049448533 )
                    {
                      if ( v9 == -1822626534 )
                      {
                        v5 = v26;
                        v4 = a4;
                        v6 = -2109195768;
                        v52[v26] = v12;
                        a3 = v34;
                        a2 = v48;
                        a1 = v26 + 1;
                        goto LABEL_69;
                      }
                      goto LABEL_24;
                    }
                    v9 = -1822626534;
                    v12 = v50;
                  }
                  if ( v9 == -1594435630 )
                  {
LABEL_23:
                    v9 = -1822626534;
                    v12 = v30 + 1;
                    goto LABEL_24;
                  }
                  if ( v9 == -811602148 )
                    break;
LABEL_24:
                  if ( v9 > -305545986 )
                    goto LABEL_25;
                }
                v9 = v8;
              }
              while ( v8 <= -305545986 );
            }
          }
          v50 = v28 + 1;
          v5 = v26 - 7;
          v43 = v26 - 7;
          v30 = 0x1000000;
          v6 = (((v26 - 7) >> 31) & 0x96B0986C) - 1744686325;
          if ( v6 <= -444507637 )
            goto LABEL_69;
        }
        if ( v6 == 1319777738 )
          break;
        if ( v6 == 1660167534 )
        {
          v14 = __OFSUB__(a2, 0x1000000);
          v13 = a2 - 0x1000000 < 0;
          v6 = 400573796;
          v15 = -395968525;
LABEL_66:
          if ( v13 ^ v14 )
            v6 = v15;
          if ( v6 <= -444507637 )
            goto LABEL_69;
        }
        else
        {
          if ( v6 != 1881953091 )
            goto LABEL_5;
          v6 = v47;
          if ( v47 <= -444507637 )
            goto LABEL_69;
        }
      }
      v6 = 400573796;
      if ( v51 != v32 )
        v6 = -751818089;
      if ( v6 > -444507637 )
        goto LABEL_6;
LABEL_69:
      if ( v6 > -1534202244 )
        break;
      if ( v6 <= -1744686326 )
      {
        if ( v6 == -2109195768 )
        {
          v26 = a1;
          v6 = -1882325015;
          v15 = -1735363298;
          v5 = a1;
          v14 = __OFSUB__(a1, 256);
          v13 = a1 - 256 < 0;
          goto LABEL_66;
        }
        if ( v6 != -1882325015 )
        {
          if ( v6 == -1798225969 )
          {
            v6 = 190114385;
            v5 = 1319777738;
            v51 = v31;
            v16 = v25;
            v32 = 0x1000000;
            goto LABEL_119;
          }
          goto LABEL_5;
        }
        v36 = a1;
        v35 = a3;
        v17 = 1227597061;
        v18 = 0;
LABEL_91:
        while ( v17 <= 1227597060 )
        {
          if ( v17 != 307379757 )
          {
            if ( v17 == 385223167 )
            {
              v17 = -1437864421;
              a1 = a2;
              v5 = v39 + 1;
              goto LABEL_103;
            }
            goto LABEL_90;
          }
          a2 = v46;
          v17 = 385223167;
        }
        if ( v17 == 2045173338 )
        {
          v17 = -1437864421;
          v37 = &v40[v38];
          v45 = v38 + 1;
          a1 = (int)&v40[v38];
          v5 = v38 + 1;
          goto LABEL_103;
        }
        if ( v17 == 1227597061 )
        {
          v38 = v18;
          v20 = 2045173338;
          v22 = __OFSUB__(v18, 256);
          v21 = v18 - 256 < 0;
          v17 = -2078856237;
          goto LABEL_100;
        }
        while ( 1 )
        {
LABEL_90:
          if ( v17 > 307379756 )
            goto LABEL_91;
LABEL_103:
          while ( v17 > -1437864422 )
          {
            if ( v17 != -1437864421 )
            {
              if ( v17 == -166592410 )
              {
                v19 = *v37;
                *v37 = *v27;
                v17 = 1227597061;
                *v27 = v19;
                v18 = v45;
              }
              goto LABEL_90;
            }
            v27 = (int *)a1;
            v39 = v5;
            v20 = -1616260743;
            v22 = __OFSUB__(v5, 256);
            v21 = v5 - 256 < 0;
            v17 = -166592410;
LABEL_100:
            if ( v21 ^ v22 )
              v17 = v20;
            if ( v17 > 307379756 )
              goto LABEL_91;
          }
          if ( v17 == -1616260743 )
            break;
          if ( v17 == -2078856237 )
          {
            v4 = a4;
            v6 = -444507636;
            v5 = 1660167534;
            a2 = 0x1000000;
            v25 = a4 < 0;
            v16 = a4 < 0;
            a3 = v35;
            a1 = v36;
LABEL_119:
            if ( v16 )
              v6 = v5;
            if ( v6 > -444507637 )
              goto LABEL_6;
            goto LABEL_69;
          }
        }
        v20 = 307379757;
        v46 = (int)&v40[v39];
        v23 = v40[v39];
        v22 = __OFSUB__(v23, *v27);
        v21 = v23 - *v27 < 0;
        a2 = (int)v27;
        v17 = 385223167;
        goto LABEL_100;
      }
      switch ( v6 )
      {
        case -1744686325:
          v5 = v43;
          v6 = 783469431;
          v30 = v52[v43];
          break;
        case -1735363298:
          v5 = v26 - 2;
          v41 = v26 - 2;
          v29 = 0x1000000;
          v6 = (((v26 - 2) >> 31) & 0xBF92E851) + 86368826;
          if ( v6 <= -444507637 )
            goto LABEL_69;
          break;
        case -1538876337:
          v5 = v42;
          v6 = 514714397;
          v28 = v52[v42];
          goto LABEL_6;
        default:
LABEL_5:
          if ( v6 <= -444507637 )
            goto LABEL_69;
          break;
      }
    }
    if ( v6 <= -1382112004 )
      break;
    if ( v6 == -1382112003 )
    {
      a3 = v33;
      v6 = -1534202243;
      goto LABEL_69;
    }
    if ( v6 != -994522485 )
    {
      if ( v6 == -751818089 )
      {
        v16 = v25;
        v6 = -1514475565;
        v5 = -1382112003;
        v33 = 0x1000000;
        goto LABEL_119;
      }
      goto LABEL_5;
    }
    v49 = v29 + 1;
    v5 = v26 - 5;
    v42 = v26 - 5;
    v28 = 0x1000000;
    v6 = (((v26 - 5) >> 31) & 0x7A674ECE) - 1538876337;
    if ( v6 <= -444507637 )
      goto LABEL_69;
  }
  if ( v6 == -1514475565 )
  {
    v6 = -1382112003;
    v33 = v52[v4];
    goto LABEL_69;
  }
  if ( v6 == -1415659758 )
    goto LABEL_4;
  if ( v6 != -1534202243 )
    goto LABEL_5;
  return a3;
}

这种程序用黑盒测试就在好不过了。

注入程序 hook.c

#include <windows.h>
#include <stdio.h>

typedef int (*FUNC)(int);

void hook()
{
    FUNC ptr_func;
    int i;
    // 获得基地址
    uintptr_t image_base = (uintptr_t)GetModuleHandle(NULL);
    ptr_func = (FUNC)(image_base + 0x1000);
    for (i = 0; i < 256; i++)
    {
        printf("[%d, %d],\n", i, ptr_func(i));
    }
}

BOOL WINAPI DllMain(
    _In_ HINSTANCE hinstDLL, // 指向自身的句柄
    _In_ DWORD fdwReason,    // 调用原因
    _In_ LPVOID lpvReserved  // 隐式加载和显式加载
)
{
    puts("hook start");
    hook();
    return TRUE;
}

DllMain

跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是就是DllMain。在载入Dll的时候,会先调用DllMain进行初始化。

之后用msvc的编译器进行编译,命令如下:

cl /LD hook.c

注入

可以到网上去下载注入工具进行dll注入,下面就是注入后的结果。

D:\test>crackme_dll.exe
Input your flag:
hook start
[0, 0],
[1, 1],
[2, -1],
[3, -1],
[4, 2],
[5, -1],
[6, -1],
[7, -1],
[8, -1],
[9, 3],
[10, -1],
[11, -1],
[12, -1],
[13, -1],
[14, -1],
[15, -1],
[16, 4],
[17, -1],
[18, -1],
[19, -1],
[20, -1],
[21, -1],
[22, -1],
[23, -1],
[24, -1],
[25, 5],
[26, -1],
[27, -1],
[28, -1],
[29, -1],
[30, -1],
[31, -1],
[32, 6],
[33, -1],
[34, -1],
[35, -1],
[36, -1],
[37, -1],
[38, -1],
[39, 7],
[40, -1],
[41, -1],
[42, -1],
[43, -1],
[44, -1],
[45, -1],
[46, 8],
[47, -1],
[48, -1],
[49, -1],
[50, -1],
[51, -1],
[52, -1],
[53, 9],
[54, -1],
[55, -1],
[56, -1],
[57, -1],
[58, -1],
[59, -1],
[60, 10],
[61, -1],
[62, -1],
[63, -1],
[64, -1],
[65, -1],
[66, -1],
[67, 11],
[68, -1],
[69, -1],
[70, -1],
[71, -1],
[72, -1],
[73, -1],
[74, 12],
[75, -1],
[76, -1],
[77, -1],
[78, -1],
[79, -1],
[80, -1],
[81, 13],
[82, -1],
[83, -1],
[84, -1],
[85, -1],
[86, -1],
[87, -1],
[88, 14],
[89, -1],
[90, -1],
[91, -1],
[92, -1],
[93, -1],
[94, -1],
[95, 15],
[96, -1],
[97, -1],
[98, -1],
[99, -1],
[100, -1],
[101, -1],
[102, 16],
[103, -1],
[104, -1],
[105, -1],
[106, -1],
[107, -1],
[108, -1],
[109, 17],
[110, -1],
[111, -1],
[112, -1],
[113, -1],
[114, -1],
[115, -1],
[116, 18],
[117, -1],
[118, -1],
[119, -1],
[120, -1],
[121, -1],
[122, -1],
[123, 19],
[124, -1],
[125, -1],
[126, -1],
[127, -1],
[128, -1],
[129, -1],
[130, 20],
[131, -1],
[132, -1],
[133, -1],
[134, -1],
[135, -1],
[136, -1],
[137, 21],
[138, -1],
[139, -1],
[140, -1],
[141, -1],
[142, -1],
[143, -1],
[144, 22],
[145, -1],
[146, -1],
[147, -1],
[148, -1],
[149, -1],
[150, -1],
[151, 23],
[152, -1],
[153, -1],
[154, -1],
[155, -1],
[156, -1],
[157, -1],
[158, 24],
[159, -1],
[160, -1],
[161, -1],
[162, -1],
[163, -1],
[164, -1],
[165, 25],
[166, -1],
[167, -1],
[168, -1],
[169, -1],
[170, -1],
[171, -1],
[172, 26],
[173, -1],
[174, -1],
[175, -1],
[176, -1],
[177, -1],
[178, -1],
[179, 27],
[180, -1],
[181, -1],
[182, -1],
[183, -1],
[184, -1],
[185, -1],
[186, 28],
[187, -1],
[188, -1],
[189, -1],
[190, -1],
[191, -1],
[192, -1],
[193, 29],
[194, -1],
[195, -1],
[196, -1],
[197, -1],
[198, -1],
[199, -1],
[200, 30],
[201, -1],
[202, -1],
[203, -1],
[204, -1],
[205, -1],
[206, -1],
[207, 31],
[208, -1],
[209, -1],
[210, -1],
[211, -1],
[212, -1],
[213, -1],
[214, 32],
[215, -1],
[216, -1],
[217, -1],
[218, -1],
[219, -1],
[220, -1],
[221, 33],
[222, -1],
[223, -1],
[224, -1],
[225, -1],
[226, -1],
[227, -1],
[228, 34],
[229, -1],
[230, -1],
[231, -1],
[232, -1],
[233, -1],
[234, -1],
[235, 35],
[236, -1],
[237, -1],
[238, -1],
[239, -1],
[240, -1],
[241, -1],
[242, 36],
[243, -1],
[244, -1],
[245, -1],
[246, -1],
[247, -1],
[248, -1],
[249, 37],
[250, -1],
[251, -1],
[252, -1],
[253, -1],
[254, -1],
[255, -1],

然后我们获得了这张表,在用这张表进行逆推,即可得到正确的输入9e588220107b7b271019430020

运行实例

D:\test>crackme_dll.exe
Input your flag:
9e588220107b7b271019430020
Success

Linux 动态库劫持

_init

和Windows的WinMain入口函数一样,Linux的动态库也有一个入口函数,就是_init。在载入so文件的时候,会先调用_init进行初始化。

gcc编译器进行编译,命令如下:

gcc -fPIC -shared hook2.c -c -o hook2.o
ld -shared -ldl hook2.o -o hook2.so

例子 hook2.c

#include <stdio.h>
#include <dlfcn.h>

void _init()
{
    void *image_base = *(void **)dlopen(NULL, 1);
    printf("Image base: %p\n", image_base);
}

下面是运行的实例:

ex@Ex:~/test$gcc -fPIC -shared hook2.c -c -o hook2.o
ex@Ex:~/test$ ld -shared -ldl hook2.o -o hook2.so
ex@Ex:~/test$ cat main.c
#include <stdio.h>

int main()
{
    puts("hello world");
    return 0;
}
ex@Ex:~/test$ gcc main.c -o main
ex@Ex:~/test$ ./main
hello world
ex@Ex:~/test$ LD_PRELOAD=./hook2.so ./main
Image base: 0x55f1063cb000
hello world
ex@Ex:~/test$ 

源码

obfuscator.c

// clang -mllvm -fla -O3
#define MAX 0x1000000
#define ARRAY(array, index) ((index) < 0 ? MAX : array[(index)])

inline static int get_min(int a, int b, int c)
{
    if (a > b)
    {
        if (b > c)
        {
            return c;
        }
        else
        {
            return b;
        }
    }
    else
    {
        if (a > c)
        {
            return c;
        }
        else
        {
            return a;
        }
    }
}

inline static void sort(int *array, int n)
{
    int i, ii, *min, temp;
    for (i = 0; i < n; i++)
    {
        min = &array[i];
        for (ii = i + 1; ii < n; ii++)
        {
            if (array[ii] < *min)
            {
                min = &array[ii];
            }
        }

        temp = array[i];
        array[i] = *min;
        *min = temp;
    }
}

int table(int n)
{
    int array[256], i;
    if (n >= 256)
    {
        return -1;
    }

    array[0] = 0;
    for (i = 1; i < 256; i++)
    {
        array[i] = get_min(ARRAY(array, i - 2) + 1, ARRAY(array, i - 5) + 1, ARRAY(array, i - 7) + 1);
    }

    sort(array, 256);

    if (ARRAY(array, n) < MAX && (ARRAY(array, n - 1) != ARRAY(array, n)))
    {
        return ARRAY(array, n);
    }
    else
    {
        return -1;
    }
}

这是一个动态规划算法的改动版本,如果不知道具体用途的话是很难猜测其功能的。

main.c

#include <stdio.h>
#include <string.h>

extern int table(int n);

int str_to_hex(char *str)
{
    char high = 0, low = 0, i;

    if (strlen(str) % 2 != 0)
    {
        return 0;
    }

    for (i = 0; str[i]; i += 2)
    {
        if (str[i] >= '0' && str[i] <= '9')
        {
            high = str[i] - '0';
        }
        else if (str[i] >= 'a' && str[i] <= 'f')
        {
            high = str[i] - 'a' + 10;
        }
        else
        {
            return 0;
        }

        if (str[i + 1] >= '0' && str[i + 1] <= '9')
        {
            low = str[i + 1] - '0';
        }
        else if (str[i + 1] >= 'a' && str[i + 1] <= 'f')
        {
            low = str[i + 1] - 'a' + 10;
        }
        else
        {
            return 0;
        }

        str[i / 2] = ((high & 0x0f) << 4 | (low & 0x0f));
    }

    str[i / 2] = '\0';
    return 1;
}

int main()
{
    unsigned char str[32];
    char key[] = "yougettheflag";
    int i;

    puts("Input your flag:");
    scanf("%30s", str);
    if (str_to_hex(str) == 1)
    {
        for (i = 0; i < strlen(key); i++)
        {
            if (table(str[i]) != key[i] - 'a')
            {
                puts("Failed");
                return 0;
            }
        }
        puts("Success");
    }
    else
    {
        puts("Failed");
    }

    return 0;
}

总结

方法虽然简单,却很实用。