| |||||||||||
IntroductionMost of us know about reflection (Example: Windows COM, .NET) and its flexibilities. Have you ever wondered how to accomplish reflection in Win32 DLL? Here is the funny tool that resulted from that kind of my thought. "Uh!.. Reflection in Win32 DLL", oops!.. don't believe 100%. Don't worry, you can't accomplish perfect reflection in WIn32 DLLs in a simple way. In perfect reflection, you don't need the knowledge of source, function, parameters & types until its usage. When considering Win32 DLL, I feel late binding is some sort of reflection (don't accuse me, just an opinion). Late bound DLLs can do without the path name, function name until its usage, but need the function argument type list to compile. It seems late binding is impossible when you don't know the parameter list & type until execution. This article is just a crazy demonstration of how you can possibly load and execute any function in a Win32 DLL without compiling the tool for the particular DLL. Here in this section, I'm directly jumping to detail explanation assuming you have sufficient knowledge about DLLs, late binding, etc. Idea behind thisTo accomplish what this tool should do on run time ...
That's it. Last three steps are the exceptional procedures in this tool from normal late binding code. So I'll explain those in detail. As I stated earlier, every parameter to a function in a DLL is passed through stack. Compiler pushes the parameters in to the stack whenever you compile an application to call a function with Stack should be 32 bit aligned, i.e., align to Code detailThe above logic is explained in this below code. This code performs the core functionality of this tool. ![]() void CExecDLLDlg::OnBtnExec() { // this list control has the user input of parameter types & values CListCtrl *pcList = (CListCtrl*)GetDlgItem(IDC_LSTPARAM); char szText1[MAXCHAR_TEXT]; char szText2[MAXCHAR_TEXT]; int nCount = pcList->GetItemCount(); int nArrayCount = nCount; struct ST_PARAM { int nType; // if =0 then its DWORD // if =1 ...... DWORD dwValue; } *pArray = new ST_PARAM[nArrayCount]; DWORD dwDWord; DWORD *pdwDWord; char *lpString; char **lppString; / go through the list and collect / /// the arguments in a structure array for(int i=0,j=0;(i<nCount)&&(j<nArrayCount);i++,j++) { pcList->GetItemText(i,0,szText1,MAXCHAR_TEXT); pcList->GetItemText(i,1,szText2,MAXCHAR_TEXT); if(!strcmp(szText1,"Int")) { pArray[j].nType = 0; dwDWord = atol(szText2); pArray[j].dwValue = dwDWord; } else if(!strcmp(szText1,"Short")) { pArray[j].nType = 1; dwDWord = atoi(szText2); pArray[j].dwValue = dwDWord; } else if(!strcmp(szText1,"Byte")) { pArray[j].nType = 2; dwDWord = atoi(szText2); pArray[j].dwValue = dwDWord; } else if((!strcmp(szText1,"Int*"))|| (!strcmp(szText1,"Short*"))|| (!strcmp(szText1,"Byte*"))) { pArray[j].nType = 3; pdwDWord = new DWORD; *pdwDWord = atol(szText2); pArray[j].dwValue = (DWORD)pdwDWord; } else if(!strcmp(szText1,"String")) { pArray[j].nType = 4; lpString = new char[strlen(szText2)+1]; strcpy(lpString,szText2); pArray[j].dwValue = (DWORD)lpString; } else if(!strcmp(szText1,"String*")) // not supported (not tested :D) { pArray[j].nType = 5; lppString = new char*[1]; lpString = new char[strlen(szText2)+1]; strcpy(lpString,szText2); lppString[1] = lpString; pArray[j].dwValue = (DWORD)lppString; } else if(!strcmp(szText1,"Struct*")) {// here already aligned memory address (using CStructDlg::OnOK() function ) //will be in current list item. So get it and put it pArray[j].nType = 6; CMemInfo *pcMemInfo; pcMemInfo = (CMemInfo*)atol(szText2); pArray[j].dwValue = pcMemInfo->m_dwMemAddress; } else if(!strcmp(szText1,"Array*")) {// here already aligned memory address (using CArrayDlg::OnOK() function ) //will be in this list item. So get it and put it pArray[j].nType = 7; CMemInfo *pcMemInfo; pcMemInfo = (CMemInfo *)atol(szText2); pArray[j].dwValue = pcMemInfo->m_dwMemAddress; } else if(!strcmp(szText1,"Struct")) {// here already aligned memory address (using CStructDlg::OnOK() function ) //will be in this list item. So get it and put it CMemInfo *pcMemInfo; pcMemInfo = (CMemInfo *)atol(szText2); DWORD dwLen = pcMemInfo->m_dwMemLen; DWORD nMemCount = (dwLen/4); DWORD nCurCnt = i; nArrayCount += nMemCount; nArrayCount--; if(dwLen%4) { delete pArray; MessageBox("Memory is not aligned", "Execute DLL", MB_OK|MB_ICONERROR); return; } pArray = (ST_PARAM*)realloc(pArray, sizeof(ST_PARAM)*nArrayCount); pdwDWord = (DWORD*)pcMemInfo->m_dwMemAddress; for(;j<nCurCnt+nMemCount;j++) { pArray[j].nType = 8; pArray[j].dwValue = *pdwDWord; pdwDWord++; } j--; } } / load the DLL // typedef int (__stdcall *FUNCTION)(void); DLL path GetDlgItemText(IDC_EDTFILEPATH,szText1 ,MAXCHAR_TEXT); Function name (Obtained through enumeration using Dbghelp API) GetDlgItemText(IDC_CMBFUNCNAME,szText2 ,MAXCHAR_TEXT); HMODULE hMod = LoadLibrary(szText1); if(!hMod) { MessageBox("DLL not found","Execute DLL",MB_OK|MB_ICONERROR); return; } get the function pointer // FUNCTION proc = GetProcAddress(hMod,szText2); if(!proc) { MessageBox("Function not found","Execute DLL",MB_OK|MB_ICONERROR); return; } /// one by one push the arguments // for(i=nArrayCount-1;i>=0;i--) // args should be pushed in reverse order { dwDWord = pArray[i].dwValue; _asm { mov eax, dwDWord push eax } } /// call the function and store the return value / _asm { call proc mov dwDWord, eax } // convert and display the return value /// GetDlgItemText(IDC_CMBRETTYPE,szText1,MAXCHAR_TEXT); if(!strcmp(szText1,"Void")) { // do nothing strcpy(szText2,""); } else if((!strcmp(szText1,"Int")) ||(!strcmp(szText1,"Short")) ||(!strcmp(szText1,"Byte"))) { sprintf(szText2,"%u",dwDWord); } else if(!strcmp(szText1,"Int*")) { sprintf(szText2,"%u",*((int*)dwDWord)); } else if(!strcmp(szText1,"Short*")) { sprintf(szText2,"%u",*((SHORT*)dwDWord)); } else if(!strcmp(szText1,"Byte*")) { sprintf(szText2,"%u",*((BYTE*)dwDWord)); } else if(!strcmp(szText1,"String")) { sprintf(szText2,"%s",(char*)dwDWord); } else if(!strcmp(szText1,"String*"))// at this moment not supported { sprintf(szText2,"%s",(char*)dwDWord); } SetDlgItemText(IDC_EDTRETVALUE,szText2); FreeLibrary(hMod); clean up the allocated aruments // /// oops !..... delete pArray; } Secondary part of the code includes aligning user defined (structure) type data and array type data. void CStructDlg::OnOK() { .. .... align user input structure pass the aligned mem address .... ,, } void CArrayDlg::OnOK() { .. .... align user input array pass the aligned mem address .... ,, } In addition to this, you may find bool GetDLLFileExports (char *szFileName, unsigned int *nNoOfExports, char **&pszFunctions) { .... } Other codes are kind of aesthetic stuff. Like getting the input from user, presenting to user, etc. Sample testsThese zips contain a tool project and a sample DLL project to test this tool. Use TestDLL if you are squeamish to use some other DLL found in your PC. Else, just ignore this TestDLL and use Windows system DLLs. I'm not encouraging or discouraging you to use these DLLs, it's up to you. Example 1: Take USER32.DLL found in System32 directory. This DLL has most of the GUI APIs. Select the DLL and then the function Example 2: Bit more complicated example. On the USER32.DLL, select the function Specifying any incorrect parameter type for a function may result in tool crash. For example, you can make the tool crash on the second example if you specify "Struct" instead of "Struct*" or vice versa. Known issues & lacking features
Planned features
Conclusion |