Skip to content

Commit e416a4d

Browse files
committed
fix
1 parent bc209dc commit e416a4d

11 files changed

Lines changed: 741 additions & 2 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build/*
2+
.vscode
3+
build

CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required (VERSION 3.8)
2+
3+
if (POLICY CMP0141)
4+
cmake_policy(SET CMP0141 NEW)
5+
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
6+
endif()
7+
8+
project ("NapCat")
9+
set(CMAKE_CXX_STANDARD 26)
10+
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
11+
set(BIT7Z_AUTO_FORMAT true)
12+
13+
if(MSVC)
14+
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
15+
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
16+
endif()
17+
add_subdirectory ("hook")
18+
add_subdirectory("main")
19+
# 移动NUCAT资源文件进主程序 Debug目录

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
1-
# NTQQAntiRecall
2-
NTQQ反撤回
1+
# 基于NTQQ实现的防撤回
2+
3+
原理可见 [ntrecall](https://napneko.github.io/other/ntrecall)
4+
5+
## 适配对象
6+
Windows QQ 9.9.19 34740 已测试
7+
8+
## 使用介绍
9+
10+
下载压缩包将两个文件丢进QQ.exe所在目录 为NapCatBootMain.exe 建立桌面快捷即可

hook/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
cmake_minimum_required(VERSION 3.12)
2+
3+
if (POLICY CMP0141)
4+
cmake_policy(SET CMP0141 NEW)
5+
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
6+
endif()
7+
8+
project(NapCatWinBootHook)
9+
set(CMAKE_CXX_STANDARD 26)
10+
11+
if(MSVC)
12+
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
13+
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
14+
endif()
15+
file(GLOB SOURCES "*.cpp")
16+
add_library(NapCatWinBootHook SHARED ${SOURCES})

hook/ExecutableAnalyse.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include "ExecutableAnalyse.h"
2+
3+
#if defined(_LINUX_PLATFORM_)
4+
uint64_t SearchRangeAddressInModule(std::shared_ptr<hak::proc_maps> module, const std::vector<uint8_t> &pattern, uint64_t searchStartRVA, uint64_t searchEndRVA)
5+
{
6+
uint8_t *base = reinterpret_cast<uint8_t *>(module->start());
7+
uint8_t *searchStart = base + searchStartRVA;
8+
uint8_t *searchEnd;
9+
if (searchEndRVA == 0)
10+
searchEnd = reinterpret_cast<uint8_t *>(module->end());
11+
else
12+
searchEnd = base + searchEndRVA;
13+
14+
// 确保搜索范围有效
15+
if (searchEnd > reinterpret_cast<uint8_t *>(module->end())){
16+
printf("out of moudle end");
17+
searchEnd = reinterpret_cast<uint8_t *>(module->end());
18+
}
19+
for (uint8_t *current = searchStart; current < searchEnd; ++current)
20+
if (std::equal(pattern.begin(), pattern.end(), current))
21+
return reinterpret_cast<int64_t>(current);
22+
23+
return 0;
24+
}
25+
#elif defined(_WIN_PLATFORM_)
26+
uint64_t SearchRangeAddressInModule(HMODULE module, const std::vector<uint8_t> &pattern, uint64_t searchStartRVA, uint64_t searchEndRVA)
27+
{
28+
HANDLE processHandle = GetCurrentProcess();
29+
MODULEINFO modInfo;
30+
if (!GetModuleInformation(processHandle, module, &modInfo, sizeof(MODULEINFO)))
31+
{
32+
return 0;
33+
}
34+
// 在模块内存范围内搜索模式
35+
uint8_t *base = static_cast<uint8_t *>(modInfo.lpBaseOfDll);
36+
uint8_t *searchStart = base + searchStartRVA;
37+
if (searchEndRVA == 0)
38+
{
39+
// 如果留空表示搜索到结束
40+
searchEndRVA = modInfo.SizeOfImage;
41+
}
42+
uint8_t *searchEnd = base + searchEndRVA;
43+
44+
// 确保搜索范围有效
45+
if (searchStart >= base && searchEnd <= base + modInfo.SizeOfImage)
46+
for (uint8_t *current = searchStart; current < searchEnd; ++current)
47+
if (std::equal(pattern.begin(), pattern.end(), current))
48+
return reinterpret_cast<uint64_t>(current);
49+
50+
return 0;
51+
}
52+
#endif

hook/ExecutableAnalyse.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <iostream>
2+
#include <fstream>
3+
#include <vector>
4+
#include <string>
5+
6+
// #define _LINUX_PLATFORM_
7+
#define _WIN_PLATFORM_
8+
9+
#define _X64_ARCH_
10+
11+
#if defined(_WIN_PLATFORM_)
12+
#include <Windows.h>
13+
#include <psapi.h>
14+
#elif defined(_LINUX_PLATFORM_)
15+
#include <proc_maps.h>
16+
#endif
17+
18+
// std::vector<char> ReadFileToMemory(const std::string &filePath);
19+
// size_t SearchHexPattern(const std::vector<char> &data, const std::string &hexPattern);
20+
#if defined(_LINUX_PLATFORM_)
21+
uint64_t SearchRangeAddressInModule(std::shared_ptr<hak::proc_maps> module, const std::vector<uint8_t> &pattern, uint64_t searchStartRVA = 0, uint64_t searchEndRVA = 0);
22+
#elif defined(_WIN_PLATFORM_)
23+
uint64_t SearchRangeAddressInModule(HMODULE module, const std::vector<uint8_t> &pattern, uint64_t searchStartRVA = 0, uint64_t searchEndRVA = 0);
24+
#endif

hook/HookHelper.h

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#include <string>
2+
// 跨平台兼容个灯
3+
#include <iostream>
4+
#define _WIN_PLATFORM_
5+
6+
#if defined(_WIN_PLATFORM_)
7+
#include <Windows.h>
8+
#elif defined(_LINUX_PLATFORM_)
9+
#include <cstring>
10+
#include <unistd.h>
11+
#include <sys/mman.h>
12+
#endif
13+
14+
void *GetCallAddress(uint8_t *ptr)
15+
{
16+
// 读取操作码
17+
if (ptr[0] != 0xE8)
18+
{
19+
printf("Not a call instruction!\n");
20+
return 0;
21+
}
22+
23+
// 读取相对偏移量
24+
int32_t relativeOffset = *reinterpret_cast<int32_t *>(ptr + 1);
25+
26+
// 计算函数地址
27+
uint8_t *callAddress = ptr + 5; // call 指令占 5 个字节
28+
void *functionAddress = callAddress + relativeOffset;
29+
30+
return reinterpret_cast<void *>(functionAddress);
31+
}
32+
33+
void *SearchAndFillJump(uint64_t baseAddress, void *targetAddress)
34+
{
35+
uint8_t jumpInstruction[] = {
36+
0x49, 0xBB,
37+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38+
0x41, 0xFF, 0xE3};
39+
40+
memcpy(jumpInstruction + 2, &targetAddress, 8);
41+
42+
// Iterate through memory regions
43+
uint64_t searchStart = baseAddress - 0x7fffffff;
44+
uint64_t searchEnd = baseAddress + 0x7fffffff;
45+
46+
#if defined(_WIN_PLATFORM_)
47+
while (searchStart < searchEnd - sizeof(jumpInstruction))
48+
{
49+
MEMORY_BASIC_INFORMATION mbi;
50+
if (VirtualQuery(reinterpret_cast<void *>(searchStart), &mbi, sizeof(mbi)) == 0)
51+
break;
52+
if (mbi.State == MEM_COMMIT)
53+
{
54+
for (char *addr = static_cast<char *>(mbi.BaseAddress); addr < static_cast<char *>(mbi.BaseAddress) + mbi.RegionSize - 1024 * 5; ++addr)
55+
{
56+
57+
bool isFree = true;
58+
for (int i = 0; i < 1024 * 5; ++i)
59+
{
60+
if (addr[i] != 0)
61+
{
62+
isFree = false;
63+
break;
64+
}
65+
}
66+
if (isFree)
67+
{
68+
DWORD oldProtect;
69+
addr += 0x200;
70+
// printf("addr: %p\n", addr);
71+
72+
if (!VirtualProtect(addr, sizeof(jumpInstruction), PAGE_EXECUTE_READWRITE, &oldProtect))
73+
break;
74+
memcpy(addr, jumpInstruction, sizeof(jumpInstruction));
75+
if (!VirtualProtect(addr, sizeof(jumpInstruction), PAGE_EXECUTE_READ, &oldProtect))
76+
break;
77+
78+
return addr;
79+
}
80+
}
81+
}
82+
searchStart += mbi.RegionSize;
83+
}
84+
#elif defined(_LINUX_PLATFORM_)
85+
// 保证地址对齐
86+
searchStart &= 0xfffffffffffff000;
87+
searchStart += 0x1000;
88+
searchEnd &= 0xfffffffffffff000;
89+
90+
auto pmap = hak::get_maps();
91+
do
92+
{
93+
auto fpmap = pmap;
94+
pmap = fpmap->next();
95+
if (std::min(pmap->start(), searchEnd) - std::max(fpmap->end(), searchStart) > 0x2000) // 搜索一片 0x2000 大小的空区域
96+
{
97+
void *addr = mmap(reinterpret_cast<void *>(std::max(fpmap->end(), searchStart)), 0x2000, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
98+
// printf("addr: %p\n", addr);
99+
if (addr == MAP_FAILED)
100+
{
101+
printf("mmap failed\n");
102+
continue;
103+
}
104+
if (reinterpret_cast<uint64_t>(addr) > searchEnd - sizeof(jumpInstruction))
105+
{
106+
munmap(addr, 0x2000);
107+
printf("addr > searchEnd\n");
108+
continue;
109+
}
110+
memcpy(addr, jumpInstruction, sizeof(jumpInstruction));
111+
if (mprotect(addr, 0x2000, PROT_READ | PROT_EXEC) == -1) // 设置内存 r-w
112+
{
113+
munmap(addr, 0x2000);
114+
printf("mprotect failed\n");
115+
continue;
116+
}
117+
return addr;
118+
}
119+
} while (pmap->next() != nullptr);
120+
121+
#endif
122+
return nullptr;
123+
}
124+
125+
bool Hook(uint8_t *callAddr, void *lpFunction)
126+
{
127+
uint64_t startAddr = reinterpret_cast<uint64_t>(callAddr) + 5;
128+
int64_t distance = reinterpret_cast<uint64_t>(lpFunction) - startAddr;
129+
#if defined(_WIN_PLATFORM_)
130+
// printf("Hooking %p to %p, distance: %lld\n", callAddr, lpFunction, distance);
131+
132+
DWORD oldProtect;
133+
DWORD reProtect;
134+
if (!VirtualProtect(callAddr, 10, PAGE_EXECUTE_READWRITE, &oldProtect))
135+
{
136+
printf("VirtualProtect failed\n");
137+
return false;
138+
}
139+
if (distance < INT32_MIN || distance > INT32_MAX)
140+
{
141+
void *new_ret = SearchAndFillJump(startAddr, lpFunction);
142+
if (new_ret == nullptr)
143+
{
144+
printf("Can't find a place to jump\n");
145+
return false;
146+
}
147+
distance = reinterpret_cast<uint64_t>(new_ret) - startAddr;
148+
// printf("new_ret: %p, new_distance: %lld\n", new_ret, distance);
149+
}
150+
// 直接进行小跳转
151+
memcpy(callAddr + 1, reinterpret_cast<int32_t *>(&distance), 4); // 修改 call 地址
152+
if (!VirtualProtect(callAddr, 10, oldProtect, &reProtect)) // 恢复原来的内存保护属性
153+
{
154+
std::cout << GetLastError()<<"/"<<callAddr<<"/"<<oldProtect<<"/"<<reProtect;
155+
printf("VirtualProtect failed\n");
156+
return false;
157+
}
158+
return true;
159+
#elif defined(_LINUX_PLATFORM_)
160+
// printf("Hooking %p to %p, distance: %ld\n", callAddr, lpFunction, distance);
161+
162+
auto get_page_addr = [](void *addr) -> void *
163+
{
164+
return (void *)((uintptr_t)addr & ~(getpagesize() - 1));
165+
};
166+
167+
if (mprotect(get_page_addr(callAddr), 2 * getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC) == -1) // 设置内存可写 两倍 pagesize 防止处于页边界
168+
{
169+
printf("mprotect failed\n");
170+
return false;
171+
}
172+
if (distance < INT32_MIN || distance > INT32_MAX)
173+
{
174+
void *new_ret = SearchAndFillJump(startAddr, lpFunction);
175+
if (new_ret == nullptr)
176+
{
177+
printf("Can't find a place to jump\n");
178+
return false;
179+
}
180+
distance = reinterpret_cast<uint64_t>(new_ret) - startAddr;
181+
// printf("new_ret: %p, new_distance: %ld\n", new_ret, distance);
182+
}
183+
184+
memcpy(callAddr + 1, reinterpret_cast<int32_t *>(&distance), 4); // 修改 call 地址
185+
if (mprotect(get_page_addr(callAddr), 2 * getpagesize(), PROT_READ | PROT_EXEC) == -1) // 还原内存保护属性
186+
{
187+
printf("mprotect failed\n");
188+
return false;
189+
}
190+
return true;
191+
#endif
192+
}

hook/NapCat.exe.manifest

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
3+
<assemblyIdentity type="win32" name="cn.nanaeo.nucat" version="6.0.0.0"/>
4+
<application>
5+
<windowsSettings>
6+
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
7+
</windowsSettings>
8+
</application>
9+
</assembly>

0 commit comments

Comments
 (0)