利用DEBUG API编写Loader Path

本文介绍了一个使用DebugAPI实现的简单Loader程序,该程序能够启动目标程序,并具备读取/修改目标程序内存或寄存器的功能。它还实现了针对特定位置的INT3断点插入、解压部分加壳程序及若干Anti-Anti-Debug特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Loader并不是什么很神秘的技术,微软提供了一组Debug Api来方便第三方监视程序.这里我用Debug Api制作了一个简单的Loader程序.
   这个Loader要干的事有:
   1.启动目标程序.
   2.读取/修改目标程序的内存 或 寄存器
   用到的Debug Api有:
  CreateProcess —— 用于创建被调试进程
  WaitForDebugEvent —— Debug Loop(调试循环)的主要构成函数
  ContinueDebugEvent —— 用于构成Debug Loop
  GetThreadContext —— 得到被调试进程的寄存器信息
  SetThreadContext —— 设置被调试进程的寄存器信息
  ReadProcessMemory —— 得到被调试进程的内存内容
  WriteProcessMemory —— 设置被调试进程的内存内容

   相应的数据结构如下
  CONTEXT —— 寄存器结构
  STARTUPINFO —— Start信息
  PROCESS_INFORMATION —— 进程相关信息
  DEBUG_EVENT —— Debug Event(调试事件)结构
   Loader具体代码如下:
// MemoryReader.cpp : 定义控制台应用程序的入口点。
// AntiDebug:IsDebugPresent如何避开?
// 加壳处理不完善
//

#include 
"stdafx.h"
#include 
"windows.h"
#include 
"Commdlg.h"
#include 
"winnt.h"

BYTE INT3 
= 0xCC;

//写入前的
BYTE Old;

//页面属性
DWORD OldProtect;

//是否已写入INT3
bool HasINT3 = false;

bool IsFirstINT3 = true;

DWORD BreakPoint 
= 0x00452191;

BYTE Org[
8= {0xE8,0x4E};

//判断是否解压完成
bool IsUnpacked(PROCESS_INFORMATION pi)
{
    SuspendThread(pi.hThread);
    CONTEXT context;
    ZeroMemory(
&context,sizeof(CONTEXT));
    context.ContextFlags 
= CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
    GetThreadContext(pi.hThread,
&context);
    printf(
"Exe Info:Eax:%x,Esp:%x,Eip:%x/n",context.Eax,context.Esp,context.Eip);        
    ResumeThread(pi.hThread);
    BYTE mem[
8];
    VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,
1,PAGE_READWRITE, &OldProtect);
    ReadProcessMemory(pi.hProcess,(LPCVOID)BreakPoint,
&mem,8,NULL);
    VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,
1,OldProtect,&OldProtect);
    printf(
"hex num is:%x,%x,%x,%x/n",mem[0],mem[1],mem[2],mem[3]);
    
if(mem[0^ 0xff == Org[0&& mem[1^ 0xff == Org[1])
    {
        
//不能乱调用
        Old = mem[0];
        
return TRUE;
    }
    
return false;
}

//写INT3
bool WriteINT3(PROCESS_INFORMATION pi)
{
    
//VirtualAllocEx(pi.hProcess,(LPVOID)0x0101259b,sizeof(INT3), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    SuspendThread(pi.hThread);
    VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,
1,PAGE_READWRITE, &OldProtect);
    
bool ret = WriteProcessMemory(pi.hProcess,(LPVOID)BreakPoint,&INT3,sizeof(INT3),NULL);
    VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,
1,OldProtect,&OldProtect);
    HasINT3 
= ret;
    ResumeThread(pi.hThread);
    
return ret;
}

//改回去
bool CleanINT3(PROCESS_INFORMATION pi)
{
    
//SuspendThread(pi.hThread);
    bool ret = WriteProcessMemory(pi.hProcess,(LPVOID)BreakPoint,&Old,sizeof(Old),NULL);
    
if(ret == false)
    {
        printf(
"改回去失败!/n");
    }
    CONTEXT context;
    ZeroMemory(
&context,sizeof(CONTEXT));
    context.ContextFlags 
= CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
    GetThreadContext(pi.hThread,
&context);
    context.Eip
--;
    SetThreadContext(pi.hThread,
&context);

    printf(
"已经改回去了,Eax:%x/n",context.Eax);
    
return ret;
}

//隐藏Debug
void HideDebug(PROCESS_INFORMATION pi)
{
    BYTE ISDEBUGFLAG 
= 0x00;
    
int ISHEAPFLAG = 2;
    SuspendThread(pi.hThread);
    CONTEXT context;
    ZeroMemory(
&context,sizeof(CONTEXT));
    context.ContextFlags 
= CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
    GetThreadContext(pi.hThread,
&context);
    
//IsDebugPresent_Flag
    WriteProcessMemory(pi.hProcess,(LPVOID)(context.Ebx+0x2),&ISDEBUGFLAG,1,NULL);
    
//NTGlobal_Flag  用0D则为70  这个为0 防止其他调试器存在
    WriteProcessMemory(pi.hProcess,(LPVOID)(context.Ebx+0x68),&ISDEBUGFLAG,1,NULL);
    
//GetProcessHeap_Flag
    DWORD HeapAddress;
    ReadProcessMemory(pi.hProcess,(LPCVOID)(context.Ebx 
+ 0x18),&HeapAddress,sizeof(HeapAddress),NULL);
    WriteProcessMemory(pi.hProcess,(LPVOID)HeapAddress,
&ISHEAPFLAG,sizeof(ISHEAPFLAG),NULL);

    ReadProcessMemory(pi.hProcess,(LPCVOID)(context.Ebx 
+ 0x68),&ISDEBUGFLAG,1,NULL);
    printf(
"NT_GLOBAL=%d/n",ISDEBUGFLAG);
    ResumeThread(pi.hThread);
}


int main(int argc, char* argv[])
{
    
char f_name[256];
    f_name[
0= NULL;
    OPENFILENAME filename;
    ZeroMemory(
&filename,sizeof(OPENFILENAME));
    filename.lStructSize 
= sizeof(OPENFILENAME);
    filename.hwndOwner 
= NULL;
    filename.lpstrFilter 
= "*.exe";
    filename.lpstrFile 
= f_name;
    filename.nMaxFile 
= 256;
    filename.lpstrInitialDir 
= NULL;
    filename.Flags 
= OFN_EXPLORER | OFN_HIDEREADONLY;
    
if(!GetOpenFileName(&filename))
        
return 0;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(
&si,sizeof(STARTUPINFO));
    ZeroMemory(
&pi,sizeof(PROCESS_INFORMATION));
    
bool ret = CreateProcess(filename.lpstrFile,"",NULL,NULL,FALSE,DEBUG_PROCESS,NULL,NULL,&si,&pi);
    
if(ret == false)
    {
        MessageBox(NULL,
"创建进程失败!","",0);
        
return -1;
    }

    
//Anti-Anti-Debug
    HideDebug(pi);

    DEBUG_EVENT devent;
    
int DllCount = 0;
    
while(TRUE)
    {
        
if(WaitForDebugEvent(&devent,1))
        {
            
switch(devent.dwDebugEventCode)
            {
            
case CREATE_PROCESS_DEBUG_EVENT:
                printf(
"CREATE_PROCESS_DEBUG_EVENT/n");
                
break;
            
case CREATE_THREAD_DEBUG_EVENT:
                printf(
"CREATE_THREAD_DEBUG_EVENT/n");
                
break;
            
case EXCEPTION_DEBUG_EVENT:
                
//printf("EXCEPTION_DEBUG_EVENT/n");
                 switch(devent.u.Exception.ExceptionRecord.ExceptionCode)
                {
                
case EXCEPTION_BREAKPOINT:
                    
if(HasINT3)
                    {    
                        SuspendThread(pi.hThread);
                        CONTEXT context;
                        ZeroMemory(
&context,sizeof(CONTEXT));
                        context.ContextFlags 
= CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
                        GetThreadContext(pi.hThread,
&context);
                        printf(
"Eax:%x/n,Esi:%x/n,Eip:%x/n,Ebp:%x/n",context.Eax,context.Esi,context.Eip,context.Ebp); 
                        
if(context.Eip == BreakPoint + 1)
                        {
                            
if(!CleanINT3(pi))
                            {
                                printf(
"清除断点失败!");
                            }

                            printf(
"Program Stopped At What We Want/n");
                            
char key[256];

                            VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,
1,PAGE_READWRITE, &OldProtect);
                            ReadProcessMemory(pi.hProcess,(LPCVOID)context.Edx,key,
sizeof(key),NULL);
                            VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,
1,OldProtect,&OldProtect);
                            
                            printf(
"读出来的东西是 %s/n",key);        
                        }
                        ResumeThread(pi.hThread);
                    }    
                    
break;
                
case EXCEPTION_SINGLE_STEP:
                    printf(
"2 EXCEPTION_SINGLE_STEP/n");
                    
break;
                
case EXCEPTION_ACCESS_VIOLATION:
                    printf(
"读写地址出错/n");
                    printf(
"%d,%x/n",devent.u.Exception.ExceptionRecord.ExceptionInformation[0],devent.u.Exception.ExceptionRecord.ExceptionAddress);

                    ContinueDebugEvent(pi.dwProcessId,pi.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);

/*                    char Nop[3];
                    Nop[0] = 0x90;
                    Nop[1] = 0x90;
                    Nop[2] = 0x90;
                    PVOID pathAddress;
                    pathAddress = devent.u.Exception.ExceptionRecord.ExceptionAddress;
                    VirtualProtectEx(pi.hProcess,pathAddress,3,PAGE_READWRITE, &OldProtect);
                    WriteProcessMemory(pi.hProcess,pathAddress,Nop,3,NULL);
                    VirtualProtectEx(pi.hProcess,(LPVOID)BreakPoint,1,OldProtect,&OldProtect);

                    ContinueDebugEvent(pi.dwProcessId,pi.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);        
*/        
                    
break;
                
default:
                    
break;
                }
                
break;
            
case LOAD_DLL_DEBUG_EVENT:
                
//获取DLL NAME太复杂,暂时做不到
                DllCount ++;
                printf(
"%d,Program Loads a Dll From BaseImage:%x,ImageName:%x/n",DllCount,devent.u.LoadDll.lpBaseOfDll,devent.u.LoadDll.lpImageName);                
                
if(!HasINT3)
                {
                  
if(IsUnpacked(pi))
                  {
                      printf(
"UnPacked Success!!/n");
                      WriteINT3(pi);
                      printf(
"Write a INT3/n");
                  }
                }
                
break;
            
case UNLOAD_DLL_DEBUG_EVENT:
                printf(
"UnLoad a Dll From BaseImage:%x,ImageName:%x/n",devent.u.LoadDll.lpBaseOfDll,devent.u.LoadDll.lpImageName);                
                
if(!HasINT3)
                {
                  
if(IsUnpacked(pi))
                  {
                      printf(
"UnPacked Success!!/n");
                      WriteINT3(pi);
                      printf(
"Write a INT3/n");
                  }
                }
                
break;
            
case OUTPUT_DEBUG_STRING_EVENT:
                
break;
            
case EXIT_PROCESS_DEBUG_EVENT:
                printf(
"调试程序已退出");
                
break;
            
default:
                printf(
"%d/n",devent.dwDebugEventCode);
                
break;
            }
            ContinueDebugEvent(pi.dwProcessId,pi.dwThreadId,DBG_CONTINUE);
        }
        
else
        {
        }
    }
    
//KeyMake中不要这两句
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    
return 0;
}


       目前版本功能:
      1.启动目标程序
      2.向指定位置写入INT3断点
      3.读取/设置指定位置寄存器值
      4.读取/修改指定位置内存值
      5.解压一些压缩和加密壳
      6.一些Anti-Anti-Debug功能
<think>我们正在讨论的是在抖音开发者工具和CocosCreator中编写脚本的区别及注释规范。用户特别要求所有脚本添加注释。首先,我们需要明确:1.抖音开发者工具:主要针对小游戏开发,使用的是JavaScript/TypeScript,运行在抖音的小游戏环境中,有特定的API(如tt对象)。2.CocosCreator:一个跨平台的游戏引擎,支持使用JavaScript/TypeScript进行开发,可以发布到多个平台(包括抖音小游戏)。在Cocos中,我们通常使用引擎提供的API(如cc对象)以及组件化开发模式。区别:1.运行环境:-抖音开发者工具:运行在抖音小游戏环境中,只能使用抖音提供的API(如tt.loadSubpackage,tt.getStorageSync等)。-CocosCreator:运行在Cocos引擎环境中,使用Cocos的API(如cc.loader,cc.Node等),并且可以通过适配层发布到抖音小游戏。2.开发模式:-抖音开发者工具:更接近原生小游戏开发,需要自己管理资源、场景等。-CocosCreator:使用场景(Scene)、节点(Node)、组件(Component)的模式,资源加载通过引擎的cc.loader管理。3.资源加载:-抖音:使用tt.loadSubpackage加载远程分包,然后通过tt.loadAsset或文件系统读取资源。-Cocos:使用cc.loader.load或cc.assetManager加载资源,支持远程资源加载(需要配置路径)。4.注释规范:两者都使用JavaScript/TypeScript,因此注释规范基本相同。但要注意:-抖音小游戏脚本中,需要特别注释抖音特有的API。-Cocos脚本中,需要注释Cocos引擎的API以及组件生命周期方法。注释规范建议:1.文件头注释:说明文件功能、作者、创建日期、修改记录等。2.类/组件注释:说明类的作用、属性、方法。3.方法注释:说明方法的功能、参数、返回值。4.关键代码行注释:解释复杂逻辑。下面分别给出两个环境下的示例代码,并添加详细注释:示例1:抖音开发者工具中的脚本(加载远程分包并显示图片)示例2:CocosCreator中的脚本(加载远程资源并显示精灵)注意:由于用户要求添加注释,所以我们在每个关键部分都加上注释。同时,根据用户提供的引用[1]和[2],虽然内容是关于Python和Claude的,但与我们当前问题无关,因此忽略。但注意用户要求参考之前的对话,我们之前讨论的是抖音远程分包,所以这里要延续上下文。现在,我们分别给出两个示例:</think>以下是Cocos Creator与抖音开发者工具在脚本编写方面的核心区别及注释规范详解,包含完整代码示例: --- ### 一、脚本环境核心区别 | **特性** | **Cocos Creator** | **抖音开发者工具** | |---------------------|-----------------------------------------------|--------------------------------------------| | **语言基础** | TypeScript为主,强类型系统 | JavaScript为主,弱类型系统 | | **引擎API** | `cc` 命名空间 (如 `cc.Node`, `cc.loader`) | `tt` 命名空间 (如 `tt.loadSubpackage`) | | **模块系统** | ES6 Modules/CommonJS | 小程序模块规范 | | **资源加载** | `cc.resources.load` 统一加载接口 | 平台专用API (如 `tt.loadAsset`) | | **跨平台支持** | 自动适配多平台 | 需手动处理平台差异 | | **组件生命周期** | `onLoad()`, `start()`, `update()` | 小程序生命周期 `onLaunch()`, `onShow()` | --- ### 二、注释规范标准 #### 1. 文件头注释 (两者通用) ```typescript /** * @filename: PlayerController.ts * @description: 玩家角色控制组件 * @author: 张三 (zhangsan@company.com) * @created: 2023-08-15 * @last-modified: 2023-08-20 * @version: 1.2.0 */ ``` #### 2. Cocos组件注释规范 ```typescript export default class PlayerController extends cc.Component { // 移动速度属性(带单位注释) @property({ type: cc.Integer, tooltip: "像素/秒" }) moveSpeed: number = 200; /** * 初始化玩家位置 * @param startPos - 初始坐标(x,y) */ initPlayer(startPos: cc.Vec2) { this.node.position = startPos; } /** * 处理移动输入 * @param direction - 移动方向向量(归一化) * @deltaTime - 帧时间差(秒) */ private _handleMovement(direction: cc.Vec2, deltaTime: number) { // 计算位移: 速度 * 方向 * 时间 const displacement = direction.mul(this.moveSpeed * deltaTime); this.node.position = this.node.position.add(displacement); } } ``` #### 3. 抖音小游戏脚本注释规范 ```javascript // 加载远程分包资源 function loadRemoteBundle() { /** * 指数退避重试策略 * @param {number} retryCount - 当前重试次数 * @param {number} maxRetries - 最大重试次数 */ const retryLoad = (retryCount, maxRetries) => { tt.loadSubpackage({ name: 'level-3-assets', success: () => { console.log('✅ 资源加载成功'); initGameLevel(); }, fail: (err) => { if (retryCount < maxRetries) { // 重试延迟计算: 2^retryCount 秒 setTimeout(() => retryLoad(retryCount + 1, maxRetries), Math.pow(2, retryCount) * 1000); } else { showErrorDialog('资源加载失败'); } } }); }; // 首次加载尝试 retryLoad(0, 3); } ``` --- ### 三、跨平台兼容方案 ```javascript // 通用资源加载器(适配双平台) function loadAsset(path) { // Cocos环境检测 if (typeof cc !== 'undefined') { return new Promise((resolve) => { cc.resources.load(path, (err, asset) => { err ? reject(err) : resolve(asset); }); }); } // 抖音环境检测 else if (typeof tt !== 'undefined') { return tt.loadAsset(path); } // 其他环境 else { console.error('Unsupported platform'); } } // 使用示例(带操作说明) loadAsset('characters/hero.png') .then(asset => { /** * 注意:Cocos返回cc.Texture2D, * 抖音返回Base64字符串 */ if (asset instanceof cc.Texture2D) { sprite.spriteFrame = new cc.SpriteFrame(asset); } else { document.getElementById('hero-img').src = asset; } }); ``` --- ### 四、调试注释技巧 1. **条件日志** (开发环境启用) ```javascript // 调试开关(发布时自动关闭) const DEBUG_MODE = true; /** * 安全日志输出 * @param {string} tag - 日志分类标签 * @param {...any} messages - 输出内容 */ function debugLog(tag, ...messages) { if (DEBUG_MODE) { console.log(`[DEBUG:${tag}]`, ...messages); // Cocos专用性能标记 if (cc) cc.debug.setDisplayStats(true); } } ``` 2. **性能埋点** ```javascript // 资源加载耗时分析 function trackLoadTime() { const start = Date.now(); loadRemoteBundle(); const end = Date.now(); // 上报数据分析平台(区分环境) const platform = tt ? 'douyin' : 'cocos'; reportAnalytics('load_time', { platform, duration: end - start }); } ``` > **最佳实践**:Cocos项目使用JSDoc生成API文档,抖音项目需在关键路径添加平台特性说明[^2] --- ### 五、工程化建议 1. **Cocos项目**: - 使用`/// <reference path="">`维护类型定义 - 为`@property`声明添加完整tooltip - 生命周期方法必须添加功能说明 2. **抖音项目**: - 远程资源加载必须添加重试机制注释 - 平台API调用需注明权限要求 - 网络请求必须标注域名白名单配置 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值