LPVOID engine_HookFunctionInProcess(HANDLE hProcess,LPSTR lpModuleName,LPSTR lpFunctionName,LPVOID lpHookFunctionAddress,PDWORD pdwHookFunctionSize,LPVOID* lpFunctionAddress,INT iRndJmp) { LPVOID lpModule=NULL; LPVOID lpFunction=NULL; MEMORY_BASIC_INFORMATION mbi; CHAR lpTmpFunction[MAX_FUNC_LEN*2]; CHAR lpLocalStub[MAX_FUNC_LEN*3]; CHAR lpLocalFunc[MAX_FUNC_LEN*3]; DWORD dwBytesRead; DWORD dwReadLen=0; DWORD dwExistingJMP=0; DWORD dwStubSize; DWORD dwFree=0; DWORD dwBytesWritten; DWORD dwOldProtect; LPVOID lpRemoteStub=NULL; INT iFuncLen; PBYTE pReadAddress; NTSTATUS ntStatus; // Get module address lpModule=(LPVOID)engine_GetRemoteModuleHandle(hProcess,lpModuleName); if (!lpModule) return NULL; // Get function address lpFunction=engine_GetRemoteProcAddress(hProcess,lpModule,lpFunctionName); if (!lpFunction) return NULL; // Get info about the function address if (!NT_SUCCESS(SafeNtQueryVirtualMemory(hProcess,lpFunction,MemoryBasicInformation,&mbi,sizeof(mbi),NULL))) return NULL; // Flush instruction cache NtFlushInstructionCache(hProcess,mbi.BaseAddress,mbi.RegionSize); // Change the protection for the region if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,PAGE_EXECUTE_READWRITE,&mbi.Protect))) return NULL; // Fill stub buffer with nops RtlFillMemory(lpLocalStub,MAX_FUNC_LEN*3,NOP); // Read MAX_FUNC_LEN instruction(s) from the function into our function buffer if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpFunction,lpTmpFunction,MAX_FUNC_LEN*2,&dwBytesRead))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } pReadAddress=(PBYTE)lpTmpFunction; // check if first opcode in the function is a another jump if (*pReadAddress==LONG_JMP_OPCODE) { // get relative address memcpy(&dwExistingJMP,pReadAddress+1,4); // get absolute address dwExistingJMP=(DWORD)lpFunction+dwExistingJMP; // readlen dwReadLen=jtJmpTable[RELATIVE_JMP].iCodeSize engine_BuildJMPBuffer((CHAR*)lpLocalStub,((DWORD)lpRemoteStub+dwReadLen)-dwExistingJMP,RELATIVE_JMP); NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Get the length of the first instruction(s) // This part is done by Z0MBiE's LDE32 v1.05 iFuncLen=disasm_main(pReadAddress); // get first instruction length while (iFuncLen!=-1 && dwReadLen<(DWORD)jtJmpTable[iRndJmp].iCodeSize) { dwReadLen+=iFuncLen; pReadAddress+=iFuncLen; iFuncLen=disasm_main(pReadAddress); // next instruction length } // API code is too short or too long too hook this way (for now ;)) if (dwReadLen<(DWORD)jtJmpTable[iRndJmp].iCodeSize||dwReadLen>MAX_FUNC_LEN*2) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Read the first instruction(s) from the function into our stub buffer if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpFunction,lpLocalStub,dwReadLen,&dwBytesRead))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Allocate space with read/write access for our "stub" // note: always use a relative jump for our stub -> RELATIVE_JMP dwStubSize=dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize; if (!NT_SUCCESS(NtAllocateVirtualMemory(hProcess,&lpRemoteStub,0,&dwStubSize,MEM_COMMIT|MEM_TOP_DOWN,PAGE_READWRITE))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Check if (dwStubSize<dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } engine_BuildJMPBuffer((CHAR*)lpLocalStub+dwReadLen,jtJmpTable[RELATIVE_JMP].jcStub((DWORD)lpFunction+dwReadLen,(DWORD)lpRemoteStub,dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize),RELATIVE_JMP); // Copy the "stub" buffer to process memory if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpRemoteStub,lpLocalStub,dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize,&dwBytesWritten))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Check if (dwBytesWritten<dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // change access if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&lpRemoteStub,&dwStubSize,PAGE_EXECUTE_READ,&dwOldProtect))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Fill it with NOP RtlFillMemory(lpLocalFunc,MAX_FUNC_LEN*3,NOP); // Prepare jmpcode engine_BuildJMPBuffer((CHAR*)lpLocalFunc,jtJmpTable[iRndJmp].jcFunc((DWORD)lpHookFunctionAddress,(DWORD)lpFunction,(DWORD)jtJmpTable[iRndJmp].iCodeSize),iRndJmp); ntStatus=NtWriteVirtualMemory(hProcess,lpFunction,lpLocalFunc,dwReadLen,&dwBytesWritten); // Check that we really wrote our jmpcode completely if (!NT_SUCCESS(ntStatus) || dwBytesWritten!=dwReadLen) { // Try to fix stuff if (dwBytesWritten) NtWriteVirtualMemory(hProcess,lpFunction,lpRemoteStub,dwBytesWritten,&dwBytesWritten); NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Save size of read function length if (pdwHookFunctionSize) *pdwHookFunctionSize=dwReadLen; // Save address of function if (lpFunctionAddress) *lpFunctionAddress=lpFunction; return lpRemoteStub; }
int main(int argc, char *argv[]) { if (argc < 2) { std::clog << "usage: " << (argc > 0 ? argv[0] : "btctool") << " <function> [<args>]\n" << encode16_usage << '\n' << decode16_usage << '\n' << encode64_usage << '\n' << decode64_usage << '\n' << encode58_usage << '\n' << decode58_usage << '\n' << disasm_usage << '\n' << sha256_usage << '\n' << rmd160_usage << '\n' << privkey_usage << '\n' << pubkey_usage << '\n' << address_usage << '\n' << encrypt_usage << '\n' << decrypt_usage << '\n' << privkeys_usage << '\n' << pubkeys_usage << '\n' << addresses_usage << std::endl; return -1; } --argc, ++argv; const char *f = argv[0]; switch (f[0] | 0x20) { case '-': // - if (std::strcmp(f, "--version") == 0) { // --version std::clog << VERSION << std::endl; return 0; } break; case 'a': // a if ((f[1] | 0x20) == 'd' && (f[2] | 0x20) == 'd' && (f[3] | 0x20) == 'r' && (f[4] | 0x20) == 'e' && (f[5] | 0x20) == 's' && (f[6] | 0x20) == 's') { // address if (f[7] == '\0') { // address return address_main(argc, argv); } if ((f[7] | 0x20) == 'e' && (f[8] | 0x20) == 's' && f[9] == '\0') { // addresses return addresses_main(argc, argv); } } break; case 'd': // d switch (f[1] | 0x20) { case 'e': // de if ((f[2] | 0x20) == 'c') { // dec switch (f[3] | 0x20) { case 'o': // deco if ((f[4] | 0x20) == 'd' && (f[5] | 0x20) == 'e') { // decode switch (f[6]) { case '1': // decode1 if (f[7] == '6' && f[8] == '\0') { // decode16 return decode16_main(argc, argv); } break; case '5': // decode5 if (f[7] == '8' && f[8] == '\0') { // decode58 return decode58_main(argc, argv); } break; case '6': // decode6 if (f[7] == '4' && f[8] == '\0') { // decode64 return decode64_main(argc, argv); } break; } } break; case 'r': // decr if ((f[4] | 0x20) == 'y' && (f[5] | 0x20) == 'p' && (f[6] | 0x20) == 't' && f[7] == '\0') { // decrypt return decrypt_main(argc, argv); } break; } } break; case 'i': // di if ((f[2] | 0x20) == 's' && (f[3] | 0x20) == 'a' && (f[4] | 0x20) == 's' && (f[5] | 0x20) == 'm' && f[6] == '\0') { // disasm return disasm_main(argc, argv); } break; } break; case 'e': // e if ((f[1] | 0x20) == 'n' && (f[2] | 0x20) == 'c') { // enc switch (f[3] | 0x20) { case 'o': // enco if ((f[4] | 0x20) == 'd' && (f[5] | 0x20) == 'e') { // encode switch (f[6]) { case '1': // encode1 if (f[7] == '6' && f[8] == '\0') { // encode16 return encode16_main(argc, argv); } break; case '5': // encode5 if (f[7] == '8' && f[8] == '\0') { // encode58 return encode58_main(argc, argv); } break; case '6': // encode6 if (f[7] == '4' && f[8] == '\0') { // encode64 return encode64_main(argc, argv); } break; } } break; case 'r': // encr if ((f[4] | 0x20) == 'y' && (f[5] | 0x20) == 'p' && (f[6] | 0x20) == 't' && f[7] == '\0') { // encrypt return encrypt_main(argc, argv); } break; } } break; case 'p': // p switch (f[1] | 0x20) { case 'r': // pr if ((f[2] | 0x20) == 'i' && (f[3] | 0x20) == 'v' && (f[4] | 0x20) == 'k' && (f[5] | 0x20) == 'e' && (f[6] | 0x20) == 'y') { // privkey if (f[7] == '\0') { // privkey return privkey_main(argc, argv); } if ((f[7] | 0x20) == 's' && f[8] == '\0') { // privkeys return privkeys_main(argc, argv); } } break; case 'u': // pu if ((f[2] | 0x20) == 'b' && (f[3] | 0x20) == 'k' && (f[4] | 0x20) == 'e' && (f[5] | 0x20) == 'y') { // pubkey if (f[6] == '\0') { // pubkey return pubkey_main(argc, argv); } if ((f[6] | 0x20) == 's' && f[7] == '\0') { // pubkeys return pubkeys_main(argc, argv); } } break; } break; case 'r': // r if ((f[1] | 0x20) == 'm' && (f[2] | 0x20) == 'd' && f[3] == '1' && f[4] == '6' && f[5] == '0' && f[6] == '\0') { // rmd160 return rmd160_main(argc, argv); } break; case 's': // s if ((f[1] | 0x20) == 'h' && (f[2] | 0x20) == 'a' && f[3] == '2' && f[4] == '5' && f[5] == '6' && f[6] == '\0') { // sha256 return sha256_main(argc, argv); } break; } std::clog << "unknown function: " << f << std::endl; return -1; }
DWORD __cdecl disasm(DWORD,const BYTE* x) { return disasm_main(x); }