linux 库函数劫持技术,黑盒测试-动态库劫持-so注入-Dll注入

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

方法

编写注入dll、so

进行注入测试

举例

用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@(int a1@, int a2@, signed int a3@, 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

#include

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

#include

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

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

#include

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;

}

总结

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

0

0

vote

Article Rating

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值