256色老游戏截图出现花屏

lyshiba 2013-02-22 02:19:42
发现我截的几个256色游戏:blood,diablo,热血传奇、暴力摩托都是花屏的,
就是有轮廓但是颜色错乱,哪位兄弟帮忙给个能正确截图的代码,
只要存成bmp即可,急用,谢谢!
...全文
382 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
lyshiba 2013-02-26
  • 打赏
  • 举报
回复
引用 10 楼 zzz3265 的回复:
应该是调色板问题, 需要自己获取当前的调色板

我已经获取到当前调色板了,代码如下:
 // 返回颜色表项数
int nColors = GetDeviceCaps(hDC(),SIZEPALETTE);
if (! nColors)
nColors = GetDeviceCaps(hDC,NUMCOLORS);

// 定义句柄,并分配空间
HANDLE hLogPal = GlobalAlloc(GHND,sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY));

if (! hLogPal)
return NULL;

// 句柄和调色板表项结构指针建立联系
LPLOGPALETTE lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal);

// 初始化调色板表项结构
lpLogPal->palVersion = 0x300;
lpLogPal->palNumEntries = (WORD)nColors;
GetSystemPaletteEntries(hDC,0,nColors,(LPPALETTEENTRY)(lpLogPal->palPalEntry));
// 定义调色板句柄,根据调色板表项创建调色板
HPALETTE hPal1 = CreatePalette(lpLogPal);
if (hPal1)
{
hDC = ::GetDC(NULL);
hOldPal=SelectPalette(hDC,hPal1,FALSE);
AnimatePalette(hPal1, 0, nColors, (LPPALETTEENTRY)(lpLogPal->palPalEntry));
RealizePalette(hDC);
}

// 获取该调色板下新的像素值
GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwPaletteSize, (BITMAPINFO *)lpbi,DIB_RGB_COLORS);
//保存图片、释放资源

虽然没有前面那种花屏的现象,但是截图还是有一点色差,截图如下:

游戏原来现实效果如下:

是我获取当前调色板错误,还是有其他因素没有考虑?

lyshiba 2013-02-26
  • 打赏
  • 举报
回复
引用 11 楼 shen_wei 的回复:
获取当前的调色板。。 biBitCount; 设置16
不行,biBitCount的值是通过GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES)。对于256色的老游戏截图,biBitCount的值肯定是8,强行设置成16,打开保存的图片时候会出现绘图失败。
shen_wei 2013-02-25
  • 打赏
  • 举报
回复
获取当前的调色板。。 biBitCount; 设置16
Yofoo 2013-02-22
  • 打赏
  • 举报
回复
应该是调色板问题, 需要自己获取当前的调色板
一个傻冒 2013-02-22
  • 打赏
  • 举报
回复
把Bitmap的PixelFormat属性改成256色。 默认好象是24位。
lyshiba 2013-02-22
  • 打赏
  • 举报
回复
这个是暴利摩托的截图。不是dx的问题,是256色的问题。你可以把自己电脑显示调成256色,然后用QQ截图试试。也是花屏。电脑调成256色:桌面--右击属性--设置--高级--适配器--列出所有模式。
一个傻冒 2013-02-22
  • 打赏
  • 举报
回复
传奇,暗黑这肯定不是DOS游戏。 我有一点不明白,你说花屏,这几款游戏都是这个现象吗?用DX做的游戏,你抓屏应该是黑屏啊。
stjay 2013-02-22
  • 打赏
  • 举报
回复
貌似是DOS游戏?
一个傻冒 2013-02-22
  • 打赏
  • 举报
回复
原文出处:http://blog.csdn.net/xieqidong/archive/2008/05/05/2391111.aspx 对DirectX/COM接口的挂钩 一般的挂钩(Hook)都是针对Windows API或消息的,而本文要讲的是如何挂钩一个DirectX/COM接口,有意思吧,请往下看,文中以DirectInput作为范例进行讲解。 目标 相比挂钩一个API调用,拦截一个COM接口的方法需要做更多的工作,如果我们要拦截的DLL已经被作者仔细检查过,只导出了类似create这样的接口函数,那么怎样才能达到我们挂钩的目的呢? 从本质上来说,一个COM接口就是一张与其链接在一起的虚函数指针列表,所以,我们只需跟踪链接,并查看每一个节点,直到找到想要替换的函数指针即可。 第一步 从上图也可以看到,只有类似create的接口COM函数是可见的,由于DirectInputCreate函数会返回一个COM接口,所以就从它开始跟踪,在此,可以把DLL注入到目标程序的输入地址表(IAT)中。 第二步 如果目标程序调用了DirectInputCreate,我们的函数也会被调用,而且,会得到一个指针,其指向了虚函数表的指针,而这个虚函数表就是DirectInput的接口。 DECLARE_INTERFACE_(IDirectInputW, IUnknown) { /*** IUnknown methods ***/ STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; STDMETHOD_(ULONG,AddRef)(THIS) PURE; STDMETHOD_(ULONG,Release)(THIS) PURE; /*** IDirectInputW methods ***/ STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *, LPUNKNOWN) PURE; STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW, LPVOID,DWORD) PURE; STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; }; 第三步 现在,就可用CreateDevice创建自己的设备了,在此会再次得到一个不同的虚函数指针表地址,它代表了设备。 选择需要替换的方法,并在适当位置修改虚函数指针表以注入我们自己的函数。 实现 下面是实现步骤 第一步 要对一个API函数进行挂钩,可以使用SetWindowsHookEx这个Windows API,在此,我们创建了一个系统钩子,以监视启动的进程,检查其中是否有我们的目标程序。在确定之后,必须把它的输入模块名与想要进行替换的DLL进行比较,因为我们是对DirectInput进行挂钩,所以此项为DINPUT8.DLL。要找到此DLL,需遍历描述符。 //遍历每个输入描述符,在必要时重定向 while ( pImportDesc->FirstThunk ) { PSTR pszImportModuleName = MakePtr( PSTR, hModEXE, pImportDesc->Name); if ( lstrcmpi( pszImportModuleName, Hook->Name ) == 0 ) { sprintf(dbBuffer,"Dll Found in module %s replace it\n", Hook->Name ); WriteToLog(dbBuffer); RedirectIAT( Hook, pImportDesc, (PVOID)hModEXE ); } pImportDesc++; //继续下一个输入描述符 } 找到之后,应使用VirtualQuery( pIAT, &mbi, sizeof(mbi) )从IAT中移除写保护,这样就可以写入到内存中了。在内存打开之后,还需遍历IAT查找入口项。 while ( pIteratingIAT->u1.Function ) { void* HookFn = 0; if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) ) { PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData ); //遍历挂钩函数 SFunctionHook* FHook = DLLHook->Functions; while ( FHook->Name ) { if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 ) { sprintf(dbBuffer,"Hooked function: %s\n", (char*)pImportName->Name ); WriteToLog(dbBuffer); //在结构SFunctionHook中保存被替换的函数 FHook->OrigFn = (unsigned long*)pIteratingIAT->u1.Function; HookFn = FHook->HookFn; break; } FHook++; } } } 现在,可替换为自己的函数了。 //在挂钩之后,替换IAT函数指针 if ( HookFn ) { //检查是代码还是数据 //如果是代码,不应写入。 if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) ) { pIteratingIAT->u1.Function = (DWORD)HookFn; } else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { //检查“桩”是否在2GB内存地址空间之上 if ( pIteratingIAT->u1.Function > (DWORD)0x80000000 ) pIteratingIAT->u1.Function = (DWORD)HookFn; } } 最后就是还原内存属性。 VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare); 第二步 在CreateInterface方法内部,通过把我们的CreateDevice函数指针注入到虚函数表(Vtbl)中,就可以挂钩到COM接口内部,而虚函数表则由返回的ppvOut指针得到。 DirectInput8Create_Type OldFn = (DirectInput8Create_Type)D3DHook.Functions[D3DFN_DirectInput8Create].OrigFn; HRESULT hr = OldFn( hinst, dwVersion, riidltf, ppvOut, punkOuter ); 解析此指针,直到找到指向虚函数表接口的指针,而在这个地址上,需要再次移除内存保护,以便注入自己的函数到表中,并且保存原函数指针。 接下来,把我们自己的函数指针注入到虚函数表接口内CreateDevice函数指针的偏移量上,并还原内存保护。 可以看到,CreateDevice是DirectInput接口的第4个方法,这意味着其在虚函数表内的偏移量为0x0C(指针乘3) typedef struct IDirectInput *LPDIRECTINPUT; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectInput_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) #define IDirectInput_AddRef(p) (p)->lpVtbl->AddRef(p) #define IDirectInput_Release(p) (p)->lpVtbl->Release(p) #define IDirectInput_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c) #define IDirectInput_EnumDevices(p,a,b,c,d) (p)->lpVtbl->EnumDevices(p,a,b,c,d) #define IDirectInput_GetDeviceStatus(p,a) (p)->lpVtbl->GetDeviceStatus(p,a) #define IDirectInput_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) #define IDirectInput_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) #else #define IDirectInput_QueryInterface(p,a,b) (p)->QueryInterface(a,b) #define IDirectInput_AddRef(p) (p)->AddRef() #define IDirectInput_Release(p) (p)->Release() #define IDirectInput_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c) #define IDirectInput_EnumDevices(p,a,b,c,d) (p)->EnumDevices(a,b,c,d) #define IDirectInput_GetDeviceStatus(p,a) (p)->GetDeviceStatus(a) #define IDirectInput_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) #define IDirectInput_Initialize(p,a,b) (p)->Initialize(a,b) #endif 在知道从何处注入之后,下面就要考虑如何实现了。可以查看CreateDevice在dinput.h头文件中的声明,会发现它与DirectX Help中的并不匹配。 HRESULT CreateDevice( REFGUID rguid, LPDIRECTINPUTDEVICE *lplpDirectInputDevice, LPUNKNOWN pUnkOuter ); 这是它在dinput.h头文件中的定义,所以我们必须添加第四个参数,其为接口指针,下面是完整的函数声明: HRESULT __stdcall PASCAL MyCreateDevice(LPVOID *ppvOut,REFGUID rguid, LPDIRECTINPUTDEVICE *lplpDirectInputDevice, LPUNKNOWN pUnkOuter 另外有一点非常重要,必须在声明中使用 __stdcall调用约定。__stdcall调用约定常用于调用Win32 API函数,被调用者负责清理堆栈;而__cdecl则是C和C++程序的默认调用约定,由调用者来负责清理堆栈,这可不是我们想要的。 当查看此调用的反汇编时,会看到堆栈指针验证函数 _RTC_CheckEsp在对接口函数调用之后被调用。 if (lpdi->CreateDevice(GUID_SysKeyboard, &lpdikey, NULL)!=DI_OK) 00401365 mov esi,esp 00401367 push 0 00401369 push offset lpdikey (4552C8h) 0040136E push offset _GUID_SysKeyboard (44643Ch) 00401373 mov eax,dword ptr [lpdi (4552C4h)] 00401378 mov ecx,dword ptr [eax] 0040137A mov edx,dword ptr [lpdi (4552C4h)] 00401380 push edx 00401381 mov eax,dword ptr [ecx+0Ch] 00401384 call eax 00401386 cmp esi,esp 00401388 call _RTC_CheckEsp (4026A0h) 0040138D test eax,eax 0040138F je Game_Init+78h (401398h) return(0); 00401391 xor eax,eax 00401393 jmp Game_Init+107h (401427h) //设置协作级 if (lpdikey->SetCooperativeLevel(main_window_handle,DDSCL_NORMAL); 如果忘了把函数声明为 __stdcall,函数也会正常工作,但是esp指针测试将会失败,因为其会设置eax,并在函数调用之后对它进行测试。 第三步 现在,当创建设备时,调用会重定向到我们的CreateDevice函数中。另外,在我们调用原始函数之后,会在lplpDirectInputDevice中得到一个新的指针,它可以把我们直接带到设备的虚函数表中。 HRESULT hr = OldCreateDev(ppvOut,rguid,lplpDirectInputDevice,pUnkOuter); 比如说,我们要替换GetDeviceState这个函数,为得到它的偏移量,必须在DInput.dll内部查找其定义,可以看到它是第10个方法,所以偏移量为0x24。知道了偏移量,按照第二步中介绍的步骤:移除内存保护、保存原始指针、注入自己的函数、还原内存保护,一气呵成。
lyshiba 2013-02-22
  • 打赏
  • 举报
回复
嗯,具体点?
一个傻冒 2013-02-22
  • 打赏
  • 举报
回复
兄弟DX截图那得hook dx。
lyshiba 2013-02-22
  • 打赏
  • 举报
回复
楼上的方法不行啊,请问有DX的截图方法吗?
一个傻冒 2013-02-22
  • 打赏
  • 举报
回复
http://download.csdn.net/download/zhou0rz/4192142
登陆器支持IGE0204引擎,无限制,无病毒,支持最新功能! 1、添加登陆器窗口化模式 2、客户端内核代码添加了16位真彩 3、修复LOGO变蓝的问题 4、移除内核跟登陆器通讯的加密方式 5、改接口文件名为mir.ini 6、登陆器配置器添加两套界面 7、修正内核雷达信息显示为蓝点的问题 8、修正退出游戏后不能切换为原桌面32位颜的问题 9、修正进入游戏后窗口的坐标位置显示不正确问题 10、移除登陆器释放文件mir.ini中的多余文件,改FulScreen变量为WindowsMode 11、禁用了登陆器的添加游戏、脱机登陆两种按钮 12、修改登陆器版本为商业版 13、更新客户端内核至:2014.06.30 14、修改登陆器公告打开时执行公告地址。 15、添加游戏备用列表功能。 16、取消登陆器选择窗口化模式时变换16位真彩,已经修改到游戏内核运行。 17、取消登陆器跟配置器的加密系统文件,全部修改为明文! 18、修改荣誉、人物状态栏、英雄头像栏、英雄状态栏等图片的读取位置改为自定义补丁读取。否则卡屏! 19、修改游戏版本错误提示的网址。 20、修复登陆器在16位桌面中运行 窗口化登陆字样 出现白底现象! 21、完成登陆器支持盛大最新游戏客户端和游戏客户端![同时支持WIL和WZL两种格式] 22、修复最新客户端人物发型显示错误问题! 23、添加新土城、沙巴克显示功能,解决登陆器在新客户端下土城花屏问题、完善沙巴克显示错乱问题! 24、修改登陆器只搜索wil客户端的问题。现已支持最新客户端自动搜索功能。 25、修改登陆器找回密码字体颜为黑。显示明显,原来为白显示不明显。 26、修改登陆器添加游戏更能为游戏介绍地址、脱机登陆为游戏充值地址。 27、取消登陆游戏时LOGO显示,无需等待 28、完善打开登陆器自动执行公告和官方网站等按钮不能用的问题 29、修正充值页面地址打开为系统C盘的问题。 30、删除本地列表读取功能和编辑游戏功能,减小程序的大小 31、设置列表点开后不自动关闭上面的列表。 32、修复服务器列表获取失败不提示的问题 33、取消客户端退出游戏还原桌面颜,修改为登陆器退出后还原桌面颜,否则双开会有一个黑屏的。 34、解决火龙守护兽不显示的问题 35、修正商铺点开不能关闭的问题 36、修正火龙教主打死后人物飞地图黑屏及玩家消失的问题。 37、修正登陆器点开第二分组时充值按钮和介绍按钮混乱的问题。 38、修正wzl客户端下空文件导致游戏无法进入的问题。 39、修正客户端打开时窗口不居中的问题。

15,978

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧