CreateProcess لنظام Android ADB Sererver؟
-
30-09-2019 - |
سؤال
عندما أستخدم CreateProcess لإنشاء عملية adb.exe ، سيتم حظره في Readfile.
void KillAdbProcess()
{
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
return;
cProcesses = cbNeeded / sizeof(DWORD);
for ( i = 0; i < cProcesses; i++ )
if( aProcesses[i] != 0 ){
bool shouldKill =false;
wchar_t szProcessName[MAX_PATH] = L"<unknown>";
//Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ | PROCESS_TERMINATE,
FALSE, aProcesses[i] );
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod),
&cbNeeded) )
{
GetModuleFileNameExW( hProcess, hMod, szProcessName,
sizeof(szProcessName)/sizeof(TCHAR));
int len = wcslen(szProcessName);
if(!wcscmp(L"\\adb.exe",szProcessName+len-8)){
shouldKill = true;
}
}
}
if(shouldKill) TerminateProcess(hProcess,0);
CloseHandle( hProcess );
}
}
int testadb(){
KillAdbProcess();
char buff[4096] = {0};
int len = sizeof(buff);
DWORD exitCode = 0;
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(sa));
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(sa);
HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
// Create the child output pipe.
if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
return false;
// Create new output read handle and the input write handles. Set
// the Properties to FALSE. Otherwise, the child inherits the
// properties and, as a result, non-closeable handles to the pipes
// are created.
if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
GetCurrentProcess(),
&hOutputRead, // Address of new handle.
0,FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
return false;
// Close inheritable copies of the handles you do not want to be
// inherited.
if (!CloseHandle(hOutputReadTmp)) return false;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
STARTUPINFOW si;
GetStartupInfoW(&si);
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = NULL;
if(buff) {
si.hStdOutput = hOutputWrite;
si.hStdError = hOutputWrite;
} else {
si.hStdOutput = NULL;
si.hStdError = NULL;
}
wchar_t cmdBuf[512] = L"adb.exe start-server";
if( !::CreateProcessW(NULL, cmdBuf, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi) )
{
exitCode = -1;
goto exit;
}
::CloseHandle(hOutputWrite);
hOutputWrite = NULL;
len--; //keep it for string end char.
DWORD dwBytes = 0;
DWORD dwHasRead = 0;
while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL))
{
printf("read byte=%d\n",dwBytes);
if(0 == dwBytes) break;
dwHasRead += dwBytes;
//GetExitCodeProcess(pi.hProcess, &exitCode);
//if(STILL_ACTIVE != exitCode) break;
if(dwHasRead >= len) break;
}
buff[dwHasRead] = 0;
::GetExitCodeProcess(pi.hProcess, &exitCode);
exit:
if(hOutputRead) ::CloseHandle(hOutputRead);
if(hOutputWrite) ::CloseHandle(hOutputWrite);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
return 0;
}
إذا قمت بتغيير الرمز إلى
while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL))
{
printf("read byte=%d\n",dwBytes);
if(0 == dwBytes) break;
dwHasRead += dwBytes;
GetExitCodeProcess(pi.hProcess, &exitCode);
if(STILL_ACTIVE != exitCode) break;
if(dwHasRead >= len) break;
}
إنه يعمل ، ولكن عندما أحذف رمز printf ، سيتم حظره مرة أخرى.
while(::ReadFile(hOutputRead, buff+dwHasRead, len-dwHasRead, &dwBytes, NULL))
{
if(0 == dwBytes) break;
dwHasRead += dwBytes;
GetExitCodeProcess(pi.hProcess, &exitCode);
if(STILL_ACTIVE != exitCode) break;
if(dwHasRead >= len) break;
}
في مدونة ADB.exe ، أرى بعض التعليمات البرمجية مثل Belows:
#if ADB_HOST
int launch_server()
{
#ifdef HAVE_WIN32_PROC
/* we need to start the server in the background */
/* we create a PIPE that will be used to wait for the server's "OK" */
/* message since the pipe handles must be inheritable, we use a */
/* security attribute */
HANDLE pipe_read, pipe_write;
SECURITY_ATTRIBUTES sa;
STARTUPINFO startup;
PROCESS_INFORMATION pinfo;
char program_path[ MAX_PATH ];
int ret;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
/* create pipe, and ensure its read handle isn't inheritable */
ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
if (!ret) {
fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
return -1;
}
SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
ZeroMemory( &startup, sizeof(startup) );
startup.cb = sizeof(startup);
startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
startup.hStdOutput = pipe_write;
startup.hStdError = GetStdHandle( STD_ERROR_HANDLE );
startup.dwFlags = STARTF_USESTDHANDLES;
ZeroMemory( &pinfo, sizeof(pinfo) );
/* get path of current program */
GetModuleFileName( NULL, program_path, sizeof(program_path) );
ret = CreateProcess(
program_path, /* program path */
"adb fork-server server",
/* the fork-server argument will set the
debug = 2 in the child */
NULL, /* process handle is not inheritable */
NULL, /* thread handle is not inheritable */
TRUE, /* yes, inherit some handles */
DETACHED_PROCESS, /* the new process doesn't have a console */
NULL, /* use parent's environment block */
NULL, /* use parent's starting directory */
&startup, /* startup info, i.e. std handles */
&pinfo );
CloseHandle( pipe_write );
if (!ret) {
fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
CloseHandle( pipe_read );
return -1;
}
CloseHandle( pinfo.hProcess );
CloseHandle( pinfo.hThread );
/* wait for the "OK\n" message */
{
char temp[3];
DWORD count;
ret = ReadFile( pipe_read, temp, 3, &count, NULL );
CloseHandle( pipe_read );
if ( !ret ) {
fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
return -1;
}
if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
}
#elif defined(HAVE_FORKEXEC)
char path[PATH_MAX];
int fd[2];
// set up a pipe so the child can tell us when it is ready.
// fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
if (pipe(fd)) {
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
return -1;
}
get_my_path(path);
pid_t pid = fork();
if(pid < 0) return -1;
if (pid == 0) {
// child side of the fork
// redirect stderr to the pipe
// we use stderr instead of stdout due to stdout's buffering behavior.
adb_close(fd[0]);
dup2(fd[1], STDERR_FILENO);
adb_close(fd[1]);
// child process
int result = execl(path, "adb", "fork-server", "server", NULL);
// this should not return
fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
} else {
// parent side of the fork
char temp[3];
temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
// wait for the "OK\n" message
adb_close(fd[1]);
int ret = adb_read(fd[0], temp, 3);
adb_close(fd[0]);
if (ret < 0) {
fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno);
return -1;
}
if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
setsid();
}
#else
#error "cannot implement background server start on this platform"
#endif
return 0;
}
#endif
أعتقد أن عملية الطفل من adb.exe ترث مقبض adb.exe ، إذا لم تخرج عملية الطفل من adb.exe ، فسيحظر Readfile إلى الأبد. ولكن عندما أقوم بإعداد "adb.exe reerver server" في الأمر ، كل شيء على ما يرام. فكيف يقوم أمر Windows Constair CreateProcess و Readfile؟
المحلول
أنا وجدت الإجابة: إعادة توجيه إدخال/إخراج وحدة التحكم التعسفية - codeproject.
تقنية إعادة توجيه إدخال/إخراج عملية وحدة التحكم هي عينة للغاية: تتيح لنا API CreateProcess () من خلال بنية StartupInfo إعادة توجيه المقابض القياسية لعملية تحكم الطفل. حتى نتمكن من تعيين هذه المقابض على مقبض الأنابيب أو مقبض الملفات أو أي مقبض يمكننا قراءته والكتابة. تم وصف تفاصيل هذه التقنية بوضوح في MSDN: Howto: عمليات التحكم في وحدة التحكم مع مقابض قياسية تم إعادة توجيهها.
ومع ذلك ، فإن رمز نموذج MSDN لديه مشكلة كبيرة. أولاً ، يفترض أن عملية الطفل سترسل الإخراج في البداية ، ثم انتظر الإدخال ، ثم قم بتدوير المخزن المؤقت للإخراج والخروج. إذا لم تتصرف عملية الطفل هكذا ، فسيتم تعليق عملية الوالدين. والسبب في ذلك هو أن وظيفة ReadFile () تظل محظورة حتى ترسل عملية الطفل بعض الإخراج أو الخروج.
ثانياً ، تواجه مشكلة في إعادة توجيه وحدة تحكم 16 بت (بما في ذلك تطبيقات MS-DOS المستندة إلى وحدة التحكم.) على Windows 9x ، لا تزال القراءة محظورة حتى بعد انتهاء عملية الطفل ؛ على Windows NT/XP ، تقوم ReadFile بإرجاع FALSE دائمًا باستخدام رمز الخطأ المعين إلى ERROR_BREKN_PIPE إذا كانت عملية الطفل عبارة عن تطبيق DOS.
حل مشكلة كتلة القراءة
لمنع حظر العملية الوالدية بواسطة Readfile ، يمكننا ببساطة تمرير مقبض الملفات كمستهلك لعملية الطفل ، ثم مراقبة هذا الملف. هناك طريقة أكثر بساطة هي استدعاء وظيفة PeeKnamedPipe () قبل استدعاء ReadFile (). تتحقق وظيفة PeeKnamedPipe عن معلومات حول البيانات في الأنبوب ، ثم تعود على الفور. إذا لم تكن هناك بيانات متوفرة في الأنبوب ، فلا تدعو Readfile.
من خلال استدعاء PeeKnamedPipe قبل القراءة ، نقوم أيضًا بحل مشكلة كتلة إعادة توجيه وحدة تحكم 16 بت على Windows 9x.