-
在WII游戏纸片马里奥中读取SD卡文件数据(续)
2012-01-30 20:52彻底搞定了,终于在应用层实现了运行时文件重定向到SD卡。
之前失败的原因是目标文件是通过异步API读取的文件,我修改读取到的buffer数据时这片数据已经被处理程序读取过了。
于是可以这么办,
先同步读文件,
DVDReadPrio(pFileinfo,addr,length,offset,prio);
然后修改目的数据
int err = fat_open("paper.bfn");
if(!err)
{
fat_read(addr,offset,length);
DCFlushRange(addr,length);
}
这时候调用一次异步读取函数,但是呢,不给他读到期望的地方,只是象征性的给读到一个无效的内存上,目的是通知回调,“我们已经完成了读取”,这时候程序会去处理目标内存的数据,但是已经是SD卡里的数据了。
bResult = DVDReadAsyncPrio(pFileinfo,0x80001800,4,0,callback,prio);
}测试通过。
这是我自09年放出LeijiLoader后的一大心愿,在发售游戏内部运行时读取SD卡文件替换DVD文件访问,今天终于实现了,很开心,哈哈。
-
在WII游戏纸片马里奥中读取SD卡文件数据
2012-01-29 21:57sd卡和fat文件系统相关函数直接使用塞尔达TP HACK项目中的代码,ios层函数在纸马中定位的API。
分别是
int (*_ios_open)(const char *filename, u32 mode) = 0x802958FC;
int (*_ios_close)(int fd) = 0x80295ADC;
int (*_ios_ioctl)(int fd, u32 n, const void *in, u32 inlen, void *out, u32 outlen) = 0x802961AC;
int (*_ios_ioctlv)(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec) = 0x802964FC;
虽然成功打开并读取了SD上的文件,却无法实现DVD文件读取重定向到SD卡的功能,给指定地址提供数据后不知道为什么不生效,我刷过缓存了。
main.c代码如下:
#include "loader.h"
enum ApiIdEnum
{
WIIAPIID_DVDConvertPathToEntrynum,
WIIAPIID_DVDOpen,
WIIAPIID_DVDFastOpen,
WIIAPIID_DVDReadPrio,
WIIAPIID_DVDReadAsyncPrio,
WIIAPIID_COUNT
};
#define DOLSTART 0x80004000
int g_nApiAddress[WIIAPIID_COUNT];
BOOL ApiHook(int nHookFromAddress,int nHookToAddress,BOOL bHook);
int FindRawCode(const unsigned char* SearchPattern,int nSize);
BOOL HOOK_DVDOpen(char* szDVDFilePath, DVDFileInfo* pFileinfo);
int HOOK_DVDReadPrio(DVDFileInfo* pFileinfo,unsigned char* buf,int nLength,int nOffset,int nPrio);
BOOL HOOK_DVDFastOpen(int nEntrynum,DVDFileInfo* pFileinfo);
BOOL HOOK_DVDReadAsyncPrio( DVDFileInfo* fileInfo, void* addr, s32 length,s32 offset,DVDCallback callback, s32 prio );
void start()
{
g_nApiAddress[WIIAPIID_DVDConvertPathToEntrynum] = 0x80278F24;
g_nApiAddress[WIIAPIID_DVDOpen] = 0;
g_nApiAddress[WIIAPIID_DVDFastOpen] = 0x8027922C;
g_nApiAddress[WIIAPIID_DVDReadPrio] = 0x802793A0;
g_nApiAddress[WIIAPIID_DVDReadAsyncPrio] = 0x802792B8;
ApiHook(g_nApiAddress[WIIAPIID_DVDOpen],HOOK_DVDOpen,TRUE);
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,TRUE);
ApiHook(g_nApiAddress[WIIAPIID_DVDReadPrio],HOOK_DVDReadPrio,TRUE);
ApiHook(g_nApiAddress[WIIAPIID_DVDReadAsyncPrio],HOOK_DVDReadAsyncPrio,TRUE);
unsigned char SearchMEM2Fix[] = {0x3C,0x80,0x90,0x00,0x7C,0x03,0x20,
0x40,0x41,0x80,0,0,0x38,0x04,0x08,0x00,0x7C,0x03,0x00,0x40,0x40,0x80};
//令MEM2上的堆内存起始地址后撤,获得128K内存
int nMEM2FixAddr = FindRawCode(SearchMEM2Fix,sizeof(SearchMEM2Fix));
*(int*)nMEM2FixAddr = 0x3C809002;
*((int*)nMEM2FixAddr+2) = 0x60000000;
*((int*)nMEM2FixAddr+5) = 0x60000000;
}
typedef struct HookInfo
{
int code1;
int code2;
int code3;
int code4;
int funaddr;
}HookInfo;
HookInfo g_hookInfo[WIIAPIID_COUNT];
BOOL ApiHook(int nHookFromAddress,int nHookToAddress,BOOL bHook)
{
if(nHookFromAddress == 0)
return FALSE;
for(int i=0;i {
//安装钩子
if(bHook && g_hookInfo[i].funaddr == 0)
{
//发现未使用的钩子结构,保存
memcpy(&g_hookInfo[i].code1,(int*)nHookFromAddress,4*4);
g_hookInfo[i].funaddr = nHookToAddress;
//指令劫持
/* lis r0,x@h
** ori r0,r0,x@l
** mtctr r0
** bctr
*/
*(int*)nHookFromAddress = 0x3c010000 + (nHookToAddress/65536) - 1;
*((int*)nHookFromAddress+1) = 0x60010000 + (nHookToAddress%65536);
*((int*)nHookFromAddress+2) = 0x7c0903a6;
*((int*)nHookFromAddress+3) = 0x4e800420;
//刷新数据缓存 指令缓存
DCFlushRange(nHookFromAddress,4*4);
ICInvalidateRange(nHookFromAddress,4*4);
ICSync();
return TRUE;
}
//拆除钩子
if(!bHook && g_hookInfo[i].funaddr == nHookToAddress)
{
//还原劫持的指令
memcpy((int*)nHookFromAddress,&g_hookInfo[i].code1,4*4);
//清除钩子结构
g_hookInfo[i].funaddr = 0;
//刷新数据缓存 指令缓存
DCFlushRange(nHookFromAddress,4*4);
ICInvalidateRange(nHookFromAddress,4*4);
ICSync();
return TRUE;
}
}
return FALSE;
}
int FuzzyMemcmp(unsigned char* a1,unsigned char* a2,int nSize)
{
int nIndex = 0;
do
{
if(*(a2+nIndex) == 0)
continue;//模糊匹配
if(*(a1+nIndex) < *(a2+nIndex))
return -1;
else if(*(a1+nIndex) > *(a2+nIndex))
return 1;
}while(++nIndex < nSize);
return 0;
}
int FindRawCode(const unsigned char* SearchPattern,int nSize)
{
int nDolSize = *(int*)0x800030d4;
void* Addr = DOLSTART;
void* Addr_end = Addr + nDolSize;
while(Addr <= Addr_end-nSize)
{
if(FuzzyMemcmp(Addr, SearchPattern, nSize)==0)
{
return Addr;
}
Addr += 4;
}
return 0;
}
BOOL HOOK_DVDFastOpen(int nEntrynum,DVDFileInfo* pFileinfo)
{
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,FALSE);
BOOL (*fpDVDFastOpen)(int nEntrynum,DVDFileInfo* pFileinfo);
fpDVDFastOpen = g_nApiAddress[WIIAPIID_DVDFastOpen];
BOOL bResult = fpDVDFastOpen(nEntrynum,pFileinfo);
int (*fpDVDConvertPathToEntrynum)(const char* fileName) = g_nApiAddress[WIIAPIID_DVDConvertPathToEntrynum];
int nRet = fpDVDConvertPathToEntrynum("font/papermarioset_JPN.bfn");
DVDFileInfo fileinfo;
fpDVDFastOpen(nRet,&fileinfo);
if(fileinfo.startAddr == pFileinfo->startAddr)
{
//找到文件
sd_init();
fat_init();
int err = fat_open("paper.bfn");
if(!err)
{
pFileinfo->length = fat_file_size;
}
}
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,TRUE);
return bResult;
}
BOOL HOOK_DVDOpen(char* szDVDFilePath, DVDFileInfo* pFileinfo)
{
ApiHook(g_nApiAddress[WIIAPIID_DVDOpen],HOOK_DVDOpen,FALSE);
BOOL (*fpDVDOpen)(char* szDVDFilePath, DVDFileInfo* pFileinfo);
fpDVDOpen = g_nApiAddress[WIIAPIID_DVDOpen];
//原过程
BOOL bResult = fpDVDOpen(szDVDFilePath,pFileinfo);
ApiHook(g_nApiAddress[WIIAPIID_DVDOpen],HOOK_DVDOpen,TRUE);
return bResult;
}
BOOL HOOK_DVDReadAsyncPrio( DVDFileInfo* pFileinfo, void* addr, s32 length,s32 offset,DVDCallback callback, s32 prio )
{
//原过程
ApiHook(g_nApiAddress[WIIAPIID_DVDReadAsyncPrio],HOOK_DVDReadAsyncPrio,FALSE);
BOOL (*fpDVDReadAsyncPrio)( DVDFileInfo* pFileinfo, void* addr, s32 length,s32 offset,DVDCallback callback, s32 prio);
fpDVDReadAsyncPrio = g_nApiAddress[WIIAPIID_DVDReadAsyncPrio];
BOOL bResult = fpDVDReadAsyncPrio(pFileinfo,addr,length,offset,callback,prio);
//检索文件
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,FALSE);
BOOL (*fpDVDFastOpen)(int nEntrynum,DVDFileInfo* pFileinfo);
fpDVDFastOpen = g_nApiAddress[WIIAPIID_DVDFastOpen];
int (*fpDVDConvertPathToEntrynum)(const char* fileName) = g_nApiAddress[WIIAPIID_DVDConvertPathToEntrynum];
int nRet = fpDVDConvertPathToEntrynum("font/papermarioset_JPN.bfn");
DVDFileInfo fileinfo;
fpDVDFastOpen(nRet,&fileinfo);
if(fileinfo.startAddr == pFileinfo->startAddr)
{
//找到文件
int err = fat_open("paper.bfn");
if(!err)
{
strcpy(0x80001810,"2");
fat_read(0x80001800,2,8);
fat_read(addr,offset,length);
DCFlushRange(addr,length);
}
}
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,TRUE);
ApiHook(g_nApiAddress[WIIAPIID_DVDReadAsyncPrio],HOOK_DVDReadAsyncPrio,TRUE);
return bResult;
}
int HOOK_DVDReadPrio(DVDFileInfo* pFileinfo,unsigned char* buf,int nLength,int nOffset,int nPrio)
{
//原过程
ApiHook(g_nApiAddress[WIIAPIID_DVDReadPrio],HOOK_DVDReadPrio,FALSE);
int (*fpDVDReadPrio)(DVDFileInfo* pFileinfo,unsigned char* buf,int nLength,int nOffset,int nPrio);
fpDVDReadPrio = g_nApiAddress[WIIAPIID_DVDReadPrio];
int nResult = fpDVDReadPrio(pFileinfo,buf,nLength,nOffset,nPrio);
//检索文件
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,FALSE);
BOOL (*fpDVDFastOpen)(int nEntrynum,DVDFileInfo* pFileinfo);
fpDVDFastOpen = g_nApiAddress[WIIAPIID_DVDFastOpen];
int (*fpDVDConvertPathToEntrynum)(const char* fileName) = g_nApiAddress[WIIAPIID_DVDConvertPathToEntrynum];
int nRet = fpDVDConvertPathToEntrynum("font/papermarioset_JPN.bfn");
DVDFileInfo fileinfo;
fpDVDFastOpen(nRet,&fileinfo);
if(fileinfo.startAddr == pFileinfo->startAddr)
{
//找到文件
int err = fat_open("paper.bfn");
if(!err)
{
strcpy(0x80001810,"1");
fat_read(0x80001800,2,8);
fat_read(buf,nOffset,nLength);
DCFlushRange(buf,nLength);
}
}
ApiHook(g_nApiAddress[WIIAPIID_DVDFastOpen],HOOK_DVDFastOpen,TRUE);
ApiHook(g_nApiAddress[WIIAPIID_DVDReadPrio],HOOK_DVDReadPrio,TRUE);
return nResult;
}
----------------------------------------纸马是老游戏,没有mem2上的执行代码保护,所以这里我干脆就直接用老APIHOOK方案了,程序代码住在mem2上。
虽然没有实现文件访问重定向,但是逻辑是正确的,并且终于在发售游戏内部实时的访问了SD卡的数据,也算个收获。
下一阶段,可能会考虑在发售游戏内部使用socket(说白了就是从libogc源代码里扣程序,以及部分API定位工作),以及在发售游戏内部使用lua脚本进行二次开发之类的,当然也有可能什么都不做,毕竟我很懒的,而且很多年不写程序了现在手很生。
-
编程获得WII硬件的唯一ID
2012-01-29 14:06其实就是获得NANDKEY。
#define KEYSTORE_SIZE 0x40
#define KEYRING_SIZE 0x20
#define KEYSTORE_ADDR 0x936496f4
#define KEYRING_ADDR 0x93649ff4
typedef struct {
u8 in_use;
u16 data[16];
u8 pad[3];
} __attribute__((packed)) keystore_t;
typedef struct {
u8 in_use;
u8 type;
u16 pad0;
u32 permission_mask;
u32 unk0;
u32 keyid;
s16 index;
u16 pad1;
} __attribute__((packed)) keyring_t;
static keystore_t keystore[KEYSTORE_SIZE];
static keyring_t keyring[KEYRING_SIZE];
void *starlet_keystore = (void *) KEYSTORE_ADDR;
void *starlet_keyring = (void *) KEYRING_ADDR;
memcpy(keystore, starlet_keystore, sizeof(keystore));
memcpy(keyring, starlet_keyring, sizeof(keyring));
keystore_t ks = keystore[keyring[2].index];
然后ks.data就是key了,想要编写需要注册码的自制软件就可以用这个做硬件ID了。 -
新的wii api hook方案
2012-01-29 09:26这回可以保证线程安全,从效率上来说也比以前的来回写函数头要高,方案更接近detour的inline hook。
HOOK前保存目标函数的第一条指令,然后重写为bl 0xYYYYYYYY,跳转到stub代码,stub代码为
lis r0,HOOKFun@ha
ori r0,r0,HOOKFun@l
mtctr r0
bctr
然后找一块内存作为原函数的函数头,代码为原目标函数第一条指令加:
lis r0,(ORIFun+4)@ha
ori r0,r0,(ORIFun+4)@l
mtctr r0
bctr
------------------------------------------
发现一些新游戏不允许在(mem2)0x90000000以上位置执行代码了,那么要进行二次开发就势必要使用针对(mem1)0x80000000上面的堆栈移动大法了,而新游戏要修正堆栈看起来也比老游戏要麻烦的多。
那么二次开发的流程就会变为,在原堆栈空间上编译二次开发代码,读取到0x90000000上,将OSInit()的最后一条blr指令修改为跳转执行0x80001800的存根代码,0x80001800处存放将0x90000000上的数据复制到原堆栈空间上的代码。
-
过年好
2012-01-20 20:00不来这里好久了,这两天看看访问记录貌似还有朋友会过来看看。
祝大家春节快乐。
-
WII游戏最后的故事.pk文件解密分析
2012-01-20 19:56工欲善其事,必先利其器,事先准备工具IDAPro,WII模拟器Dolphin,真机调试器usbgecko.
其中静态分析时主要使用IDA,调试时混合使用模拟器和真机调试器,这是因为模拟器可以下多个指令断点但不能下内存断点,而真机调试器只能下一个断点但可以下内存断点。
开始调试前,首先下一份WII的官方开发包,找到里面包含的文档阅读,之后根据静态库里面的.a文件在最后的故事的main.dol里定位几个关键的API:
DVDConvertPathToEntrynum 0x80600B00
DVDFastOpen 0x80600E10
DVDOpen 0x80600E80(后来发现读pk文件没走这个API)
DVDClose 0x80600FA0
DVDReadAsync 0x806010F0
DVDRead 0x806011E0(后来发现读pk文件没走这个API)
然后在s32 DVDConvertPathToEntrynum(const char* fileName);API处下断点,
断在fileName = preload/title00.pk,函数返回后发现文件序号为0xAD
于是在BOOL DVDFastOpen(s32 entrynum, DVDFileInfo* fileInfo);API处下断点,
断在entrynum = 0xAD,fileInfo = 0x8089DD70
但是用IDA查看调用者指令,发现这里打开文件后立刻用DVDClose关闭了。
于是保持断点继续执行,再次断下显示entrynum = 0xAD,fileInfo = 0x80CA5244
IDA查看调用者指令:
.text2:80477D04 bl DVDFastOpen # BOOL DVDFastOpen(s32 entrynum, DVDFileInfo* fileInfo);
.text2:80477D08 stw %r31, 0x40(%r31) # Store Word
.text2:80477D0C lis %r7, loc_804777A8@h # Load Immediate Shifted
.text2:80477D10 addi %r3, %r31, 0x14 # Add Immediate
.text2:80477D14 li %r8, 2 # Load Immediate
.text2:80477D18 lwz %r5, 8(%r31) # Load Word and Zero
.text2:80477D1C addi %r7, %r7, loc_804777A8@l # Add Immediate
.text2:80477D20 lwz %r4, 4(%r31) # Load Word and Zero
.text2:80477D24 addi %r0, %r5, 0x1F # Add Immediate
.text2:80477D28 lwz %r6, 0xC(%r31) # Load Word and Zero
.text2:80477D2C clrrwi %r5, %r0, 5 # Clear Right Immediate
.text2:80477D30 bl DVDReadAsync # BOOL DVDReadAsync(DVDFileInfo* fileInfo, void* addr,s32 length, s32 offset,DVDCallback callback);
.text2:80477D34 cmpwi %r3, 0 # Compare Word Immediate
.text2:80477D38 bne loc_80477D7C # Branch if not equal
.text2:80477D3C lwz %r3, 0(%r30) # Load Word and Zero
.text2:80477D40 li %r4, 4 # Load Immediate
.text2:80477D44 bl sub_8047EC48 # Branch
.text2:80477D48 mr %r3, %r30 # Move Register
.text2:80477D4C bl sub_80086528 # Branch
.text2:80477D50 stw %r28, 0(%r31) # Store Word
.text2:80477D54 addi %r3, %r31, 0x14 # Add Immediate
.text2:80477D58 stw %r28, 4(%r31) # Store Word
.text2:80477D5C bl DVDClose # BOOL DVDClose(DVDFileInfo* fileInfo);说明这里用DVDReadAsync读取了文件,
故而在BOOL DVDReadAsync(DVDFileInfo* fileInfo, void* addr,s32 length, s32 offset,DVDCallback callback);API处下断点
结果为
DVDReadAsync(结构80CA5244,地址912E4860,长度13F420,偏移0,回调804777A8)
于是在0x912E4860处下内存读断点
断在0x8062C0E0处
调用栈为:
80477818
80477e68
80479974
800d6588
800d618c
804857a8
800041b0 <- bl main()IDA观察0x8062C0E0:
.text2:8062C0E0 lbz %r0, 0(%r3) # Load Byte and Zero
在这条指令断下,而这条指令恰好是该函数的第一行指令,再根据POWERPC的ABI,r3是函数的第一个参数,故而确定本函数第一个参数为pk文件中的加密数据。
继续看
.text2:8062C0E0 lbz %r0, 0(%r3) # Load Byte and Zero
.text2:8062C0E4 rlwinm %r0, %r0, 0,24,27 # Rotate Left Word Immediate then AND with Mask
.text2:8062C0E8 cmpwi %r0, 0x30 # Compare Word Immediate
.text2:8062C0EC beq loc_8062C10C # Branch if equal
.text2:8062C0F0 cmpwi %r0, 0x10 # Compare Word Immediate
.text2:8062C0F4 beq loc_8062C110 # Branch if equal
.text2:8062C0F8 cmpwi %r0, 0x20 # Compare Word Immediate
.text2:8062C0FC beq loc_8062C114 # Branch if equal
.text2:8062C100 cmpwi %r0, 0x80 # Compare Word Immediate
.text2:8062C104 beq loc_8062C118 # Branch if equal
.text2:8062C108 blrr3与0xf0做运算后,判断他是0x10 0x20 0x30 0x80中的某一支,否则直接返回,注意返回前没有修改r3寄存器的值,于是确定本函数返回值类型为void.
关 于r4寄存器,可以在函数中看到很多类似stb %r0, 0(%r4) # Store Byte的代码,并且之前r4没有被赋值,于是可以确定本函数第二个参数为输出数据,在8062C0E0处下断点,记录r4的数值,执行完这个函数 后,dump内存观察r4所指向的数据,确定为解密数据。
在本函数的调用者处观察,发现只填充了r3 r4寄存器,说明本函数只有2个参数,于是函数原型可以定义为:
void decode(BYTE* src,BYTE* dest);
在IDA中把WII的汇编代码复制出来,格式化处理,直接写个WII程序把他仍进去,即可在WII端解密,运行效果良好。
最后,还是得抽时间把decode函数还原为高级语言代码,不然X86上的解密程序和加密程序无法实现。
-----------------------------------------
21号更新:
发现找到的这个decode是LZ77的解压函数,有工具可以直接处理,这里就不再跟踪调试了。
-
好爽的一场暴雨
2011-06-23 20:34晚上下班步行回家,水淹到膝盖了。
很刺激,路上很多人在拍摄视频。
-
惊愕读完
2011-06-05 11:22还不错,期待动画化,分裂和惊愕构成的这个故事素质和凉宫春日的消失水平接近,个人认为。
-
今天过的很充实
2011-06-04 20:43去willreno北京的房子参观,玩了会PS3,看他弹了弹钢琴,一起看了几集爱情公寓,笑的前仰后合的。
路上地铁里一直在读 凉宫春日的惊愕,这次入手晚了点,实在对不起凉宫厨的称号。
-
从资源加载GDI+的图像对象
2011-06-03 14:29BOOL ImageFromIDResource(UINT nID, LPCTSTR sTR,Gdiplus::Image * &pImg)
{
HRSRC hRsrc = ::FindResource (NULL,MAKEINTRESOURCE(nID),sTR); // type
if (!hRsrc)
return false;
// load resource into memory
HGLOBAL hRes=::LoadResource(NULL,hRsrc);
if (!hRes!=NULL)
return FALSE;
LPVOID pTheImage=::LockResource(hRes);
if (pTheImage!=NULL)
{
DWORD dwTheImage=::SizeofResource(NULL,hRsrc);
ULONG uWrite;
CComPtr <IStream> pStream; //com
CreateStreamOnHGlobal(NULL,TRUE,&pStream);
pStream->Write(pTheImage,dwTheImage,&uWrite);
pImg=Gdiplus::Image::FromStream(pStream);
pStream.Release();
UnlockResource(hRes);
}
FreeResource(hRes);
return true;
} -
状态继续恢复中
2011-05-25 16:12GDI+缩放显示图片时可以反失真处理。
MFC用static显示图片的方法,先设置属性nofife为true simple(好像需要)为true,然后从CStatic派生一个类出生,相应WM_PAINT消息,在OnPaint函数里绘制图片即可。
-
状态又恢复了一点
2011-05-23 14:46一年不工作没自信了,不过今天给了1周的活,1天就做完了,找到些信心了。
从内存数据生成CImage对象的方法:
void LoadMemImage(void* pMemData,long len)
{
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, len);
void * pData = GlobalLock(hGlobal);
memcpy(pData, pMemData, len);
GlobalUnlock(hGlobal);
IStream * pStream = NULL;
if (CreateStreamOnHGlobal(hGlobal,TRUE,&pStream) == S_OK)
{
CImage image;
if (SUCCEEDED(image.Load(pStream)))
{
}
pStream -> Release();
}
GlobalFree(hGlobal);
}VARIANT DATE类型转BSTR
if(S_OK == VariantChangeType(&TheValue, &TheValue, 0, VT_BSTR))
{
_bstr_t strTime = (_bstr_t(TheValue.bstrVal));}
得到BLOB类型的数据
long lDataSize = m_pRecordset->GetFields()->GetItem("X")->ActualSize;///得到数据的长度
if(lDataSize > 0)
{
_variant_t varBLOB;
varBLOB = m_pRecordset->GetFields()->GetItem("X")->GetChunk(lDataSize);
if(varBLOB.vt == (VT_ARRAY | VT_UI1)) ///判断数据类型是否正确
{
char *pBuf = NULL;
SafeArrayAccessData(varBLOB.parray,(void **)&pBuf); ///得到指向数据的指针
//干活
SafeArrayUnaccessData (varBLOB.parray);
}
} -
VC++监听socket和获得本机IP
2011-05-21 09:04监听的代码:
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
recv
closesocket(sockConn);
}
获得本机IP:char *ptrip=NULL;
char name[32]="";
PHOSTENT hostinfo;
if(gethostname (name, sizeof(name)) == 0)
{
//如果成功 将本地主机名存放入name缓冲区
if((hostinfo = gethostbyname(name)) != NULL)
{
//获取主机IP
ptrip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
}
} -
VC++调用webService
2011-05-20 13:49头天上班,而且是很久以来的第一次上班,都不会写代码了。
让我研究VC++如何调用webService,一头雾水,因为都不知道这是个什么东西。
研究了会成功访问了一个公开的webService,感觉找到了一些上班的感觉,记录下来。
POST /TranslateService.asmx HTTP/1.1
Host: www.webservicex.net
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://www.webservicex.net/Translate"
EnglishTOChinese or EnglishTOFrench or EnglishTOGerman or EnglishTOItalian or EnglishTOJapanese or EnglishTOKorean or EnglishTOPortuguese or EnglishTOSpanish or ChineseTOEnglish or FrenchTOEnglish or FrenchTOGerman or GermanTOEnglish or GermanTOFrench or ItalianTOEnglish or JapaneseTOEnglish or KoreanTOEnglish or PortugueseTOEnglish or RussianTOEnglish or SpanishTOEnglish or SpanishToFrench or PortugueseToFrench or ItalianToFrench or GreekToFrench or GermanToFrench or FrenchToGreek or FrenchToItalian or FrenchToPortuguese or FrenchToDutch or FrenchToSpanish or EnglishToRussian or EnglishToDutch or DutchToEnglish or DutchToFrench or GreekToEnglish
string
以这个报文为例,访问的代码是:
HRESULT hr;
try
{
//创建SoapConnector类的对象
SoapConnector.CreateInstance(__uuidof(HttpConnector30));
//指定Web服务的地址
SoapConnector-> Property [ "EndPointURL"] =(LPSTR)(LPCTSTR)WebUrl;
//与Web服务连接
hr=SoapConnector-> Connect();
if(FAILED(hr)) return " ";
//指定Web服务完成的操作
SoapConnector-> Property [ "SoapAction"] = _T("http://www.webservicex.net/Translate");
//准备发送消息给Web服务
SoapConnector-> BeginMessage();
// 创建SoapSerializer对象
Serializer.CreateInstance(__uuidof(SoapSerializer30));
// 将serializer连接到connector的输入字符串
Serializer-> Init(_variant_t((IUnknown*)SoapConnector-> InputStream));
// 创建SOAP消息
Serializer-> StartEnvelope( "soap","http://www.w3.org/2001/XMLSchema-instance","");
Serializer-> StartBody( "");
Serializer-> StartElement( "Translate", "http://www.webservicex.net", "", "soap");
Serializer-> StartElement( "LanguageMode", "", "", "soap");
Serializer-> WriteString((_bstr_t)(LPCTSTR)"EnglishTOChinese");
Serializer-> EndElement();
Serializer-> StartElement( "Text", "", "", "soap");
Serializer-> WriteString((_bstr_t)(LPCTSTR)"hello");
Serializer-> EndElement();
Serializer-> EndElement();
Serializer-> EndBody();
Serializer-> EndEnvelope();
//消息真正地发给Web服务
hr=SoapConnector-> EndMessage();
if(FAILED(hr)) return "Fail";
// 读取响应
Reader.CreateInstance(__uuidof(SoapReader30));
// 将reader联接到connector的输出字符串
Reader-> Load(_variant_t((IUnknown*)SoapConnector-> OutputStream), _T( " "));
return CString((const char *)Reader-> RpcResult-> text);
}
catch (_com_error e)
{
return (CString)(char*)e.Description();
}
遇到别的webService,对着改即可。
//#import "D:\WINDOWS\system32\msxml4.dll"
//
//#import "D:\Program Files\Common Files\MSSoap\Binaries\MSSOAP30.dll" \
// exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", \
// "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME","IErrorInfo")
//using namespace MSXML2;
//using namespace MSSOAPLib30;
-----------
然而到了下午真的要访问的时候,发现给我的并非SOAP协议报文。
而是一个wsdl,于是用命令行sproxy.exe /wsdl /out:1.h wsdl地址
的方法生成 .h文件,就可以调用了
-
当时估计就差这一步
2011-01-15 14:17NDS官方SDK游戏读取文件流程可以是:
FS_Init()
FS_InitFile()
FS_OpenFile()
FS_ReadFile()
FS_CloseFile()
此前我把第2个调用忽略了,导致一年前给一位破解高手出主意用API动态读数据不成功。(只能读第一次成功)
这些调用如果稳定的话总比自己操作BIOS要方便,可惜。
------------
某个DS游戏用了一堆pak文件,想看看是怎么回事,于是对FS_OpenFile下断,断在了第一个断点函数内部,这个函数的原型大概是 BYTE* LoadFile(char* szRomPath).
这类文件都有三个段落,正好对应这里的后三个断点函数,查文档没看到G2d类函数,可能是sdk4.0版本加入的。
算法就不跟踪了,只要找到开发包,基本上这类函数的源代码都在里。------------
关于用C在NDS上做二次开发也很简单,批处理文件如下:
C:\devkitPro\devkitARM\bin\arm-eabi-gcc.exe -std=c99 -w -nostartfiles -O3 -nodefaultlibs -Wl,-Ttext,0x215AF40 -o main.elf main.c
C:\devkitPro\devkitARM\bin\arm-eabi-strip.exe --strip-debug --strip-all --discard-all -o none.elf main.elf
C:\devkitPro\devkitARM\bin\arm-eabi-objcopy.exe -O binary none.elf codehandler.bin
pause因为1号堆总是从0x215AF4开始所以设置text节到那里,若有别的情况可以更换地址。
最后生成的codehandler.bin用CT2直接打入到text节的位置即可,扣除内存点以BSS段为标准。用C二次开发代码如(比汇编容易多了,file结构懒得数多大,直接给他100字节):
unsigned char buffer[32];
void start()
{
unsigned char file[100];
void (*FS_InitFile)(void* file) = 0x2009FEC;
int (*FS_OpenFile)(void* file,const char *path) = 0x200A2CC;
int (*FS_ReadFile)(void* file, void* dst, int len) = 0x200A468;
int (*FS_CloseFile)(void* file) = 0x200A314;
FS_InitFile(file);
if(FS_OpenFile(file,"List/tNpcName.dat"))
{
FS_ReadFile(file,buffer,32);
FS_CloseFile(file);
}
}地址是用CT2扫出来的。找个bx r14的地方改为b 0x215AF40,运行正常,把List/tNpcName.dat的数据读到了buffer里。
api HOOK方案就先不写了,用处是有,但是懒的弄了,如果整套方案完成,像给游戏添加触摸操作之类的小HACK将很容易的做出来,网上某位高手的反汇编过程未免太折磨人了。
16号更新,发现API Pad_Read()扫不出来,原来是被可耻的内联了:
static inline u16 PAD_Read(void)
{
return (u16)(((reg_PAD_KEYINPUT | *(vu16 *)HW_BUTTON_XY_BUF) ^
(PAD_PLUS_KEY_MASK | PAD_BUTTON_MASK)) & (PAD_PLUS_KEY_MASK | PAD_BUTTON_MASK));
}获得触摸数据的TP_GetCalibratedPoint是正常函数
Code Rush 鱼跃此时海

