鱼C论坛

 找回密码
 立即注册
查看: 5680|回复: 13

[已解决]实现CE搜索字符串功能

[复制链接]
发表于 2019-2-14 15:59:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
在学习C语言编程中,遇到一个问题,就是模拟CE的搜索功能,搜索出字符串,问题见贴图,但是对于这个题没半点办法,写了半天,错误一堆,所以向大家求教,麻烦了,如果有解决,愿意以100鱼币酬谢(ps:实在编写不出,可以的话,求教代码)
最佳答案
2019-2-14 20:16:54
本帖最后由 人造人 于 2019-2-14 20:34 编辑

先贴出代码

ProcessOperation.hpp
#ifndef _PROCESS_OPERATION_HPP_
#define _PROCESS_OPERATION_HPP_

#include <string>
#include <windows.h>

HANDLE GetProcessHandle(std::string ProcessName);
void CloseProcessHandle(HANDLE hProcess);
UINT32 ReadAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress);
void WriteAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress, UINT32 data);


#endif

ProcessOperation.cpp
#include "ProcessOperation.hpp"
#include <tlhelp32.h>

static DWORD GetProcessIdByName(std::string ProcessName)
{
        DWORD dwProcessId = 0;
        PROCESSENTRY32 pe32;
        HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        pe32.dwSize = sizeof(PROCESSENTRY32);
        Process32First(hProcessSnap, &pe32);

        do
        {
                if(ProcessName == pe32.szExeFile)
                {
                        dwProcessId = pe32.th32ProcessID;
                        break;
                }
        }
        while(Process32Next(hProcessSnap, &pe32));

        CloseHandle(hProcessSnap);
        return dwProcessId;
}

HANDLE GetProcessHandle(std::string ProcessName)
{
        DWORD dwProcessId = GetProcessIdByName(ProcessName);
        if(dwProcessId == 0)
                return nullptr;
        return OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, 0, dwProcessId);
}

void CloseProcessHandle(HANDLE hProcess)
{
        CloseHandle(hProcess);
}

UINT32 ReadAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress)
{
        UINT32 data;
        SIZE_T NumberOfBytesRead;

        ReadProcessMemory(hProcess, (LPCVOID)BaseAddress, &data, 4, &NumberOfBytesRead);
        return data;
}

void WriteAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress, UINT32 data)
{
        SIZE_T NumberOfBytesWritten;

        WriteProcessMemory(hProcess, (LPVOID)BaseAddress, &data, 4, &NumberOfBytesWritten);
}

main.cpp
#include "ProcessOperation.hpp"
#include <iostream>
#include <string>

char *SearchString(HANDLE hProcess, DWORD begin_address, DWORD end_address, std::string string)
{
        auto iter = string.begin();
        char *result = (char *)begin_address;
        for(UINT32 i = 0; begin_address + i < end_address; ++i)
        {
                char buf[4];
                *(UINT32 *)&buf[0] = ReadAddress_4Byte(hProcess, begin_address + i);
                if(iter == string.end())
                        break;
                if(*iter == buf[0])
                {
                        ++iter;
                }
                else
                {
                        iter = string.begin();
                        result = (char *)(begin_address + i + 1);
                }
        }
        if(iter == string.end())
                return result;
        return nullptr;
}

int main()
{
        HANDLE hProcess = GetProcessHandle("C.exe");
        std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
        CloseProcessHandle(hProcess);
        return 0;
}
00BB7CF8
请按任意键继续. . .


C.exe
#include <stdio.h>

int main(void)
{
        getchar();
        printf("%s\n", "hello world!");
        return 0;
}


程序打开C.exe这个进程,在这个进程中搜索字符串“hello world!”
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;

我们没有必要搜索0x00000000~0xFFFFFFFF,这全部的4GB内存,可以通过分析PE结构得到当前字符串可能在哪些段
例如当前程序(C.exe)中的“hello world!”,这个字符串在.rdata段
1.png
2.png

可以看到“hello world!”字符串在C.exe文件中的偏移是0x64f8
0x64f8位于.rdata段
.rdata段从文件中偏移为0x5800的位置开始
0x64f8 - 0x5800 = 0xcf8
0xcf8就是“hello world!”字符串在C.exe文件中,在.rdata段的偏移
这是在文件中的偏移

运行后.rdata段从0x17000开始,这也是一个偏移,是从ImageBase开始的偏移
从图中可以看到,ImageBase是0x400000
0x400000 + 0x17000 = 0x417000
0x417000是运行后.rdata段的虚拟地址,在文件中“hello world!”字符串在.rdata段的偏移是0xcf8,运行后在.rdata段的偏移也是0xcf8
0x417000 + 0xcf8 = 0x417cf8
0x417cf8这是“hello world”字符串在内存中的地址,在C.exe进程中的虚拟地址

但是看SearchString.exe的输出
00BB7CF8
请按任意键继续. . .

为什么是00BB7CF8,还有
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
0xbb7000和0xbba000是怎么回事?

前面说ImageBase是0x400000,但是这是一个建议的地址,操作系统可以不使用这个地址
在我的系统上就没有使用这个地址

3.png

用CE找到的“hello world!”字符串在地址00BB7CF8
用这个地址反向计算出实际加载的ImageBase

bb7cf8 - cf8 = bb7000
bb7000 - 17000 = ba0000

ba0000就是实际使用的ImageBase

那么
ba0000 + 17000 + cf8 = bb7cf8
0xbb7cf8就是“hello world”字符串在内存中的地址,在C.exe进程中的虚拟地址

SearchString.exe输出的也是这个地址

0xbb7000是在内存中.rdata段的开始
0xbba000是在内存中.rdata段的结束
我们只搜索.rdata段


有些编译器会把只读的数据放在代码段(.text段),这时我们就需要搜索.text段
还有一些其他数据可能在其他段,我们可以分析PE结构来搜索一切可能的段

有一点我目前还不明白,通过malloc申请的内存在哪个段?
我认为可以通过分析某些数据结构得到这个段的开始地址和结束地址
这依然没有必要搜索全部的4GB内存

还有最后一个问题
之前我们的ImageBase是通过CE找到的,我们如何在我们的程序中直接添加这个功能?直接在我们的程序中计算.rdata段的开始地址和结束地址?
这个问题交给你了,如果你有兴趣的话
我目前还没有好的方法
^_^
1.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-2-14 16:32:48 | 显示全部楼层
有两个问题
1.这些数据是什么类型?
在内存中的二进制数字?
字符串类型?
应该是第一种吧

2.你要查找名字字符串,那么这个名字字符串有什么特征?
例如名字的第一个字符是‘@’,并且以‘\0’结尾
或者名字以0xaa55开始,以数字 -1 结尾
也就是说这个字符串必须要有某些特征才能查找

例如,这段内存中存储了角色的名字和角色的自我描述,都是字符串类型,那么肯定有某种机制来标识哪一个是名字,哪一个是自我描述
例如在内存地址0x10的位置开始的4个字节存储名字字符串的偏移地址,在内存地址0x14的位置开始的4个字节存储自我描述字符串的偏移地址

另一种情况
试想一下,一个程序在数据段保存了一个名字字符串,在代码段的某个位置进行如下引用
push    0x1380
push    0x4540
call      SetName

第一条指令把那个在数据段中的字符串的地址压栈,第二条指令压入新字符串的地址,第三条指令调用一个函数,用来把数据段中的那个名字字符串修改成第2个参数指向的字符串

也就是说,你必须找到那个名字字符串在原来的那个程序中是如何引用的,这样你才能找到这个字符串

CE其实并不知道哪一个是名字字符串,CE会把所有字符串都列出来,然后一步一步过滤,例如你在哪个程序中看到名字是“abcd”,那么你在CE中搜索“abcd”,等哪个名字字符串变成“1234”时,你又继续进行搜索“1234”,这样CE的列表框中就出现了这个字符串的地址和这个字符串

如果你是通过CE找到的这个字符串,那么你可以用CE找到这个字符串的指针,然后直接在你的程序中打开那个包含名字字符串的程序,用这个指针进行访问就可以了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-2-14 17:29:28 | 显示全部楼层
本帖最后由 A小小鸟 于 2019-2-14 17:49 编辑
人造人 发表于 2019-2-14 16:32
有两个问题
1.这些数据是什么类型?
在内存中的二进制数字?


这些数据是内存中的显示,从而而查找“”WOW“”字符串,以"\0"作为结尾的,我自己写出strcmp的函数实现,但是不知道如何应用到这个函数中去,其实说白了,就是C语言实现在内存中遍历字符串然后找出相对应字符串的功能,但是这个实现,我不知道用指针怎么写,这个只是模拟CE在内存查找字符串而已,就相当于自己用C语言写一个简单的函数用来在内存中在一堆字符串数据中找出"WOW"的字符串一样,求大佬指点一下吧啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-2-14 18:39:23 | 显示全部楼层
跟查找字符串原理是一样的。只要知道要查找的内存地址,传入,然后对比即可。

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

char *FindRoleNameAddr(char *pData, char *pRoleName);

char *FindRoleNameAddr(char *pData, char *pRoleName)
{
        char temp[sizeof(pRoleName)]={'\0'}; // 通过sizeof获取传入字符串的大小,定义个临时数组
        char *cpData;
        int i;

        cpData = pData; // 先用cpData指向pData的首地址
        while(1)
        {

                for(i=0; i<(sizeof(pRoleName)-1); i++) // 获取字符串保存到temp数组中
                {
                        temp[i] = *pData;
                        pData++; 
                }        


                if ( strcmp(temp, pRoleName)==0 ) // 对比获取到的字符串是否和目标字符串相等
                {
                                return cpData; // 相等返回字符串所在地址
                }

                pData = ++cpData; // 把pData指向下一个字符的地址。
        }

}

int main()
{
        char *myStr = "abcdawewowfasdfsdf";
        char *tStr = "wow";
        char *pStr;

        pStr = FindRoleNameAddr(myStr,tStr); 

        printf("找到:%s, 地址是:%p\n", pStr, pStr);

        return 0;
}

评分

参与人数 1鱼币 +5 收起 理由
A小小鸟 + 5

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-2-14 20:16:54 | 显示全部楼层    本楼为最佳答案   
本帖最后由 人造人 于 2019-2-14 20:34 编辑

先贴出代码

ProcessOperation.hpp
#ifndef _PROCESS_OPERATION_HPP_
#define _PROCESS_OPERATION_HPP_

#include <string>
#include <windows.h>

HANDLE GetProcessHandle(std::string ProcessName);
void CloseProcessHandle(HANDLE hProcess);
UINT32 ReadAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress);
void WriteAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress, UINT32 data);


#endif

ProcessOperation.cpp
#include "ProcessOperation.hpp"
#include <tlhelp32.h>

static DWORD GetProcessIdByName(std::string ProcessName)
{
        DWORD dwProcessId = 0;
        PROCESSENTRY32 pe32;
        HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        pe32.dwSize = sizeof(PROCESSENTRY32);
        Process32First(hProcessSnap, &pe32);

        do
        {
                if(ProcessName == pe32.szExeFile)
                {
                        dwProcessId = pe32.th32ProcessID;
                        break;
                }
        }
        while(Process32Next(hProcessSnap, &pe32));

        CloseHandle(hProcessSnap);
        return dwProcessId;
}

HANDLE GetProcessHandle(std::string ProcessName)
{
        DWORD dwProcessId = GetProcessIdByName(ProcessName);
        if(dwProcessId == 0)
                return nullptr;
        return OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, 0, dwProcessId);
}

void CloseProcessHandle(HANDLE hProcess)
{
        CloseHandle(hProcess);
}

UINT32 ReadAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress)
{
        UINT32 data;
        SIZE_T NumberOfBytesRead;

        ReadProcessMemory(hProcess, (LPCVOID)BaseAddress, &data, 4, &NumberOfBytesRead);
        return data;
}

void WriteAddress_4Byte(HANDLE hProcess, UINT32 BaseAddress, UINT32 data)
{
        SIZE_T NumberOfBytesWritten;

        WriteProcessMemory(hProcess, (LPVOID)BaseAddress, &data, 4, &NumberOfBytesWritten);
}

main.cpp
#include "ProcessOperation.hpp"
#include <iostream>
#include <string>

char *SearchString(HANDLE hProcess, DWORD begin_address, DWORD end_address, std::string string)
{
        auto iter = string.begin();
        char *result = (char *)begin_address;
        for(UINT32 i = 0; begin_address + i < end_address; ++i)
        {
                char buf[4];
                *(UINT32 *)&buf[0] = ReadAddress_4Byte(hProcess, begin_address + i);
                if(iter == string.end())
                        break;
                if(*iter == buf[0])
                {
                        ++iter;
                }
                else
                {
                        iter = string.begin();
                        result = (char *)(begin_address + i + 1);
                }
        }
        if(iter == string.end())
                return result;
        return nullptr;
}

int main()
{
        HANDLE hProcess = GetProcessHandle("C.exe");
        std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
        CloseProcessHandle(hProcess);
        return 0;
}
00BB7CF8
请按任意键继续. . .


C.exe
#include <stdio.h>

int main(void)
{
        getchar();
        printf("%s\n", "hello world!");
        return 0;
}


程序打开C.exe这个进程,在这个进程中搜索字符串“hello world!”
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;

我们没有必要搜索0x00000000~0xFFFFFFFF,这全部的4GB内存,可以通过分析PE结构得到当前字符串可能在哪些段
例如当前程序(C.exe)中的“hello world!”,这个字符串在.rdata段
1.png
2.png

可以看到“hello world!”字符串在C.exe文件中的偏移是0x64f8
0x64f8位于.rdata段
.rdata段从文件中偏移为0x5800的位置开始
0x64f8 - 0x5800 = 0xcf8
0xcf8就是“hello world!”字符串在C.exe文件中,在.rdata段的偏移
这是在文件中的偏移

运行后.rdata段从0x17000开始,这也是一个偏移,是从ImageBase开始的偏移
从图中可以看到,ImageBase是0x400000
0x400000 + 0x17000 = 0x417000
0x417000是运行后.rdata段的虚拟地址,在文件中“hello world!”字符串在.rdata段的偏移是0xcf8,运行后在.rdata段的偏移也是0xcf8
0x417000 + 0xcf8 = 0x417cf8
0x417cf8这是“hello world”字符串在内存中的地址,在C.exe进程中的虚拟地址

但是看SearchString.exe的输出
00BB7CF8
请按任意键继续. . .

为什么是00BB7CF8,还有
std::cout << (int *)SearchString(hProcess, 0xbb7000, 0xbba000, "hello world!") << std::endl;
0xbb7000和0xbba000是怎么回事?

前面说ImageBase是0x400000,但是这是一个建议的地址,操作系统可以不使用这个地址
在我的系统上就没有使用这个地址

3.png

用CE找到的“hello world!”字符串在地址00BB7CF8
用这个地址反向计算出实际加载的ImageBase

bb7cf8 - cf8 = bb7000
bb7000 - 17000 = ba0000

ba0000就是实际使用的ImageBase

那么
ba0000 + 17000 + cf8 = bb7cf8
0xbb7cf8就是“hello world”字符串在内存中的地址,在C.exe进程中的虚拟地址

SearchString.exe输出的也是这个地址

0xbb7000是在内存中.rdata段的开始
0xbba000是在内存中.rdata段的结束
我们只搜索.rdata段


有些编译器会把只读的数据放在代码段(.text段),这时我们就需要搜索.text段
还有一些其他数据可能在其他段,我们可以分析PE结构来搜索一切可能的段

有一点我目前还不明白,通过malloc申请的内存在哪个段?
我认为可以通过分析某些数据结构得到这个段的开始地址和结束地址
这依然没有必要搜索全部的4GB内存

还有最后一个问题
之前我们的ImageBase是通过CE找到的,我们如何在我们的程序中直接添加这个功能?直接在我们的程序中计算.rdata段的开始地址和结束地址?
这个问题交给你了,如果你有兴趣的话
我目前还没有好的方法
^_^

评分

参与人数 1鱼币 +5 收起 理由
A小小鸟 + 5

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-2-14 20:20:26 | 显示全部楼层
还有一点忘了说
SearchString函数的搜索效率不高,你也许有必要改进这个函数
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-2-15 17:40:04 | 显示全部楼层
本帖最后由 A小小鸟 于 2019-2-15 17:58 编辑
人造人 发表于 2019-2-14 20:20
还有一点忘了说
SearchString函数的搜索效率不高,你也许有必要改进这个函数


谢谢你了啊,你辛苦了,本来想直接给100鱼币,但是却无法给,只能求助小甲鱼了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-2-15 17:59:26 | 显示全部楼层
ba21 发表于 2019-2-14 18:39
跟查找字符串原理是一样的。只要知道要查找的内存地址,传入,然后对比即可。

如:

非常的感谢你,你辛苦了,也想给你100鱼币表示感谢,但是却给不了,只能求助小甲鱼帮帮忙了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-2-15 18:55:41 | 显示全部楼层
A小小鸟 发表于 2019-2-15 17:59
非常的感谢你,你辛苦了,也想给你100鱼币表示感谢,但是却给不了,只能求助小甲鱼帮帮忙了

没关系,等你求回,确定是这样,再给也不迟;别浪费哥表情。要是给不起也没关系
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-2-15 19:44:32 | 显示全部楼层
本帖最后由 A小小鸟 于 2019-2-15 19:48 编辑
ba21 发表于 2019-2-15 18:55
没关系,等你求回,确定是这样,再给也不迟;别浪费哥表情。要是给不起也没关系


不是的,系统的评分限制鱼币数量,我说到的话绝对做到的,毕竟你认认真真的给我帮忙,我怎么能随意的辜负你的热情,我就已经在投诉建议中发了帖子了,坐等小甲鱼回复的,抱歉了啊,
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-2-15 22:19:36 | 显示全部楼层
A小小鸟 发表于 2019-2-15 19:44
不是的,系统的评分限制鱼币数量,我说到的话绝对做到的,毕竟你认认真真的给我帮忙,我怎么能随意的辜 ...

坐等消息
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-2-19 12:27:04 | 显示全部楼层

对不起,我发了帖子询问了小甲鱼,小甲鱼告诉我,没有这个功能,对不起啊,这是我的错
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-2-19 12:27:37 | 显示全部楼层
人造人 发表于 2019-2-14 20:20
还有一点忘了说
SearchString函数的搜索效率不高,你也许有必要改进这个函数

对不起,我发了帖子询问了小甲鱼,小甲鱼告诉我,没有这个功能,对不起啊,这是我的错
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-2-19 13:36:02 | 显示全部楼层
A小小鸟 发表于 2019-2-19 12:27
对不起,我发了帖子询问了小甲鱼,小甲鱼告诉我,没有这个功能,对不起啊,这是我的错

没事
^_^
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-3 08:24

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表