🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
问题描述
Windows X86 远线程注入问题解惑
¥15
c++
windows
在之前提问的知道下 成功部署了 汇编器 ; 一会即将采纳。
新问题:(请通读全文 重点在于解惑)
下面这个是 X32dbg中 看到的一个功能call 作用是选中一个物体。
0057ADEB | 8B0D 90FB3801 | mov ecx, dword ptr [138FB90] |
0057ADF1 | 56 | push esi |
0057ADF2 | E8 B9C14A00 | call A26FB0 |
0057ADF7 | 8BC8 | mov ecx, eax |
0057ADF9 | E8 D223FCFF | call 53D1D0 | 选中
这个是 我用来 翻译汇编为机器码的相关代码:
该汇编器 默认 地址为绝对地址(直接翻译)。
int QQSG::AsmToHex(ks_arch arch, int mode, const char* assembly, int syntax, QString& outStr, size_t* OutSize) {
int status = 0;
ks_engine* ks;
ks_err err;
size_t count;
unsigned char* encode = nullptr;
size_t size = 0;
// Initialize Keystone engine
err = ks_open(arch, mode, &ks);
if (err != KS_ERR_OK) {
printf("Failed to initialize Keystone engine: %d\n", ks_errno(ks));
return -1;
}
// Set syntax option
if (syntax)
ks_option(ks, KS_OPT_SYNTAX, syntax);
// Assemble instruction
if (ks_asm(ks, assembly, 0, &encode, &size, &count)) {
printf("ERROR: failed on ks_asm() with count = %zu, error code = %d\n", count, ks_errno(ks));
ks_free(encode); // Free encoded data
ks_close(ks); // Close Keystone engine
return 2;
}
// Check if the encoding was successful
if (size > 0) {
// Convert machine code to hexadecimal string
for (size_t i = 0; i < size; i++) {
char tmp[5];
sprintf(tmp, "%02X", encode[i]);
outStr += tmp;
}
outStr += "\r\n";
// Print the machine code for debugging
printf("Generated machine code: ");
for (size_t i = 0; i < size; i++) {
printf("%02X ", encode[i]);
}
printf("\n");
// 调整 asmhex 的大小
asmhex.resize(asmhex.size() + size); // 调整 asmhex 的大小
memcpy_s(asmhex.data() + asmhex.size() - size, size, encode, size); // 复制数据到 asmhex
*OutSize = size;
}
else {
printf("No machine code generated for '%s'\n", assembly);
}
// Clean up resources
ks_free(encode);
ks_close(ks);
return status;
}
QByteArray QQSG::assembleCode(const QByteArray& asmcode) {
int all_size = 0;
bool hasErrors = false;
QString text = QString::fromUtf8(asmcode);
printf("Original assembly code: %s\n", text.toLocal8Bit().constData());
QStringList lines = text.split('\n');
for (QString& asmstr : lines) {
asmstr = asmstr.trimmed();
printf("Processing assembly code: %s\n", asmstr.toLocal8Bit().constData());
if (asmstr.isEmpty())
continue;
size_t hex_size = 0;
// 调用 AsmToHex 并获取结果
int status = this->AsmToHex(KS_ARCH_X86, KS_MODE_32, asmstr.toLocal8Bit().data(), 0, m_hexstr, &hex_size);
printf("Assembly conversion result: %s\n", asmstr.toLocal8Bit().constData());
if (status != 0) {
printf("Assembly conversion failed: %s\n", asmstr.toLocal8Bit().constData());
m_hexstr.append(" <= instruction format error!");
hasErrors = true;
}
else {
all_size += hex_size;
}
}
// If errors occurred, return an empty QByteArray
if (hasErrors) {
printf("Errors occurred, returning an empty QByteArray\n");
return QByteArray();
}
// Add a ret instruction at the end
/* asmhex.push_back(0xC3);*/
all_size++;
// 转换为 QByteArray 并返回
QByteArray machineCode = QByteArray::fromRawData(reinterpret_cast<const char*>(asmhex.data()), asmhex.size());
// 打印最终的机器码用于调试
printf("Final machine code: %s\n", machineCode.toHex().constData());
asmhex.clear();
return machineCode;
}
void QQSG::on_pushButtonCodeInject_clicked()
{
if (ui.lineEditCodeHwnd->text().isEmpty()) {
QMessageBox::warning(this, tr("Warning"), tr("请输入目标窗口句柄!"));
return;
}
// 尝试将文本转换为DWORD类型
QString asmcode = ui.textEditShellCode->toPlainText();
if (asmcode.isEmpty()) {
QMessageBox::warning(this, tr("Warning"), tr("请输入汇编代码!"));
return;
}
QString CodeHwndStr = "CodeHwnd:" + ui.lineEditCodeHwnd->text();
for (int i = 0; i < m_clinetList.size(); ++i)
{
QTcpSocket* socket = m_clinetList.at(i);
if (socket->state() == QTcpSocket::ConnectedState) {
socket->write(CodeHwndStr.toUtf8());
}
}
}
void QQSG::recevieReadyRead()
{
for (int i = 0; i < m_clinetList.size(); i++)
{
QTcpSocket* socket = m_clinetList.at(i);
QByteArray data = socket->readAll();
if (data.contains("StartShellcodeRemoteThread"))
{
QString asmcode = ui.textEditShellCode->toPlainText();
QByteArray asmcodeBytes = asmcode.toUtf8();
QByteArray machineCode = QQSG::assembleCode(asmcodeBytes);
DWORD processId = 0;
QString text = ui.tableWidget->item(0, UITYPE::WINWND)->text();
bool ok;
int m_hWnd = text.toInt(&ok);
GetWindowThreadProcessId((HWND)m_hWnd, &processId);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (hProcess == NULL) {
qDebug() << "Failed to open process:" << GetLastError();
return;
}
PVOID pLibRemote = VirtualAllocEx(hProcess, NULL, machineCode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pLibRemote == NULL) {
qDebug() << "Failed to allocate memory in remote process:" << GetLastError();
CloseHandle(hProcess);
return;
}
SIZE_T bytesWritten;
BOOL result = WriteProcessMemory(hProcess, pLibRemote, machineCode.data(), machineCode.size(), &bytesWritten);
if (!result || bytesWritten != machineCode.size()) {
qDebug() << "Failed to write memory to remote process:" << GetLastError();
VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);
CloseHandle(hProcess);
return;
}
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLibRemote, NULL, 0, NULL);
if (hThread == NULL) {
qDebug() << "Failed to create remote thread:" << GetLastError();
VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);
CloseHandle(hProcess);
return;
}
QMessageBox::warning(this, tr("Warning"), tr("请输入汇编代码!"));
DWORD result = WaitForSingleObject(hThread, INFINITE);
if (result != WAIT_OBJECT_0) {
qDebug() << "Failed to wait for remote thread:" << GetLastError();
VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return;
}
VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return;
}
}
}
问题来了 在学习过程中,假如我想调用这个call 实现远线程,我写了 汇编语言:
//esi的值 是选中对象的相关参数 我们假设他是 0x12345678,
mov esi,0x12345678
mov ecx,0x138FB90
mov ecx,[ecx]
push esi
call 0xA26FB0
mov ecx, eax
call 0x53D1D0
注入之后的结果如下:
Original assembly code: mov esi,0x12345678
mov ecx,0x138FB90
mov ecx,[ecx]
push esi
call 0xA26FB0
mov ecx, eax
call 0x53D1D0
Processing assembly code: mov ecx,0x138FB90
Generated machine code: B9 90 FB 38 01
Assembly conversion result: mov ecx,0x138FB90
Processing assembly code: mov ecx,[ecx]
Generated machine code: 8B 09
Assembly conversion result: mov ecx,[ecx]
Processing assembly code: push esi
Generated machine code: 56
Assembly conversion result: push esi
Processing assembly code: call 0xA26FB0
Generated machine code: E8 AB 6F A2 00
Assembly conversion result: call 0xA26FB0
Processing assembly code: mov ecx,eax
Generated machine code: 89 C1
Assembly conversion result: mov ecx,eax
Processing assembly code: call 0x53D1D0
Generated machine code: E8 CB D1 53 00
Assembly conversion result: call 0x53D1D0
Processing assembly code: ret
Generated machine code: C3
Assembly conversion result: ret
Processing assembly code:
Final machine code: b990fb38018b0956e8ab6fa20089c1e8cbd15300c3
Received machineCode code: B9 90 FB 38 01 8B 09 56 E8 AB 6F A2 00 89 C1 E8 CB D1 53 00
Received machineCode: B9 90 FB 38 01 8B 09 56 E8 AB 6F A2 00 89 C1 E8 CB D1 53 00
// 原代码中的机器码(由于语言规范及传入数据,开头的机器码不同我可以理解,call后为什么不相同呢)
8B 0D 90 FB 38 01 56 E8 B9 C1 4A 00 8B C8 E8 D2 23 FC FF
此时:线程注入没有反应;
问题:
1.为什么 call 0xA26FB0 与 call 0x53D1D0翻译后的机器码 与 源代码中(文章开头)中的机器码不同呢
2.解释这种现象的原因,是因为call 后的 是偏移量?
3.为什么 注入后没有反应;
4. 综合上述问题 ,我需要如何修改 才能实现远线程注入
5.为什么我学习别人的文章,代码里也没有相关的处理 却可以调用成功;
6.为什么内联汇编却可以呢 ,比如下面:当然 内联汇编 需要修改一下,不能直接传值
DWORD 功能相关地址 =0x138FB90;
DWORD 选中目标 = 0x12345678;
DWORD call_1 =0xA26FB0;
DWORD call_2 =0xA26FB0;
_asm
{
mov esi,选中目标
mov ecx,功能相关地址
mov ecx,[ecx]
push esi
call call_1
mov ecx, eax
call call_2
}
解决方案
如下是上述问题的解决方案,仅供参考:
在你的问题中,你涉及到了远程线程注入和汇编代码的转换,特别是关于如何将汇编语言代码注入到远程进程中并使其执行。你遇到的主要问题是关于机器码转换的准确性以及在远程线程注入后代码没有执行的原因。让我们逐一解答这些问题。
问题分析与解答
-
为什么
call 0xA26FB0
与call 0x53D1D0
翻译后的机器码与源代码中的机器码不同?汇编中的
call
指令需要使用相对于当前指令的偏移量,而不是绝对地址。因此,翻译成机器码时,call
指令后的地址是一个相对地址。- 在你的代码中,
call 0xA26FB0
和call 0x53D1D0
在实际生成机器码时,需要计算这些地址相对于call
指令本身的偏移量。 - 原始机器码中的
call
指令实际上包含的是偏移量,不是绝对地址。比如,E8 AB 6F A2 00
表示call
指令的偏移量是0x00A26FAB
(相对于call
指令的地址)。
在你的机器码生成中,
call
后的地址可能是基于某个假设的位置计算的偏移量,而不是实际的绝对地址。确保你的汇编器或代码生成工具计算了正确的相对地址偏移量。 - 在你的代码中,
-
解释这种现象的原因,是因为
call
后的地址是偏移量?是的,
call
指令使用的是相对偏移量,而不是绝对地址。call
指令后面跟的是相对于下一条指令的偏移量(即call
指令的地址加上偏移量等于目标地址)。如果你将绝对地址直接放到
call
指令中,生成的机器码可能不会正确地跳转到目标地址,因为它们需要使用相对地址计算。 -
为什么注入后没有反应?
可能有以下原因:
- 偏移量计算错误:如前所述,
call
指令需要相对地址。请确保你计算了正确的偏移量,并将其应用于机器码生成中。 - 权限问题:检查注入的代码是否具有足够的权限执行,确保目标进程允许代码注入和执行。
- 内存保护:确保分配的内存具有正确的保护属性(如
PAGE_EXECUTE_READWRITE
)。
- 偏移量计算错误:如前所述,
-
如何修改才能实现远程线程注入?
你需要确保以下几点:
- 计算相对地址:在生成机器码时,计算
call
指令的相对偏移量。你可以在汇编阶段计算call
指令的偏移量,然后再将其转化为机器码。 - 确保内存权限:分配的内存区域需要有正确的权限设置,确保可以执行写入的机器码。
- 验证注入:确保远程线程的创建和代码注入过程无误。可以通过调试工具检查注入的机器码是否正确执行。
- 计算相对地址:在生成机器码时,计算
-
为什么我学习别人的文章,代码里也没有相关的处理却可以调用成功?
可能是因为这些文章使用了不同的技术栈或工具,这些工具自动处理了相对地址的计算。例如,有些框架或库可能在注入代码前已经计算并调整了相对地址。你可以参考这些工具或库的文档,了解它们如何处理地址计算。
-
为什么内联汇编却可以?
内联汇编通常会由编译器处理,并且编译器会自动管理相对地址和跳转目标的计算。编译器在生成目标代码时会将
call
指令的相对地址计算得很准确,因此你不需要手动处理这些偏移量。
总结
要解决你面临的问题,你需要:
- 确保在汇编到机器码的过程中计算正确的相对地址。
- 检查内存分配和权限设置是否正确。
- 使用调试工具验证注入后的机器码是否按预期执行。
在生成机器码时,特别是涉及 call
指令时,确保理解相对地址和绝对地址的区别,以及如何正确计算和插入相对偏移量。
希望如上措施及解决方案能够帮到有需要的你。
PS:如若遇到采纳如下方案还是未解决的同学,希望不要抱怨&&急躁,毕竟影响因素众多,我写出来也是希望能够尽最大努力帮助到同类似问题的小伙伴,即把你未解决或者产生新Bug黏贴在评论区,我们大家一起来努力,一起帮你看看,可以不咯。
若有对当前Bug有与如下提供的方法不一致,有个不情之请,希望你能把你的新思路或新方法分享到评论区,一起学习,目的就是帮助更多所需要的同学,正所谓「赠人玫瑰,手留余香」。
☀️写在最后
如上问题有的来自我自身项目开发,有的收集网站,有的来自读者…如有侵权,立马删除。再者,针对此专栏中部分问题及其问题的解答思路或步骤等,存在少部分搜集于全网社区及人工智能问答等渠道,若最后实在是没能帮助到你,还望见谅!并非所有的解答都能解决每个人的问题,在此希望屏幕前的你能够给予宝贵的理解,而不是立刻指责或者抱怨!如果你有更优解,那建议你出教程写方案,一同学习!共同进步。
ok,以上就是我这期的Bug修复内容啦,如果还想查找更多解决方案,你可以看看我专门收集Bug及提供解决方案的专栏《CSDN问答解惑-专业版》,都是实战中碰到的Bug,希望对你有所帮助。到此,咱们下期拜拜。
码字不易,如果这篇文章对你有所帮助,帮忙给 bug菌 来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。
同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!
📣关于我
我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。