chrome remoting/ chromoting 分析#1

源码查找关键词

serviceUrls
StartHostMain


switches 进程类型开关

namespace remoting {

const char kElevateSwitchName[] = "elevate";
const char kHelpSwitchName[] = "help";
const char kProcessTypeSwitchName[] = "type";
const char kQuestionSwitchName[] = "?";
const char kVersionSwitchName[] = "version";

const char kProcessTypeController[] = "controller";
const char kProcessTypeDaemon[] = "daemon";
const char kProcessTypeDesktop[] = "desktop";
const char kProcessTypeHost[] = "host";
const char kProcessTypeRdpDesktopSession[] = "rdp_desktop_session";
const char kProcessTypeEvaluateCapability[] = "evaluate_capability";

const char kEvaluateCapabilitySwitchName[] = "evaluate-type";

#if defined(OS_WIN)
const char kEvaluateD3D[] = "d3d-support";
const char kEvaluate3dDisplayMode[] = "3d-display-mode";
#endif

const char kParentWindowSwitchName[] = "parent-window";

const char kInputSwitchName[] = "input";
const char kOutputSwitchName[] = "output";

const char kMojoPipeToken[] = "mojo-pipe-token";

} // namespace remoting

不同开关执行不同流程
HostMain->SelectMainRoutine(process_type)

// Select the entry point corresponding to the process type.
MainRoutineFn SelectMainRoutine(const std::string& process_type) {
MainRoutineFn main_routine = nullptr;

if (process_type == kProcessTypeHost) {
main_routine = &HostProcessMain;
#if defined(OS_WIN)
} else if (process_type == kProcessTypeDaemon) {
main_routine = &DaemonProcessMain;
} else if (process_type == kProcessTypeDesktop) {
main_routine = &DesktopProcessMain;
} else if (process_type == kProcessTypeRdpDesktopSession) {
main_routine = &RdpDesktopSessionMain;
#endif // defined(OS_WIN)
}

return main_routine;
}

} // namespace

因此通过process_type来指定启动的进程类型
而每个对应的进程应该有自己特有的参数

 

HostProcessMain->HostProcess->StartOnUiThread->StartOnNetworkThread 单进程条件下-> 使用kStdinConfigPath->OnConfigUpdated->ApplyConfig()
kStdinConfigPath有是命令行传的,分两种情况,当值是-时,表示从stdin读取,否则从C:\ProgramData\Chromoting\host.json读取

可以通过参数host-config 指定路径

const char kHostEnabledConfigPath[] = "enabled";
const char kHostOwnerConfigPath[] = "host_owner";
const char kHostOwnerEmailConfigPath[] = "host_owner_email";
const char kXmppLoginConfigPath[] = "xmpp_login";
const char kOAuthRefreshTokenConfigPath[] = "oauth_refresh_token";
const char kHostIdConfigPath[] = "host_id";
const char kHostNameConfigPath[] = "host_name";
const char kHostSecretHashConfigPath[] = "host_secret_hash";
const char kPrivateKeyConfigPath[] = "private_key";
const char kUsageStatsConsentConfigPath[] = "usage_stats_consent";
const char kEnableVp9ConfigPath[] = "enable_vp9";
const char kEnableH264ConfigPath[] = "enable_h264";
const char kFrameRecorderBufferKbConfigPath[] = "frame-recorder-buffer-kb";


DesktopSessionWin 启动HostService,带有参数 --console 表示以命令行模式启动HostService

DesktopProcessMain 启动后初始化视频捕获线程等,然后连接到以下参数指定的通道
const char PlatformChannel::kHandleSwitch[] = "mojo-platform-channel-handle";
如果时无效的,那么连接到以下参数指定的服务器
const char NamedPlatformChannel::kNamedHandleSwitch[] ="mojo-named-platform-channel-pipe";

由上述远程端点创建的连接以及各个线程(视频捕获,输入,。。。)被作为DesktopProcess.Start函数的参数

RdpDesktopSessionMain COM组件,只给了组件ID,然后Run
组件id对应的类是RdpDesktopSession


gaia_switches.cc
通过参数替换google服务地址,如果服务使用的接口不多,可以采用identityserver做一些兼容
const char kGoogleUrl[] = "google-url";
const char kGaiaUrl[] = "gaia-url";
const char kGoogleApisUrl[] = "google-apis-url";
const char kLsoUrl[] = "lso-url";
const char kOAuth2ClientID[] = "oauth2-client-id";
const char kOAuth2ClientSecret[] = "oauth2-client-secret";

gaia_urls.cc

// Gaia service constants
const char kDefaultGoogleUrl[] = "http://google.com";
const char kDefaultGaiaUrl[] = "https://accounts.google.com";
const char kDefaultGoogleApisBaseUrl[] = "https://www.googleapis.com";

// API calls from accounts.google.com
const char kClientLoginUrlSuffix[] = "ClientLogin";
const char kServiceLoginUrlSuffix[] = "ServiceLogin";
const char kEmbeddedSetupChromeOsUrlSuffixV1[] = "embedded/setup/chromeos";
const char kEmbeddedSetupChromeOsUrlSuffixV2[] = "embedded/setup/v2/chromeos";
// Parameter "ssp=1" is used to skip showing the password bubble when a user
// signs in to Chrome. Note that Gaia will pass this client specified parameter
// to all URLs that are loaded as part of thi sign-in flow.
const char kSigninChromeSyncDice[] = "signin/chrome/sync?ssp=1";
const char kServiceLoginAuthUrlSuffix[] = "ServiceLoginAuth";
const char kServiceLogoutUrlSuffix[] = "Logout";
const char kGetUserInfoUrlSuffix[] = "GetUserInfo";
const char kTokenAuthUrlSuffix[] = "TokenAuth";
const char kMergeSessionUrlSuffix[] = "MergeSession";
const char kOAuthGetAccessTokenUrlSuffix[] = "OAuthGetAccessToken";
const char kOAuthWrapBridgeUrlSuffix[] = "OAuthWrapBridge";
const char kOAuth1LoginUrlSuffix[] = "OAuthLogin";
const char kOAuthMultiloginSuffix[] = "oauth/multilogin";
const char kOAuthRevokeTokenUrlSuffix[] = "AuthSubRevokeToken";
const char kListAccountsSuffix[] = "ListAccounts?json=standard";
const char kEmbeddedSigninSuffix[] = "embedded/setup/chrome/usermenu";
const char kAddAccountSuffix[] = "AddSession";
const char kGetCheckConnectionInfoSuffix[] = "GetCheckConnectionInfo";

// API calls from accounts.google.com (LSO)
const char kGetOAuthTokenUrlSuffix[] = "o/oauth/GetOAuthToken/";
const char kOAuth2AuthUrlSuffix[] = "o/oauth2/auth";
const char kOAuth2RevokeUrlSuffix[] = "o/oauth2/revoke";

// API calls from www.googleapis.com
const char kOAuth2TokenUrlSuffix[] = "oauth2/v4/token";
const char kOAuth2IssueTokenUrlSuffix[] = "oauth2/v2/IssueToken";
const char kOAuth2TokenInfoUrlSuffix[] = "oauth2/v2/tokeninfo";
const char kOAuthUserInfoUrlSuffix[] = "oauth2/v1/userinfo";


架设一个伪造的oauth主机
[RoutePrefix("oauth2")]
public class OAuth2Controller : ApiController
{
[Route("v4/token")]
[HttpGet]
[HttpPost]
public object Token()
{
return new
{
access_token = "ya29.a0Ae4lvC1zRoiR8rIdd2WzN0XC1Q7X9N7DOfIcUVxuCJf2WmluzKd4PYdz0CEClvtiQCEnVU5SgpbWS4dc_DhdVuy5PaLVm4a8fiTe2_COo_sEyuA_wyG2T_ypUORhXBrsfiBPm9nluXeoYhs-qyUrZfRkP4-E0z0Viww",
id_token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjI4Yjc0MWU4ZGU5ODRhNDcxNTlmMTllNmQ3NzgzZTlkNGZhODEwZGIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MDc0MDg3MTgxOTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTc4MzMyODI2Mzk1ODY2Njg1ODIiLCJlbWFpbCI6InNhbmRzZWEuaW5mb0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6ImQwU3hSOUU0WC1jcFRQd2JLdXl1NmciLCJuYW1lIjoi6YGT5pyoIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdqQTJEZjZlT3c5VWJqSVhwcEdJdEdmQnFkX2Y4VzU4Q183RFhhWT1zOTYtYyIsImdpdmVuX25hbWUiOiLmnKgiLCJmYW1pbHlfbmFtZSI6IumBkyIsImxvY2FsZSI6InpoLUNOIiwiaWF0IjoxNTg4NDA1MTI3LCJleHAiOjE1ODg0MDg3Mjd9.GbG4mSstq3KL0ctamKQKqaTPL-Z2V8OPo7UBQWwKU2YvcC--I_h9_L7WuWqmKWv8T9zIcOOHkprVFk-6L4_5L-qgkilLKDi0Fe-lRx1J7KvegrVf00r3ZNU-i17TQ7OuKwYqIpEg_I9XjlpyN2BVzLDj3UaQeBnprdsfoWYH0vdBgPhGlRnNlAEb_SUeTg38blelh_1LECK2acG8XtBazF6aNDCGKtr40sHCh7Z0VyuZIFJOZnWqHEC7zKATXmCRNdYNxEEXR-IQHsWyRv37KRPFz36l9xZberj-kkEeE5M7ZzeHcOl6ke6yn3MnPjvgqRZ1kK6C93UcVH75dccAZw",
expires_in = 3599,
token_type = "Bearer",
scope = "https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email",
refresh_token = "1//04xpHGkbhp-tqCgYIARAAGAQSNwF-L9IrJGfc0d0BEMUi5T2FSFmkb4TFOFfgTBTg7J9HbFSmj-fZCrY1MvqMPjiQ5hEN-dgpAtA"
};
}

[Route("v1/userinfo")]
[HttpGet]
[HttpPost]
public object UserInfo()
{
return new
{
email = "tiny@sandsea.info"
};
}
}


public class ValuesController : ApiController
{


[Route("@me/hosts")]
[HttpGet]
[HttpPost]
public object MeHosts()
{
//remoting_start_host.exe--name = locaa--code = 123456--pin = 123456--redirect - url = http://localhost:1024/oauth/callback --gaia-url=http://localhost:64358  --google-apis-url=http://localhost:64358 --oauth2-clent-secret=oyJky7Ul7Tub6+ZQEx4c1Q== --directory-base-url=http://localhost:64358
return JsonConvert.DeserializeObject(
"{"
+ " \"data\":{"
+ " \"kind\":\"chromoting#hostList\","
+ " \"items\":["
+ " {"
+ " \"tokenUrlPatterns\":["
+ " \"tokenUrlPattern_1A\","
+ " \"tokenUrlPattern_1B\","
+ " \"tokenUrlPattern_1C\""
+ " ],"
+ " \"kind\":\"chromoting#host\","
+ " \"hostId\":\"test_host_id_1\","
+ " \"hostName\":\"test_host_name_1\","
+ " \"publicKey\":\"test_public_key_1\","
+ " \"jabberId\":\"test_jabber_id_1\","
+ " \"createdTime\":\"test_created_time_1\","
+ " \"updatedTime\":\"test_updated_time_1\","
+ " \"status\":\"ONLINE\","
+ " \"hostOfflineReason\":\"\","
+ " \"hostVersion\":\"test_host_version_1\""
+ " },"
+ " {"
+ " \"kind\":\"chromoting#host\","
+ " \"hostId\":\"test_host_id_2\","
+ " \"hostName\":\"test_host_name_2\","
+ " \"publicKey\":\"test_public_key_2\","
+ " \"jabberId\":\"test_jabber_id_2\","
+ " \"createdTime\":\"test_created_time_2\","
+ " \"updatedTime\":\"test_updated_time_2\","
+ " \"status\":\"OFFLINE\","
+ " \"hostOfflineReason\":\"test_host_offline_reason_2\","
+ " \"hostVersion\":\"test_host_version_2\""
+ " }"
+ " ]"
+ " }"
+ "}");
}

然后使用这个主机执行以下命令
remoting_start_host.exe --name=locaa --code=123456 --pin=123456 --redirect-url=http://localhost:1024/oauth/callback --gaia-url=http://localhost:64358  --google-apis-url=http://localhost:64358 --oauth2-clent-secret=oyJky7Ul7Tub6+ZQEx4c1Q== --directory-base-url=http://localhost:64358
directory-base-url并不生效,需要修改源码让其生效
service_urls.cc 第44行 以及后面有个类似的 ,将其注释掉 //#if !defined(NDEBUG)

这个命令会启动一个服务,

"C:\Program Files (x86)\Google\Chrome Remote Desktop\83.0.4103.2\remoting_host.exe" --type=daemon --host-config="C:\ProgramData\Google\Chrome Remote Desktop\host.json"
名称为chromoting,displayName=Chrome Remote Desktop Service

以及一个主机进程
remoting_host.exe" --type=host --mojo-pipe-token=xxxx --mojo-platform-channel-handle=xxxx


这两个对应上面的HostProcessMain和DaemonProcessMain
功能是管理客户端到服务端的会话


貌似remoting的windows服务端直接用的微软rdp
rdp_desktop_session->rdp_client->rdp_client_window -> Microsoft RDP ActiveX control


原生客户端
JniClient/IosClient ->ChromotingSession->ChromotingClientRuntime

//网页客户端 ppapi
pp_instance->ChromotingInstance->ChromotingClient

 

下一步需要分析客户端与主机之间连接的创建是怎么实现的。

 

posted @ 2020-05-02 20:31  道木先生  阅读(786)  评论(0编辑  收藏  举报