Ecoer Logo

@zohead

27

Soul Of Free Loop

steemit.com/@zohead
VOTING POWER100.00%
DOWNVOTE POWER100.00%
RESOURCE CREDITS100.00%
REPUTATION PROGRESS74.91%
Net Worth
0.044USD
STEEM
0.057STEEM
SBD
0.000SBD
Effective Power
5.007SP
├── Own SP
0.708SP
└── Incoming Deleg
+4.300SP

Detailed Balance

STEEM
balance
0.057STEEM
market_balance
0.000STEEM
savings_balance
0.000STEEM
reward_steem_balance
0.000STEEM
STEEM POWER
Own SP
0.708SP
Delegated Out
0.000SP
Delegation In
4.300SP
Effective Power
5.007SP
Reward SP (pending)
0.000SP
SBD
sbd_balance
0.000SBD
sbd_conversions
0.000SBD
sbd_market_balance
0.000SBD
savings_sbd_balance
0.000SBD
reward_sbd_balance
0.000SBD
{
  "balance": "0.057 STEEM",
  "savings_balance": "0.000 STEEM",
  "reward_steem_balance": "0.000 STEEM",
  "vesting_shares": "1150.728258 VESTS",
  "delegated_vesting_shares": "0.000000 VESTS",
  "received_vesting_shares": "6992.931548 VESTS",
  "sbd_balance": "0.000 SBD",
  "savings_sbd_balance": "0.000 SBD",
  "reward_sbd_balance": "0.000 SBD",
  "conversions": []
}

Account Info

namezohead
id272357
rank963,548
reputation1564372509
created2017-07-21T06:53:12
recovery_accountsteem
proxyNone
post_count6
comment_count0
lifetime_vote_count0
witnesses_voted_for0
last_post2018-10-30T18:12:06
last_root_post2018-10-30T18:12:06
last_vote_time2018-08-16T15:14:42
proxied_vsf_votes0, 0, 0, 0
can_vote1
voting_power0
delayed_votes0
balance0.057 STEEM
savings_balance0.000 STEEM
sbd_balance0.000 SBD
savings_sbd_balance0.000 SBD
vesting_shares1150.728258 VESTS
delegated_vesting_shares0.000000 VESTS
received_vesting_shares6992.931548 VESTS
reward_vesting_balance0.000000 VESTS
vesting_balance0.000 STEEM
vesting_withdraw_rate0.000000 VESTS
next_vesting_withdrawal1969-12-31T23:59:59
withdrawn0
to_withdraw0
withdraw_routes0
savings_withdraw_requests0
last_account_recovery1970-01-01T00:00:00
reset_accountnull
last_owner_update1970-01-01T00:00:00
last_account_update2018-08-13T15:13:51
minedNo
sbd_seconds0
sbd_last_interest_payment1970-01-01T00:00:00
savings_sbd_last_interest_payment1970-01-01T00:00:00
{
  "active": {
    "account_auths": [],
    "key_auths": [
      [
        "STM6ABQSukpVQaUYwvvnXC5aoVM7g3DGBXQq7mLz2L7NrgXU6zgw2",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "balance": "0.057 STEEM",
  "can_vote": true,
  "comment_count": 0,
  "created": "2017-07-21T06:53:12",
  "curation_rewards": 0,
  "delegated_vesting_shares": "0.000000 VESTS",
  "downvote_manabar": {
    "current_mana": 2035914951,
    "last_update_time": 1779093663
  },
  "guest_bloggers": [],
  "id": 272357,
  "json_metadata": "{\"profile\":{\"profile_image\":\"https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443\",\"name\":\"Uranus Zhou\",\"about\":\"Soul Of Free Loop\",\"website\":\"https://zohead.com/\"}}",
  "last_account_recovery": "1970-01-01T00:00:00",
  "last_account_update": "2018-08-13T15:13:51",
  "last_owner_update": "1970-01-01T00:00:00",
  "last_post": "2018-10-30T18:12:06",
  "last_root_post": "2018-10-30T18:12:06",
  "last_vote_time": "2018-08-16T15:14:42",
  "lifetime_vote_count": 0,
  "market_history": [],
  "memo_key": "STM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo",
  "mined": false,
  "name": "zohead",
  "next_vesting_withdrawal": "1969-12-31T23:59:59",
  "other_history": [],
  "owner": {
    "account_auths": [],
    "key_auths": [
      [
        "STM7VLwkYHrxWAJrLtjPyKyc3A4Pigsx6M1EFDtv5haaPGY9QkMb3",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "pending_claimed_accounts": 0,
  "post_bandwidth": 0,
  "post_count": 6,
  "post_history": [],
  "posting": {
    "account_auths": [],
    "key_auths": [
      [
        "STM7fvVpcoYsrKR857KVR1NDV3o4S8CP75rvQQxXVAG1ViGr8LMKV",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "posting_json_metadata": "{\"profile\":{\"profile_image\":\"https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443\",\"name\":\"Uranus Zhou\",\"about\":\"Soul Of Free Loop\",\"website\":\"https://zohead.com/\"}}",
  "posting_rewards": 115,
  "proxied_vsf_votes": [
    0,
    0,
    0,
    0
  ],
  "proxy": "",
  "received_vesting_shares": "6992.931548 VESTS",
  "recovery_account": "steem",
  "reputation": 1564372509,
  "reset_account": "null",
  "reward_sbd_balance": "0.000 SBD",
  "reward_steem_balance": "0.000 STEEM",
  "reward_vesting_balance": "0.000000 VESTS",
  "reward_vesting_steem": "0.000 STEEM",
  "savings_balance": "0.000 STEEM",
  "savings_sbd_balance": "0.000 SBD",
  "savings_sbd_last_interest_payment": "1970-01-01T00:00:00",
  "savings_sbd_seconds": "0",
  "savings_sbd_seconds_last_update": "1970-01-01T00:00:00",
  "savings_withdraw_requests": 0,
  "sbd_balance": "0.000 SBD",
  "sbd_last_interest_payment": "1970-01-01T00:00:00",
  "sbd_seconds": "0",
  "sbd_seconds_last_update": "2022-03-02T02:59:27",
  "tags_usage": [],
  "to_withdraw": 0,
  "transfer_history": [],
  "vesting_balance": "0.000 STEEM",
  "vesting_shares": "1150.728258 VESTS",
  "vesting_withdraw_rate": "0.000000 VESTS",
  "vote_history": [],
  "voting_manabar": {
    "current_mana": "8143659806",
    "last_update_time": 1779093663
  },
  "voting_power": 0,
  "withdraw_routes": 0,
  "withdrawn": 0,
  "witness_votes": [],
  "witnesses_voted_for": 0,
  "rank": 963548
}

Withdraw Routes

IncomingOutgoing
Empty
Empty
{
  "incoming": [],
  "outgoing": []
}
From Date
To Date
steemdelegated 4.300 SP to @zohead
2026/05/18 08:41:03
delegateezohead
delegatorsteem
vesting shares6992.931548 VESTS
Transaction InfoBlock #106153524/Trx c1a176865cd5609ab8620f9a5310d228e2d71768
View Raw JSON Data
{
  "block": 106153524,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "6992.931548 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-05-18T08:41:03",
  "trx_id": "c1a176865cd5609ab8620f9a5310d228e2d71768",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemdelegated 2.632 SP to @zohead
2026/05/13 14:55:39
delegateezohead
delegatorsteem
vesting shares4279.998178 VESTS
Transaction InfoBlock #106017723/Trx 3cf241e4424f5e3161a0f8fa9ef72b139e8312f2
View Raw JSON Data
{
  "block": 106017723,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "4279.998178 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-05-13T14:55:39",
  "trx_id": "3cf241e4424f5e3161a0f8fa9ef72b139e8312f2",
  "trx_in_block": 3,
  "virtual_op": 0
}
steemdelegated 4.307 SP to @zohead
2026/04/26 07:49:24
delegateezohead
delegatorsteem
vesting shares7005.447304 VESTS
Transaction InfoBlock #105520943/Trx a93c431f22f23e76a40eb2d71ed8d3c345914fc2
View Raw JSON Data
{
  "block": 105520943,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "7005.447304 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-04-26T07:49:24",
  "trx_id": "a93c431f22f23e76a40eb2d71ed8d3c345914fc2",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 2.658 SP to @zohead
2026/01/24 06:41:03
delegateezohead
delegatorsteem
vesting shares4322.267962 VESTS
Transaction InfoBlock #102879214/Trx 18afc9faa3e592f686326bfdb2f89a08372d81cc
View Raw JSON Data
{
  "block": 102879214,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "4322.267962 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-01-24T06:41:03",
  "trx_id": "18afc9faa3e592f686326bfdb2f89a08372d81cc",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 2.758 SP to @zohead
2024/12/18 01:38:27
delegateezohead
delegatorsteem
vesting shares4486.487159 VESTS
Transaction InfoBlock #91325180/Trx 8c1ec4f68285994a8a41977b9e1ff3139d6f6bb1
View Raw JSON Data
{
  "block": 91325180,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "4486.487159 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2024-12-18T01:38:27",
  "trx_id": "8c1ec4f68285994a8a41977b9e1ff3139d6f6bb1",
  "trx_in_block": 5,
  "virtual_op": 0
}
steemdelegated 2.862 SP to @zohead
2023/11/14 17:18:12
delegateezohead
delegatorsteem
vesting shares4655.620691 VESTS
Transaction InfoBlock #79879286/Trx bf068ea5ba648e52bb20f3d5db4bd87e1418e2f5
View Raw JSON Data
{
  "block": 79879286,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "4655.620691 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2023-11-14T17:18:12",
  "trx_id": "bf068ea5ba648e52bb20f3d5db4bd87e1418e2f5",
  "trx_in_block": 8,
  "virtual_op": 0
}
steemdelegated 4.668 SP to @zohead
2023/09/22 13:14:39
delegateezohead
delegatorsteem
vesting shares7592.529477 VESTS
Transaction InfoBlock #78366279/Trx 9916583d5e28cd2b637e04053ec4a84cd60eb570
View Raw JSON Data
{
  "block": 78366279,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "7592.529477 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2023-09-22T13:14:39",
  "trx_id": "9916583d5e28cd2b637e04053ec4a84cd60eb570",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemdelegated 4.790 SP to @zohead
2022/12/06 15:30:36
delegateezohead
delegatorsteem
vesting shares7790.746498 VESTS
Transaction InfoBlock #70063085/Trx 72982e642f703fd70549a0fc71becaa46c05a973
View Raw JSON Data
{
  "block": 70063085,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "7790.746498 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2022-12-06T15:30:36",
  "trx_id": "72982e642f703fd70549a0fc71becaa46c05a973",
  "trx_in_block": 7,
  "virtual_op": 0
}
steemdelegated 4.900 SP to @zohead
2022/04/10 18:37:06
delegateezohead
delegatorsteem
vesting shares7969.640957 VESTS
Transaction InfoBlock #63194902/Trx ecb1a6690ea2d7d123181ae35c3ab5294aef1959
View Raw JSON Data
{
  "block": 63194902,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "7969.640957 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2022-04-10T18:37:06",
  "trx_id": "ecb1a6690ea2d7d123181ae35c3ab5294aef1959",
  "trx_in_block": 1,
  "virtual_op": 0
}
zoheadclaimed reward balance: 0.057 STEEM, 0.072 SP
2022/03/02 02:59:27
accountzohead
reward sbd0.000 SBD
reward steem0.057 STEEM
reward vests117.428030 VESTS
Transaction InfoBlock #62058522/Trx f879ea205a6d81cd87c31e195c3eff467b3476f1
View Raw JSON Data
{
  "block": 62058522,
  "op": [
    "claim_reward_balance",
    {
      "account": "zohead",
      "reward_sbd": "0.000 SBD",
      "reward_steem": "0.057 STEEM",
      "reward_vests": "117.428030 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2022-03-02T02:59:27",
  "trx_id": "f879ea205a6d81cd87c31e195c3eff467b3476f1",
  "trx_in_block": 6,
  "virtual_op": 0
}
steemdelegated 5.012 SP to @zohead
2022/01/18 01:17:12
delegateezohead
delegatorsteem
vesting shares8152.116546 VESTS
Transaction InfoBlock #60826524/Trx 54b9007cd145fd69a40ae2f80ef1881b19fcac79
View Raw JSON Data
{
  "block": 60826524,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "8152.116546 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2022-01-18T01:17:12",
  "trx_id": "54b9007cd145fd69a40ae2f80ef1881b19fcac79",
  "trx_in_block": 10,
  "virtual_op": 0
}
steemdelegated 5.126 SP to @zohead
2021/06/14 08:22:06
delegateezohead
delegatorsteem
vesting shares8336.310834 VESTS
Transaction InfoBlock #54616720/Trx bbc1204b0de112f48c1494875049ed18784658fd
View Raw JSON Data
{
  "block": 54616720,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "8336.310834 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2021-06-14T08:22:06",
  "trx_id": "bbc1204b0de112f48c1494875049ed18784658fd",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 5.241 SP to @zohead
2020/12/11 18:31:54
delegateezohead
delegatorsteem
vesting shares8523.732808 VESTS
Transaction InfoBlock #49363907/Trx 4b7d9927a9e4f00bbd45029431429f02e75f7db4
View Raw JSON Data
{
  "block": 49363907,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "8523.732808 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-12-11T18:31:54",
  "trx_id": "4b7d9927a9e4f00bbd45029431429f02e75f7db4",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 1.176 SP to @zohead
2020/12/06 12:07:00
delegateezohead
delegatorsteem
vesting shares1912.543513 VESTS
Transaction InfoBlock #49215420/Trx cb54fff06470bdf81267d4ea1afe5d6e72f7ffea
View Raw JSON Data
{
  "block": 49215420,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "1912.543513 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-12-06T12:07:00",
  "trx_id": "cb54fff06470bdf81267d4ea1afe5d6e72f7ffea",
  "trx_in_block": 6,
  "virtual_op": 0
}
steemdelegated 5.251 SP to @zohead
2020/11/26 05:53:36
delegateezohead
delegatorsteem
vesting shares8540.859425 VESTS
Transaction InfoBlock #48924373/Trx 077d1eda026f2310039486830fb6af23ea1d55fb
View Raw JSON Data
{
  "block": 48924373,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "8540.859425 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-11-26T05:53:36",
  "trx_id": "077d1eda026f2310039486830fb6af23ea1d55fb",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 5.369 SP to @zohead
2020/05/09 13:12:00
delegateezohead
delegatorsteem
vesting shares8732.746021 VESTS
Transaction InfoBlock #43225778/Trx 1406949143a145a520fb878be8630d134094b576
View Raw JSON Data
{
  "block": 43225778,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "8732.746021 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-05-09T13:12:00",
  "trx_id": "1406949143a145a520fb878be8630d134094b576",
  "trx_in_block": 19,
  "virtual_op": 0
}
steemdelegated 1.201 SP to @zohead
2020/05/08 17:55:45
delegateezohead
delegatorsteem
vesting shares1953.311140 VESTS
Transaction InfoBlock #43203195/Trx 6bb931b43afd7f80d8a81924c4b2df7a42cf6cfb
View Raw JSON Data
{
  "block": 43203195,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "1953.311140 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-05-08T17:55:45",
  "trx_id": "6bb931b43afd7f80d8a81924c4b2df7a42cf6cfb",
  "trx_in_block": 9,
  "virtual_op": 0
}
steemdelegated 5.413 SP to @zohead
2019/12/31 00:52:00
delegateezohead
delegatorsteem
vesting shares8803.940816 VESTS
Transaction InfoBlock #39504608/Trx 65097ea7dd1902fb6baf46eeccc85d52d9bc6ff5
View Raw JSON Data
{
  "block": 39504608,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "8803.940816 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2019-12-31T00:52:00",
  "trx_id": "65097ea7dd1902fb6baf46eeccc85d52d9bc6ff5",
  "trx_in_block": 14,
  "virtual_op": 0
}
2019/07/21 07:41:30
authorsteemitboard
bodyCongratulations @zohead! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@zohead/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table> <sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@zohead) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=zohead)_</sub> ###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorzohead
parent permlinkandroid
permlinksteemitboard-notify-zohead-20190721t074129000z
title
Transaction InfoBlock #34850434/Trx e3a8c0700bccca4d75bbfd5e72424d9b71d55f06
View Raw JSON Data
{
  "block": 34850434,
  "op": [
    "comment",
    {
      "author": "steemitboard",
      "body": "Congratulations @zohead! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@zohead/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table>\n\n<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@zohead) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=zohead)_</sub>\n\n\n###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "zohead",
      "parent_permlink": "android",
      "permlink": "steemitboard-notify-zohead-20190721t074129000z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2019-07-21T07:41:30",
  "trx_id": "e3a8c0700bccca4d75bbfd5e72424d9b71d55f06",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 5.534 SP to @zohead
2019/01/29 19:58:57
delegateezohead
delegatorsteem
vesting shares9000.718244 VESTS
Transaction InfoBlock #29888726/Trx 00cd835eddd9738b1924dd1a3957e4e0b474f22a
View Raw JSON Data
{
  "block": 29888726,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "9000.718244 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2019-01-29T19:58:57",
  "trx_id": "00cd835eddd9738b1924dd1a3957e4e0b474f22a",
  "trx_in_block": 2,
  "virtual_op": 0
}
steemdelegated 17.920 SP to @zohead
2018/12/14 18:47:21
delegateezohead
delegatorsteem
vesting shares29146.001697 VESTS
Transaction InfoBlock #28563616/Trx 69723a17cdb47fa16d7f1ae908e02c36b6e3fc2a
View Raw JSON Data
{
  "block": 28563616,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "29146.001697 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-12-14T18:47:21",
  "trx_id": "69723a17cdb47fa16d7f1ae908e02c36b6e3fc2a",
  "trx_in_block": 24,
  "virtual_op": 0
}
zoheadpublished a new post: android
2018/10/30 18:13:03
authorzohead
body## 酷走记录仪 博客有段时间没有更新了,提前说好这不是一般的行车记录仪评测文章,我现在开的 SUV 上并没有装记录仪,只是同事刚好送了一个厂商已倒闭的记录仪,看到这款采用 Intel Atom 处理器的行车记录仪有点兴趣,准备初步研究下。 酷走行车记录仪由深圳汉普云联科技生产,具体型号为 KZV201,网上关于此行车记录仪的评测文章还是有一些的,之前 [京东众筹](http://z.jd.com/project/details/42623.html) 上的链接应该还在,想了解的朋友们可以看看。 这里我就不做具体介绍了,外观图也就不上了。初步了解此记录仪使用 Intel SOFIA Atom x3 处理器,因此集成了 3G 上网功能(带 SIM 卡插槽),支持 2.4 GHz Wi-Fi 网络、蓝牙 4.0、GPS 及 FM 发射功能。酷走记录仪虽然号称支持 1080p 全高清视频录制,然而实际测试录像效果也是比较一般,另外采用 Android 5.0 系统也挺少见。 由于机身没有任何物理按键,所有功能都要通过手机 App 连接行车记录仪 Wi-Fi 热点来完成,这也是相当坑的地方:由于厂家已经不提供支持更新了,目前手机 App 里不少功能缺失,最基本的 SIM 卡(专用物联网卡)数据流量充值功能都不能使用;手机 App 连修改记录仪系统时间的功能都没有提供(可能厂家考虑的是自动通过 SIM 卡数据流量进行时间同步),导致目前记录仪的时间都不正确。 ## Web 接口使用 为了摆脱随时可能会完全崩掉的手机 App,我就需要知道基本的管理接口和视频调阅接口。 记录仪自带的 Wi-Fi 热点使用固定的 `192.168.43.1` IP 地址(别指望能修改了),手机或者电脑连接酷走的 Wi-Fi 热点之后就可以 ping 通记录仪地址了,下面我贴出来的例子都是在 Chromebook 上测试的,首先我用 Linux 自带的 `nc` 命令来扫一下记录仪开放的端口: ```bash (xenial)zzm@localhost:~$ nc -znv 192.168.43.1 20-20000 2>&1 | grep 'succeeded' Connection to 192.168.43.1 53 port [tcp/*] succeeded! Connection to 192.168.43.1 5556 port [tcp/*] succeeded! Connection to 192.168.43.1 8080 port [tcp/*] succeeded! Connection to 192.168.43.1 8886 port [tcp/*] succeeded! ``` 显然 `53` 是 Wi-Fi 热点自带的 DNS 服务器端口,看起来 `8080` 就是 Web 接口的端口了,我在安装了酷走 App 的手机上运行 tcpdump 程序进行抓包就可以分析 8080 端口的请求了。 ### 用户登录 首先是用户登录请求: ```bash chronos@localhost ~/Downloads $ curl -v "http://192.168.43.1:8080/term?act=user_login&user=\{%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:nu ll,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22\}&password=8888&termtype=3&force=false" * Trying 192.168.43.1... * TCP_NODELAY set * Connected to 192.168.43.1 (192.168.43.1) port 8080 (#0) > GET /term?act=user_login&user={%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:null,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22}&password=8888&termtype=3&force=false HTTP/1.1 > Host: 192.168.43.1:8080 > User-Agent: curl/7.60.0 > Accept: */* > < HTTP/1.1 200 OK < Set-Cookie: JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi;Path=/ < Set-Cookie: TONG_TOKEN_ID=a2664fc77f2d4c2abf90bea58e3bb6bf;Path=/;Expires=Tue, 29-Feb-2000 00:26:30 GMT < Expires: Thu, 01 Jan 1970 00:00:00 GMT < Content-Type: text/plain;charset=UTF-8 < Access-Control-Allow-Methods: GET, POST < Access-Control-Allow-Credentials: true < Transfer-Encoding: chunked < Server: Jetty(i-jetty 1.0.27) < * Connection #0 to host 192.168.43.1 left intact {"data":"a2664fc77f2d4c2abf90bea58e3bb6bf","elapsedTime":94,"message":"鉴权通过!","responseCode":100} ``` > **注意** > > 上面 curl 命令中的括号做了转义防止 curl 请求错误,实际请求地址中并没有反斜杠,另外请求地址中的 `%22` 就是双引号,如果在浏览器中访问也可以直接把 `%22` 换成双引号。 可以看到记录仪 Web 服务器是基于 Jetty 写的,GET 请求地址中的 `user` 和 `password` 参数不能缺少,`password` 就是记录仪的管理密码(默认:8888),`user` 参数是个 JSON 对象,基本所有参数都可以使用 `null`,除了 `uuid` 参数表示登录用户(可以随机生成一串 UUID),重复发送登录请求将会报错。 最值得关注的是返回 Cookie 数据中的 `JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi` 值,后面所有 Web 请求都会用到这个会话 ID。 ### 查看设备信息 基础设备信息也可以使用 curl 附带 `JSESSIONID` Cookie 值进行查询,下面其它的请求就不详细列出来了: ```bash curl -v -b "JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi" "http://192.168.43.1:8080/term?act=device_info&appID=null" ``` 设备信息输出如下: ```text GET /term?act=device_info&appID=null HTTP/1.1 Host: 192.168.43.1:8080 Cookie: JSESSIONID=73jb4a4t1urf1un1krkwn6drr HTTP/1.1 200 OK Content-Type: text/plain;charset=UTF-8 Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Credentials: true Transfer-Encoding: chunked Server: Jetty(i-jetty 1.0.27) {"data":{"hotName":null,"hotPassword":null,"hwVerion":"4","imei":"XXXXXXX","osVersion":"2.2.3","password":null,"productCode":null,"randomCode":null,"sn":"KXXXXXX","swVersion":"1.0.27","videoPassword":null},"elapsedTime":9,"message":null,"responseCode":100} ``` 查看记录仪配置请求: ```text GET /term?act=query_termconfiginfo HTTP/1.1 {"data":{"hotName":"KUZO_KXXXXXX","hotPassword":"password","hwVerion":"4","imei":"XXXXXXX","osVersion":"2.2.3","password":"8888","productCode":null,"randomCode":null,"sn":"KXXXXXX","swVersion":"1.0.27","videoPassword":""},"elapsedTime":14,"message":"Success!","responseCode":100} ``` 查看 SIM 卡 ICCID(返回值在 `data` 中): ```text GET /term?act=query_sim_iccid HTTP/1.1 {"data":"XXXXXXX","elapsedTime":14,"message":null,"responseCode":100} ``` 查看 FM 发射状态(这个 POST 请求没有附带任何参数): ```text POST /term?act=query_fm_open_status HTTP/1.1 Content-Length: 0 {"data":false,"elapsedTime":35,"message":null,"responseCode":100} ``` 查看 GPS 状态: ```text GET /term?act=query_gps_status HTTP/1.1 {"data":{"position":{"accuracy":14.33,"altitude":15.030368,"angle":337.0591,"crs":1,"gpsTime":"20181023180046","latitude":31.907135402870722,"longitude":118.77761111400555,"speed":0.0},"positionFix":true,"positionType":1,"satellitesInUse":5,"satellitesInView":6},"elapsedTime":3,"message":null,"responseCode":100} ``` 可以看到这里能直接查询到 GPS 的当前经纬度以及精确度,还能看到卫星的使用情况,比较讽刺的是这边 GPS 时间都得到了,记录仪系统在网络不可用的情况下却没考虑使用 GPS 时间。 查询存储状态(装了 TF 卡之后就能看到存储卡容量状态了): ```text GET /term?act=query_storage_info HTTP/1.1 {"data":{"TFExist":true,"TFTotalCapacity":63847890944,"TFUsedCapacity":6595575808,"internalTotalCapacity":3918114816,"internalUsedCapacity":2259316736},"elapsedTime":3,"message":null,"responseCode":100} ``` 查询 3G 移动网络状态: ```text GET /term?act=query_3g_status HTTP/1.1 {"data":{"dialUpSuccess":false,"intensity":0,"mnc":"01","type":"3G"},"elapsedTime":19,"message":null,"responseCode":100} ``` ### 音乐管理 此记录仪支持通过手机 App 上传音乐到记录仪的存储卡,然后通过蓝牙进行播放控制,列举音乐接口: ```text GET /term?act=list_music HTTP/1.1 {"data":[{"fileSize":4008624,"id":0,"length":0,"md5":"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37","name":null,"path":"/mnt/media_rw/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/2/13/e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37.mp3","singer":null,"userUuid":"2F06A8D7CA314388B58DC46719702844","uuid":"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37"}],"elapsedTime":7,"message":"Success!","responseCode":100} ``` 查询最大音量(最大级别为 15): ```text GET /term?act=query_max_volume HTTP/1.1 {"data":15,"elapsedTime":5,"message":null,"responseCode":100} ``` 查看当前音量: ```text GET /term?act=query_volume HTTP/1.1 {"data":10,"elapsedTime":9,"message":"15 15","responseCode":100} ``` ### 视频管理 首先按小时段列举视频,请忽略不正确的系统时间: ```text GET /term?act=list_video_hour HTTP/1.1 {"data":["20000115230000","20000116000000","20000116010000","20000116020000","20000116030000"],"elapsedTime":171,"message":null,"responseCode":100} ``` 指定开始和结束的小时时间段列举视频: ```text GET /term?act=list_poi&start_time=20000116030000&end_time=20000116040000 HTTP/1.1 {"data":[],"elapsedTime":9,"message":null,"responseCode":100} ``` 列举某个小时的所有视频,可以看到存储卡里视频的完整路径: ```text GET /term?act=list_video_db&hour_time=20000116030000 HTTP/1.1 {"data":[{"createTime":"20000116030127","id":42,"length":120009,"lockType":0,"name":"2000-01-16_03-01-27.mp4","path":"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-01-27.mp4","sn":null,"uuid":"a6035028ded14482936c1580aba8e298","videoType":0},{"createTime":"20000116030327","id":43,"length":119940,"lockType":0,"name":"2000-01-16_03-03-27.mp4","path":"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-03-27.mp4","sn":null,"uuid":"291a4796c1274dfea99a978c84ca33d0","videoType":0}],"elapsedTime":986,"message":null,"responseCode":100} ``` 获取某个视频文件的封面截图,此请求直接返回 jpg 图像数据: ```text GET /term?act=snapshot&name=2000-01-16_03-21-26.mp4&time=0 HTTP/1.1 HTTP/1.1 200 OK Content-Type: image/jpeg Accept-Ranges: bytes Content-Length: 42351 Content-Disposition: attachment;filename=2000-01-16_03-19-26_0.jpg Server: Jetty(i-jetty 1.0.27) ``` 指定开始和结束时间段获取历史轨迹,由于记录仪的 GPS 是一直开启的,还好轨迹还算比较准确,这里轨迹数据比较长我就不详细贴出来了: ```text GET /term?act=query_history_track&start_time=20181013180104&end_time=20181013180304&len=121 HTTP/1.1 ``` 最后是最关键的下载录像接口,通过 `name` 参数指定视频文件名即可: ```text GET /term?act=get_video&name=2000-01-16_03-19-26.mp4 HTTP/1.1 User-Agent: Lavf/57.56.100 Accept: */* Range: bytes=0- Connection: close Host: 192.168.43.1:8080 Icy-MetaData: 1 HTTP/1.1 206 Partial Content Content-Type: application/octet-stream Accept-Ranges: bytes Content-Length: 61722040 Content-Disposition: attachment;filename=2000-01-16_03-19-26.mp4 Content-Range: bytes 3104-61722039/61722040 Connection: close Server: Jetty(i-jetty 1.0.27) ``` 有点奇葩的就是这个下载录像的请求却并没有验证用户会话 ID,因此你也可以通过任何一款播放器指定地址直接远程回放录像: ```text http://192.168.43.1:8080/term?act=get_video&name=2000-01-16_03-19-26.mp4 ``` ## 实时视频接口 通过 tcpdump 抓包就会发现记录仪的实时视频预览是通过上面扫描出来的 8886 端口,记录仪提供 RTSP 形式的实时预览接口: ```text rtsp://192.168.43.1:8886?videoapi=mc2&transport=tcp ``` 需要注意的是记录仪自带的 RTSP 服务器似乎只支持 TCP 流传输,如果使用 VLC 播放器,则需要将 Live555 流传输选项改为:RTP over RTSP (TCP) 才能正常播放。 厂家可能出于预览流畅性考虑 RTSP 并没有直接提供 1080p 的视频,然而我通过 VLC 播放器进行 RTSP 实时预览还是存在两至三秒的延时,在 Chromebook 下切换使用 [VXG Media Player](https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni) 插件进行实时预览则不存在延时问题。 ## 总结 目前该行车记录仪使用下来还存在几个主要问题: * SIM 卡无法联网的情况下系统时间没办法修改; * 手机 App 提供的记录仪通过扫码连接外部 Wi-Fi 或者手机热点的功能无法工作,如果可以的话系统时间同步应该就不是问题; * 记录仪硬件上有降噪麦克风,而且支持通过蓝牙的语音对讲功能(只用于目前手机 App 里基本不可用的结伴出行组队),但是视频录像却不支持音频录制,同样 RTSP 实时视频中也没有带音频流。 这些问题只能后面有空的话再抽时间看看能否进入记录仪的 Android 系统进行修改了,祝大家玩的开心。
json metadata{"tags":["kuzo","recorder","cn","kubo"],"links":["http://z.jd.com/project/details/42623.html","https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkkubo
permlinkandroid
title初探酷走Android行车记录仪
Transaction InfoBlock #27267708/Trx 68f39e88414f6aaeb9bdee8d2793495475e808cb
View Raw JSON Data
{
  "block": 27267708,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## 酷走记录仪\n\n博客有段时间没有更新了,提前说好这不是一般的行车记录仪评测文章,我现在开的 SUV 上并没有装记录仪,只是同事刚好送了一个厂商已倒闭的记录仪,看到这款采用 Intel Atom 处理器的行车记录仪有点兴趣,准备初步研究下。\n\n酷走行车记录仪由深圳汉普云联科技生产,具体型号为 KZV201,网上关于此行车记录仪的评测文章还是有一些的,之前 [京东众筹](http://z.jd.com/project/details/42623.html) 上的链接应该还在,想了解的朋友们可以看看。\n\n这里我就不做具体介绍了,外观图也就不上了。初步了解此记录仪使用 Intel SOFIA Atom x3 处理器,因此集成了 3G 上网功能(带 SIM 卡插槽),支持 2.4 GHz Wi-Fi 网络、蓝牙 4.0、GPS 及 FM 发射功能。酷走记录仪虽然号称支持 1080p 全高清视频录制,然而实际测试录像效果也是比较一般,另外采用 Android 5.0 系统也挺少见。\n\n由于机身没有任何物理按键,所有功能都要通过手机 App 连接行车记录仪 Wi-Fi 热点来完成,这也是相当坑的地方:由于厂家已经不提供支持更新了,目前手机 App 里不少功能缺失,最基本的 SIM 卡(专用物联网卡)数据流量充值功能都不能使用;手机 App 连修改记录仪系统时间的功能都没有提供(可能厂家考虑的是自动通过 SIM 卡数据流量进行时间同步),导致目前记录仪的时间都不正确。\n\n## Web 接口使用\n\n为了摆脱随时可能会完全崩掉的手机 App,我就需要知道基本的管理接口和视频调阅接口。\n\n记录仪自带的 Wi-Fi 热点使用固定的 `192.168.43.1` IP 地址(别指望能修改了),手机或者电脑连接酷走的 Wi-Fi 热点之后就可以 ping 通记录仪地址了,下面我贴出来的例子都是在 Chromebook 上测试的,首先我用 Linux 自带的 `nc` 命令来扫一下记录仪开放的端口:\n\n```bash\n(xenial)zzm@localhost:~$ nc -znv 192.168.43.1 20-20000 2>&1 | grep 'succeeded'\nConnection to 192.168.43.1 53 port [tcp/*] succeeded!\nConnection to 192.168.43.1 5556 port [tcp/*] succeeded!\nConnection to 192.168.43.1 8080 port [tcp/*] succeeded!\nConnection to 192.168.43.1 8886 port [tcp/*] succeeded!\n```\n\n显然 `53` 是 Wi-Fi 热点自带的 DNS 服务器端口,看起来 `8080` 就是 Web 接口的端口了,我在安装了酷走 App 的手机上运行 tcpdump 程序进行抓包就可以分析 8080 端口的请求了。\n\n### 用户登录\n\n首先是用户登录请求:\n\n```bash\nchronos@localhost ~/Downloads $ curl -v \"http://192.168.43.1:8080/term?act=user_login&user=\\{%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:nu\nll,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22\\}&password=8888&termtype=3&force=false\"\n*   Trying 192.168.43.1...\n* TCP_NODELAY set\n* Connected to 192.168.43.1 (192.168.43.1) port 8080 (#0)\n> GET /term?act=user_login&user={%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:null,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22}&password=8888&termtype=3&force=false HTTP/1.1\n> Host: 192.168.43.1:8080\n> User-Agent: curl/7.60.0\n> Accept: */*\n> \n< HTTP/1.1 200 OK\n< Set-Cookie: JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi;Path=/\n< Set-Cookie: TONG_TOKEN_ID=a2664fc77f2d4c2abf90bea58e3bb6bf;Path=/;Expires=Tue, 29-Feb-2000 00:26:30 GMT\n< Expires: Thu, 01 Jan 1970 00:00:00 GMT\n< Content-Type: text/plain;charset=UTF-8\n< Access-Control-Allow-Methods: GET, POST\n< Access-Control-Allow-Credentials: true\n< Transfer-Encoding: chunked\n< Server: Jetty(i-jetty 1.0.27)\n< \n* Connection #0 to host 192.168.43.1 left intact\n{\"data\":\"a2664fc77f2d4c2abf90bea58e3bb6bf\",\"elapsedTime\":94,\"message\":\"鉴权通过!\",\"responseCode\":100}\n```\n\n> **注意**\n> \n> 上面 curl 命令中的括号做了转义防止 curl 请求错误,实际请求地址中并没有反斜杠,另外请求地址中的 `%22` 就是双引号,如果在浏览器中访问也可以直接把 `%22` 换成双引号。\n\n可以看到记录仪 Web 服务器是基于 Jetty 写的,GET 请求地址中的 `user` 和 `password` 参数不能缺少,`password` 就是记录仪的管理密码(默认:8888),`user` 参数是个 JSON 对象,基本所有参数都可以使用 `null`,除了 `uuid` 参数表示登录用户(可以随机生成一串 UUID),重复发送登录请求将会报错。\n\n最值得关注的是返回 Cookie 数据中的 `JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi` 值,后面所有 Web 请求都会用到这个会话 ID。\n\n### 查看设备信息\n\n基础设备信息也可以使用 curl 附带 `JSESSIONID` Cookie 值进行查询,下面其它的请求就不详细列出来了:\n\n```bash\ncurl -v -b \"JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi\" \"http://192.168.43.1:8080/term?act=device_info&appID=null\"\n```\n\n设备信息输出如下:\n\n```text\nGET /term?act=device_info&appID=null HTTP/1.1\nHost: 192.168.43.1:8080\nCookie: JSESSIONID=73jb4a4t1urf1un1krkwn6drr\n\nHTTP/1.1 200 OK\nContent-Type: text/plain;charset=UTF-8\nAccess-Control-Allow-Methods: GET, POST\nAccess-Control-Allow-Credentials: true\nTransfer-Encoding: chunked\nServer: Jetty(i-jetty 1.0.27)\n\n{\"data\":{\"hotName\":null,\"hotPassword\":null,\"hwVerion\":\"4\",\"imei\":\"XXXXXXX\",\"osVersion\":\"2.2.3\",\"password\":null,\"productCode\":null,\"randomCode\":null,\"sn\":\"KXXXXXX\",\"swVersion\":\"1.0.27\",\"videoPassword\":null},\"elapsedTime\":9,\"message\":null,\"responseCode\":100}\n```\n\n查看记录仪配置请求:\n\n```text\nGET /term?act=query_termconfiginfo HTTP/1.1\n\n{\"data\":{\"hotName\":\"KUZO_KXXXXXX\",\"hotPassword\":\"password\",\"hwVerion\":\"4\",\"imei\":\"XXXXXXX\",\"osVersion\":\"2.2.3\",\"password\":\"8888\",\"productCode\":null,\"randomCode\":null,\"sn\":\"KXXXXXX\",\"swVersion\":\"1.0.27\",\"videoPassword\":\"\"},\"elapsedTime\":14,\"message\":\"Success!\",\"responseCode\":100}\n```\n\n查看 SIM 卡 ICCID(返回值在 `data` 中):\n\n```text\nGET /term?act=query_sim_iccid HTTP/1.1\n\n{\"data\":\"XXXXXXX\",\"elapsedTime\":14,\"message\":null,\"responseCode\":100}\n```\n\n查看 FM 发射状态(这个 POST 请求没有附带任何参数):\n\n```text\nPOST /term?act=query_fm_open_status HTTP/1.1\nContent-Length: 0\n\n{\"data\":false,\"elapsedTime\":35,\"message\":null,\"responseCode\":100}\n```\n\n查看 GPS 状态:\n\n```text\nGET /term?act=query_gps_status HTTP/1.1\n\n{\"data\":{\"position\":{\"accuracy\":14.33,\"altitude\":15.030368,\"angle\":337.0591,\"crs\":1,\"gpsTime\":\"20181023180046\",\"latitude\":31.907135402870722,\"longitude\":118.77761111400555,\"speed\":0.0},\"positionFix\":true,\"positionType\":1,\"satellitesInUse\":5,\"satellitesInView\":6},\"elapsedTime\":3,\"message\":null,\"responseCode\":100}\n```\n\n可以看到这里能直接查询到 GPS 的当前经纬度以及精确度,还能看到卫星的使用情况,比较讽刺的是这边 GPS 时间都得到了,记录仪系统在网络不可用的情况下却没考虑使用 GPS 时间。\n\n查询存储状态(装了 TF 卡之后就能看到存储卡容量状态了):\n\n```text\nGET /term?act=query_storage_info HTTP/1.1\n\n{\"data\":{\"TFExist\":true,\"TFTotalCapacity\":63847890944,\"TFUsedCapacity\":6595575808,\"internalTotalCapacity\":3918114816,\"internalUsedCapacity\":2259316736},\"elapsedTime\":3,\"message\":null,\"responseCode\":100}\n```\n\n查询 3G 移动网络状态:\n\n```text\nGET /term?act=query_3g_status HTTP/1.1\n\n{\"data\":{\"dialUpSuccess\":false,\"intensity\":0,\"mnc\":\"01\",\"type\":\"3G\"},\"elapsedTime\":19,\"message\":null,\"responseCode\":100}\n```\n\n### 音乐管理\n\n此记录仪支持通过手机 App 上传音乐到记录仪的存储卡,然后通过蓝牙进行播放控制,列举音乐接口:\n\n```text\nGET /term?act=list_music HTTP/1.1\n\n{\"data\":[{\"fileSize\":4008624,\"id\":0,\"length\":0,\"md5\":\"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37\",\"name\":null,\"path\":\"/mnt/media_rw/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/2/13/e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37.mp3\",\"singer\":null,\"userUuid\":\"2F06A8D7CA314388B58DC46719702844\",\"uuid\":\"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37\"}],\"elapsedTime\":7,\"message\":\"Success!\",\"responseCode\":100}\n```\n\n查询最大音量(最大级别为 15):\n\n```text\nGET /term?act=query_max_volume HTTP/1.1\n\n{\"data\":15,\"elapsedTime\":5,\"message\":null,\"responseCode\":100}\n```\n\n查看当前音量:\n\n```text\nGET /term?act=query_volume HTTP/1.1\n\n{\"data\":10,\"elapsedTime\":9,\"message\":\"15 15\",\"responseCode\":100}\n```\n\n### 视频管理\n\n首先按小时段列举视频,请忽略不正确的系统时间:\n\n```text\nGET /term?act=list_video_hour HTTP/1.1\n\n{\"data\":[\"20000115230000\",\"20000116000000\",\"20000116010000\",\"20000116020000\",\"20000116030000\"],\"elapsedTime\":171,\"message\":null,\"responseCode\":100}\n```\n\n指定开始和结束的小时时间段列举视频:\n\n```text\nGET /term?act=list_poi&start_time=20000116030000&end_time=20000116040000 HTTP/1.1\n\n{\"data\":[],\"elapsedTime\":9,\"message\":null,\"responseCode\":100}\n```\n\n列举某个小时的所有视频,可以看到存储卡里视频的完整路径:\n\n```text\nGET /term?act=list_video_db&hour_time=20000116030000 HTTP/1.1\n\n{\"data\":[{\"createTime\":\"20000116030127\",\"id\":42,\"length\":120009,\"lockType\":0,\"name\":\"2000-01-16_03-01-27.mp4\",\"path\":\"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-01-27.mp4\",\"sn\":null,\"uuid\":\"a6035028ded14482936c1580aba8e298\",\"videoType\":0},{\"createTime\":\"20000116030327\",\"id\":43,\"length\":119940,\"lockType\":0,\"name\":\"2000-01-16_03-03-27.mp4\",\"path\":\"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-03-27.mp4\",\"sn\":null,\"uuid\":\"291a4796c1274dfea99a978c84ca33d0\",\"videoType\":0}],\"elapsedTime\":986,\"message\":null,\"responseCode\":100}\n```\n\n获取某个视频文件的封面截图,此请求直接返回 jpg 图像数据:\n\n```text\nGET /term?act=snapshot&name=2000-01-16_03-21-26.mp4&time=0 HTTP/1.1\n\nHTTP/1.1 200 OK\nContent-Type: image/jpeg\nAccept-Ranges: bytes\nContent-Length: 42351\nContent-Disposition: attachment;filename=2000-01-16_03-19-26_0.jpg\nServer: Jetty(i-jetty 1.0.27)\n```\n\n指定开始和结束时间段获取历史轨迹,由于记录仪的 GPS 是一直开启的,还好轨迹还算比较准确,这里轨迹数据比较长我就不详细贴出来了:\n\n```text\nGET /term?act=query_history_track&start_time=20181013180104&end_time=20181013180304&len=121 HTTP/1.1\n```\n\n最后是最关键的下载录像接口,通过 `name` 参数指定视频文件名即可:\n\n```text\nGET /term?act=get_video&name=2000-01-16_03-19-26.mp4 HTTP/1.1\nUser-Agent: Lavf/57.56.100\nAccept: */*\nRange: bytes=0-\nConnection: close\nHost: 192.168.43.1:8080\nIcy-MetaData: 1\n\nHTTP/1.1 206 Partial Content\nContent-Type: application/octet-stream\nAccept-Ranges: bytes\nContent-Length: 61722040\nContent-Disposition: attachment;filename=2000-01-16_03-19-26.mp4\nContent-Range: bytes 3104-61722039/61722040\nConnection: close\nServer: Jetty(i-jetty 1.0.27)\n```\n\n有点奇葩的就是这个下载录像的请求却并没有验证用户会话 ID,因此你也可以通过任何一款播放器指定地址直接远程回放录像:\n\n```text\nhttp://192.168.43.1:8080/term?act=get_video&name=2000-01-16_03-19-26.mp4\n```\n\n## 实时视频接口\n\n通过 tcpdump 抓包就会发现记录仪的实时视频预览是通过上面扫描出来的 8886 端口,记录仪提供 RTSP 形式的实时预览接口:\n\n```text\nrtsp://192.168.43.1:8886?videoapi=mc2&transport=tcp\n```\n\n需要注意的是记录仪自带的 RTSP 服务器似乎只支持 TCP 流传输,如果使用 VLC 播放器,则需要将 Live555 流传输选项改为:RTP over RTSP (TCP) 才能正常播放。\n\n厂家可能出于预览流畅性考虑 RTSP 并没有直接提供 1080p 的视频,然而我通过 VLC 播放器进行 RTSP 实时预览还是存在两至三秒的延时,在 Chromebook 下切换使用 [VXG Media Player](https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni) 插件进行实时预览则不存在延时问题。\n\n## 总结\n\n目前该行车记录仪使用下来还存在几个主要问题:\n\n* SIM 卡无法联网的情况下系统时间没办法修改;\n* 手机 App 提供的记录仪通过扫码连接外部 Wi-Fi 或者手机热点的功能无法工作,如果可以的话系统时间同步应该就不是问题;\n* 记录仪硬件上有降噪麦克风,而且支持通过蓝牙的语音对讲功能(只用于目前手机 App 里基本不可用的结伴出行组队),但是视频录像却不支持音频录制,同样 RTSP 实时视频中也没有带音频流。\n\n这些问题只能后面有空的话再抽时间看看能否进入记录仪的 Android 系统进行修改了,祝大家玩的开心。",
      "json_metadata": "{\"tags\":[\"kuzo\",\"recorder\",\"cn\",\"kubo\"],\"links\":[\"http://z.jd.com/project/details/42623.html\",\"https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "kubo",
      "permlink": "android",
      "title": "初探酷走Android行车记录仪"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-10-30T18:13:03",
  "trx_id": "68f39e88414f6aaeb9bdee8d2793495475e808cb",
  "trx_in_block": 22,
  "virtual_op": 0
}
2018/10/30 18:12:18
authorcheetah
bodyHi! I am a robot. I just upvoted you! I found similar content that readers might be interested in: https://zohead.com/archives/kuzo-recorder/
json metadata
parent authorzohead
parent permlinkandroid
permlinkcheetah-re-zoheadandroid
title
Transaction InfoBlock #27267693/Trx 983b89720b6c4343355d89f001be3311f9465dee
View Raw JSON Data
{
  "block": 27267693,
  "op": [
    "comment",
    {
      "author": "cheetah",
      "body": "Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:\nhttps://zohead.com/archives/kuzo-recorder/",
      "json_metadata": "",
      "parent_author": "zohead",
      "parent_permlink": "android",
      "permlink": "cheetah-re-zoheadandroid",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-10-30T18:12:18",
  "trx_id": "983b89720b6c4343355d89f001be3311f9465dee",
  "trx_in_block": 14,
  "virtual_op": 0
}
cheetahupvoted (0.08%) @zohead / android
2018/10/30 18:12:15
authorzohead
permlinkandroid
votercheetah
weight8 (0.08%)
Transaction InfoBlock #27267692/Trx d2238eda028fb2119fbd263788bbf5a2bcc723fc
View Raw JSON Data
{
  "block": 27267692,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "android",
      "voter": "cheetah",
      "weight": 8
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-10-30T18:12:15",
  "trx_id": "d2238eda028fb2119fbd263788bbf5a2bcc723fc",
  "trx_in_block": 11,
  "virtual_op": 0
}
zoheadpublished a new post: android
2018/10/30 18:12:06
authorzohead
body## 酷走记录仪 博客有段时间没有更新了,提前说好这不是一般的行车记录仪评测文章,我现在开的 SUV 上并没有装记录仪,只是同事刚好送了一个厂商已倒闭的记录仪,看到这款采用 Intel Atom 处理器的行车记录仪有点兴趣,准备初步研究下。 酷走行车记录仪由深圳汉普云联科技生产,具体型号为 KZV201,网上关于此行车记录仪的评测文章还是有一些的,之前 [京东众筹](http://z.jd.com/project/details/42623.html) 上的链接应该还在,想了解的朋友们可以看看。 这里我就不做具体介绍了,外观图也就不上了。初步了解此记录仪使用 Intel SOFIA Atom x3 处理器,因此集成了 3G 上网功能(带 SIM 卡插槽),支持 2.4 GHz Wi-Fi 网络、蓝牙 4.0、GPS 及 FM 发射功能。酷走记录仪虽然号称支持 1080p 全高清视频录制,然而实际测试录像效果也是比较一般,另外采用 Android 5.0 系统也挺少见。 由于机身没有任何物理按键,所有功能都要通过手机 App 连接行车记录仪 Wi-Fi 热点来完成,这也是相当坑的地方:由于厂家已经不提供支持更新了,目前手机 App 里不少功能缺失,最基本的 SIM 卡(专用物联网卡)数据流量充值功能都不能使用;手机 App 连修改记录仪系统时间的功能都没有提供(可能厂家考虑的是自动通过 SIM 卡数据流量进行时间同步),导致目前记录仪的时间都不正确。 ## Web 接口使用 为了摆脱随时可能会完全崩掉的手机 App,我就需要知道基本的管理接口和视频调阅接口。 记录仪自带的 Wi-Fi 热点使用固定的 `192.168.43.1` IP 地址(别指望能修改了),手机或者电脑连接酷走的 Wi-Fi 热点之后就可以 ping 通记录仪地址了,下面我贴出来的例子都是在 Chromebook 上测试的,首先我用 Linux 自带的 `nc` 命令来扫一下记录仪开放的端口: ```bash (xenial)zzm@localhost:~$ nc -znv 192.168.43.1 20-20000 2>&1 | grep 'succeeded' Connection to 192.168.43.1 53 port [tcp/*] succeeded! Connection to 192.168.43.1 5556 port [tcp/*] succeeded! Connection to 192.168.43.1 8080 port [tcp/*] succeeded! Connection to 192.168.43.1 8886 port [tcp/*] succeeded! ``` 显然 `53` 是 Wi-Fi 热点自带的 DNS 服务器端口,看起来 `8080` 就是 Web 接口的端口了,我在安装了酷走 App 的手机上运行 tcpdump 程序进行抓包就可以分析 8080 端口的请求了。 ### 用户登录 首先是用户登录请求: ```bash chronos@localhost ~/Downloads $ curl -v "http://192.168.43.1:8080/term?act=user_login&user=\{%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:nu ll,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22\}&password=8888&termtype=3&force=false" * Trying 192.168.43.1... * TCP_NODELAY set * Connected to 192.168.43.1 (192.168.43.1) port 8080 (#0) > GET /term?act=user_login&user={%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:null,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22}&password=8888&termtype=3&force=false HTTP/1.1 > Host: 192.168.43.1:8080 > User-Agent: curl/7.60.0 > Accept: */* > < HTTP/1.1 200 OK < Set-Cookie: JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi;Path=/ < Set-Cookie: TONG_TOKEN_ID=a2664fc77f2d4c2abf90bea58e3bb6bf;Path=/;Expires=Tue, 29-Feb-2000 00:26:30 GMT < Expires: Thu, 01 Jan 1970 00:00:00 GMT < Content-Type: text/plain;charset=UTF-8 < Access-Control-Allow-Methods: GET, POST < Access-Control-Allow-Credentials: true < Transfer-Encoding: chunked < Server: Jetty(i-jetty 1.0.27) < * Connection #0 to host 192.168.43.1 left intact {"data":"a2664fc77f2d4c2abf90bea58e3bb6bf","elapsedTime":94,"message":"鉴权通过!","responseCode":100} ``` > **注意** > > 上面 curl 命令中的括号做了转义防止 curl 请求错误,实际请求地址中并没有反斜杠,另外请求地址中的 `%22` 就是双引号,如果在浏览器中访问也可以直接把 `%22` 换成双引号。 可以看到记录仪 Web 服务器是基于 Jetty 写的,GET 请求地址中的 `user` 和 `password` 参数不能缺少,`password` 就是记录仪的管理密码(默认:8888),`user` 参数是个 JSON 对象,基本所有参数都可以使用 `null`,除了 `uuid` 参数表示登录用户(可以随机生成一串 UUID),重复发送登录请求将会报错。 最值得关注的是返回 Cookie 数据中的 `JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi` 值,后面所有 Web 请求都会用到这个会话 ID。 ### 查看设备信息 基础设备信息也可以使用 curl 附带 `JSESSIONID` Cookie 值进行查询,下面其它的请求就不详细列出来了: ```bash curl -v -b "JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi" "http://192.168.43.1:8080/term?act=device_info&appID=null" ``` 设备信息输出如下: ```text GET /term?act=device_info&appID=null HTTP/1.1 Host: 192.168.43.1:8080 Cookie: JSESSIONID=73jb4a4t1urf1un1krkwn6drr HTTP/1.1 200 OK Content-Type: text/plain;charset=UTF-8 Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Credentials: true Transfer-Encoding: chunked Server: Jetty(i-jetty 1.0.27) {"data":{"hotName":null,"hotPassword":null,"hwVerion":"4","imei":"XXXXXXX","osVersion":"2.2.3","password":null,"productCode":null,"randomCode":null,"sn":"KXXXXXX","swVersion":"1.0.27","videoPassword":null},"elapsedTime":9,"message":null,"responseCode":100} ``` 查看记录仪配置请求: ```text GET /term?act=query_termconfiginfo HTTP/1.1 {"data":{"hotName":"KUZO_KXXXXXX","hotPassword":"password","hwVerion":"4","imei":"XXXXXXX","osVersion":"2.2.3","password":"8888","productCode":null,"randomCode":null,"sn":"KXXXXXX","swVersion":"1.0.27","videoPassword":""},"elapsedTime":14,"message":"Success!","responseCode":100} ``` 查看 SIM 卡 ICCID(返回值在 `data` 中): ```text GET /term?act=query_sim_iccid HTTP/1.1 {"data":"XXXXXXX","elapsedTime":14,"message":null,"responseCode":100} ``` 查看 FM 发射状态(这个 POST 请求没有附带任何参数): ```text POST /term?act=query_fm_open_status HTTP/1.1 Content-Length: 0 {"data":false,"elapsedTime":35,"message":null,"responseCode":100} ``` 查看 GPS 状态: ```text GET /term?act=query_gps_status HTTP/1.1 {"data":{"position":{"accuracy":14.33,"altitude":15.030368,"angle":337.0591,"crs":1,"gpsTime":"20181023180046","latitude":31.907135402870722,"longitude":118.77761111400555,"speed":0.0},"positionFix":true,"positionType":1,"satellitesInUse":5,"satellitesInView":6},"elapsedTime":3,"message":null,"responseCode":100} ``` 可以看到这里能直接查询到 GPS 的当前经纬度以及精确度,还能看到卫星的使用情况,比较讽刺的是这边 GPS 时间都得到了,记录仪系统在网络不可用的情况下却没考虑使用 GPS 时间。 查询存储状态(装了 TF 卡之后就能看到存储卡容量状态了): ```text GET /term?act=query_storage_info HTTP/1.1 {"data":{"TFExist":true,"TFTotalCapacity":63847890944,"TFUsedCapacity":6595575808,"internalTotalCapacity":3918114816,"internalUsedCapacity":2259316736},"elapsedTime":3,"message":null,"responseCode":100} ``` 查询 3G 移动网络状态: ```text GET /term?act=query_3g_status HTTP/1.1 {"data":{"dialUpSuccess":false,"intensity":0,"mnc":"01","type":"3G"},"elapsedTime":19,"message":null,"responseCode":100} ``` ### 音乐管理 此记录仪支持通过手机 App 上传音乐到记录仪的存储卡,然后通过蓝牙进行播放控制,列举音乐接口: ```text GET /term?act=list_music HTTP/1.1 {"data":[{"fileSize":4008624,"id":0,"length":0,"md5":"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37","name":null,"path":"/mnt/media_rw/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/2/13/e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37.mp3","singer":null,"userUuid":"2F06A8D7CA314388B58DC46719702844","uuid":"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37"}],"elapsedTime":7,"message":"Success!","responseCode":100} ``` 查询最大音量(最大级别为 15): ```text GET /term?act=query_max_volume HTTP/1.1 {"data":15,"elapsedTime":5,"message":null,"responseCode":100} ``` 查看当前音量: ```text GET /term?act=query_volume HTTP/1.1 {"data":10,"elapsedTime":9,"message":"15 15","responseCode":100} ``` ### 视频管理 首先按小时段列举视频,请忽略不正确的系统时间: ```text GET /term?act=list_video_hour HTTP/1.1 {"data":["20000115230000","20000116000000","20000116010000","20000116020000","20000116030000"],"elapsedTime":171,"message":null,"responseCode":100} ``` 指定开始和结束的小时时间段列举视频: ```text GET /term?act=list_poi&start_time=20000116030000&end_time=20000116040000 HTTP/1.1 {"data":[],"elapsedTime":9,"message":null,"responseCode":100} ``` 列举某个小时的所有视频,可以看到存储卡里视频的完整路径: ```text GET /term?act=list_video_db&hour_time=20000116030000 HTTP/1.1 {"data":[{"createTime":"20000116030127","id":42,"length":120009,"lockType":0,"name":"2000-01-16_03-01-27.mp4","path":"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-01-27.mp4","sn":null,"uuid":"a6035028ded14482936c1580aba8e298","videoType":0},{"createTime":"20000116030327","id":43,"length":119940,"lockType":0,"name":"2000-01-16_03-03-27.mp4","path":"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-03-27.mp4","sn":null,"uuid":"291a4796c1274dfea99a978c84ca33d0","videoType":0}],"elapsedTime":986,"message":null,"responseCode":100} ``` 获取某个视频文件的封面截图,此请求直接返回 jpg 图像数据: ```text GET /term?act=snapshot&name=2000-01-16_03-21-26.mp4&time=0 HTTP/1.1 HTTP/1.1 200 OK Content-Type: image/jpeg Accept-Ranges: bytes Content-Length: 42351 Content-Disposition: attachment;filename=2000-01-16_03-19-26_0.jpg Server: Jetty(i-jetty 1.0.27) ``` 指定开始和结束时间段获取历史轨迹,由于记录仪的 GPS 是一直开启的,还好轨迹还算比较准确,这里轨迹数据比较长我就不详细贴出来了: ```text GET /term?act=query_history_track&start_time=20181013180104&end_time=20181013180304&len=121 HTTP/1.1 ``` 最后是最关键的下载录像接口,通过 `name` 参数指定视频文件名即可: ```text GET /term?act=get_video&name=2000-01-16_03-19-26.mp4 HTTP/1.1 User-Agent: Lavf/57.56.100 Accept: */* Range: bytes=0- Connection: close Host: 192.168.43.1:8080 Icy-MetaData: 1 HTTP/1.1 206 Partial Content Content-Type: application/octet-stream Accept-Ranges: bytes Content-Length: 61722040 Content-Disposition: attachment;filename=2000-01-16_03-19-26.mp4 Content-Range: bytes 3104-61722039/61722040 Connection: close Server: Jetty(i-jetty 1.0.27) ``` 有点奇葩的就是这个下载录像的请求却并没有验证用户会话 ID,因此你也可以通过任何一款播放器指定地址直接远程回放录像: ```text http://192.168.43.1:8080/term?act=get_video&name=2000-01-16_03-19-26.mp4 ``` ## 实时视频接口 通过 tcpdump 抓包就会发现记录仪的实时视频预览是通过上面扫描出来的 8886 端口,记录仪提供 RTSP 形式的实时预览接口: ```text rtsp://192.168.43.1:8886?videoapi=mc2&transport=tcp ``` 需要注意的是记录仪自带的 RTSP 服务器似乎只支持 TCP 流传输,如果使用 VLC 播放器,则需要将 Live555 流传输选项改为:RTP over RTSP (TCP) 才能正常播放。 厂家可能出于预览流畅性考虑 RTSP 并没有直接提供 1080p 的视频,然而我通过 VLC 播放器进行 RTSP 实时预览还是存在两至三秒的延时,在 Chromebook 下切换使用 [VXG Media Player](https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni) 插件进行实时预览则不存在延时问题。 ## 总结 目前该行车记录仪使用下来还存在几个主要问题: * SIM 卡无法联网的情况下系统时间没办法修改; * 手机 App 提供的记录仪通过扫码连接外部 Wi-Fi 或者手机热点的功能无法工作,如果可以的话系统时间同步应该就不是问题; * 记录仪硬件上有降噪麦克风,而且支持通过蓝牙的语音对讲功能(只用于目前手机 App 里基本不可用的结伴出行组队),但是视频录像却不支持音频录制,同样 RTSP 实时视频中也没有带音频流。 这些问题只能后面有空的话再抽时间看看能否进入记录仪的 Android 系统进行修改了,祝大家玩的开心。
json metadata{"tags":["kubo","recorder","cn"],"links":["http://z.jd.com/project/details/42623.html","https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkkubo
permlinkandroid
title初探酷走Android行车记录仪
Transaction InfoBlock #27267689/Trx f7817c5191b4c4eaab8d45fd13e892a01e30b3d6
View Raw JSON Data
{
  "block": 27267689,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## 酷走记录仪\n\n博客有段时间没有更新了,提前说好这不是一般的行车记录仪评测文章,我现在开的 SUV 上并没有装记录仪,只是同事刚好送了一个厂商已倒闭的记录仪,看到这款采用 Intel Atom 处理器的行车记录仪有点兴趣,准备初步研究下。\n\n酷走行车记录仪由深圳汉普云联科技生产,具体型号为 KZV201,网上关于此行车记录仪的评测文章还是有一些的,之前 [京东众筹](http://z.jd.com/project/details/42623.html) 上的链接应该还在,想了解的朋友们可以看看。\n\n这里我就不做具体介绍了,外观图也就不上了。初步了解此记录仪使用 Intel SOFIA Atom x3 处理器,因此集成了 3G 上网功能(带 SIM 卡插槽),支持 2.4 GHz Wi-Fi 网络、蓝牙 4.0、GPS 及 FM 发射功能。酷走记录仪虽然号称支持 1080p 全高清视频录制,然而实际测试录像效果也是比较一般,另外采用 Android 5.0 系统也挺少见。\n\n由于机身没有任何物理按键,所有功能都要通过手机 App 连接行车记录仪 Wi-Fi 热点来完成,这也是相当坑的地方:由于厂家已经不提供支持更新了,目前手机 App 里不少功能缺失,最基本的 SIM 卡(专用物联网卡)数据流量充值功能都不能使用;手机 App 连修改记录仪系统时间的功能都没有提供(可能厂家考虑的是自动通过 SIM 卡数据流量进行时间同步),导致目前记录仪的时间都不正确。\n\n## Web 接口使用\n\n为了摆脱随时可能会完全崩掉的手机 App,我就需要知道基本的管理接口和视频调阅接口。\n\n记录仪自带的 Wi-Fi 热点使用固定的 `192.168.43.1` IP 地址(别指望能修改了),手机或者电脑连接酷走的 Wi-Fi 热点之后就可以 ping 通记录仪地址了,下面我贴出来的例子都是在 Chromebook 上测试的,首先我用 Linux 自带的 `nc` 命令来扫一下记录仪开放的端口:\n\n```bash\n(xenial)zzm@localhost:~$ nc -znv 192.168.43.1 20-20000 2>&1 | grep 'succeeded'\nConnection to 192.168.43.1 53 port [tcp/*] succeeded!\nConnection to 192.168.43.1 5556 port [tcp/*] succeeded!\nConnection to 192.168.43.1 8080 port [tcp/*] succeeded!\nConnection to 192.168.43.1 8886 port [tcp/*] succeeded!\n```\n\n显然 `53` 是 Wi-Fi 热点自带的 DNS 服务器端口,看起来 `8080` 就是 Web 接口的端口了,我在安装了酷走 App 的手机上运行 tcpdump 程序进行抓包就可以分析 8080 端口的请求了。\n\n### 用户登录\n\n首先是用户登录请求:\n\n```bash\nchronos@localhost ~/Downloads $ curl -v \"http://192.168.43.1:8080/term?act=user_login&user=\\{%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:nu\nll,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22\\}&password=8888&termtype=3&force=false\"\n*   Trying 192.168.43.1...\n* TCP_NODELAY set\n* Connected to 192.168.43.1 (192.168.43.1) port 8080 (#0)\n> GET /term?act=user_login&user={%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:null,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22}&password=8888&termtype=3&force=false HTTP/1.1\n> Host: 192.168.43.1:8080\n> User-Agent: curl/7.60.0\n> Accept: */*\n> \n< HTTP/1.1 200 OK\n< Set-Cookie: JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi;Path=/\n< Set-Cookie: TONG_TOKEN_ID=a2664fc77f2d4c2abf90bea58e3bb6bf;Path=/;Expires=Tue, 29-Feb-2000 00:26:30 GMT\n< Expires: Thu, 01 Jan 1970 00:00:00 GMT\n< Content-Type: text/plain;charset=UTF-8\n< Access-Control-Allow-Methods: GET, POST\n< Access-Control-Allow-Credentials: true\n< Transfer-Encoding: chunked\n< Server: Jetty(i-jetty 1.0.27)\n< \n* Connection #0 to host 192.168.43.1 left intact\n{\"data\":\"a2664fc77f2d4c2abf90bea58e3bb6bf\",\"elapsedTime\":94,\"message\":\"鉴权通过!\",\"responseCode\":100}\n```\n\n> **注意**\n> \n> 上面 curl 命令中的括号做了转义防止 curl 请求错误,实际请求地址中并没有反斜杠,另外请求地址中的 `%22` 就是双引号,如果在浏览器中访问也可以直接把 `%22` 换成双引号。\n\n可以看到记录仪 Web 服务器是基于 Jetty 写的,GET 请求地址中的 `user` 和 `password` 参数不能缺少,`password` 就是记录仪的管理密码(默认:8888),`user` 参数是个 JSON 对象,基本所有参数都可以使用 `null`,除了 `uuid` 参数表示登录用户(可以随机生成一串 UUID),重复发送登录请求将会报错。\n\n最值得关注的是返回 Cookie 数据中的 `JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi` 值,后面所有 Web 请求都会用到这个会话 ID。\n\n### 查看设备信息\n\n基础设备信息也可以使用 curl 附带 `JSESSIONID` Cookie 值进行查询,下面其它的请求就不详细列出来了:\n\n```bash\ncurl -v -b \"JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi\" \"http://192.168.43.1:8080/term?act=device_info&appID=null\"\n```\n\n设备信息输出如下:\n\n```text\nGET /term?act=device_info&appID=null HTTP/1.1\nHost: 192.168.43.1:8080\nCookie: JSESSIONID=73jb4a4t1urf1un1krkwn6drr\n\nHTTP/1.1 200 OK\nContent-Type: text/plain;charset=UTF-8\nAccess-Control-Allow-Methods: GET, POST\nAccess-Control-Allow-Credentials: true\nTransfer-Encoding: chunked\nServer: Jetty(i-jetty 1.0.27)\n\n{\"data\":{\"hotName\":null,\"hotPassword\":null,\"hwVerion\":\"4\",\"imei\":\"XXXXXXX\",\"osVersion\":\"2.2.3\",\"password\":null,\"productCode\":null,\"randomCode\":null,\"sn\":\"KXXXXXX\",\"swVersion\":\"1.0.27\",\"videoPassword\":null},\"elapsedTime\":9,\"message\":null,\"responseCode\":100}\n```\n\n查看记录仪配置请求:\n\n```text\nGET /term?act=query_termconfiginfo HTTP/1.1\n\n{\"data\":{\"hotName\":\"KUZO_KXXXXXX\",\"hotPassword\":\"password\",\"hwVerion\":\"4\",\"imei\":\"XXXXXXX\",\"osVersion\":\"2.2.3\",\"password\":\"8888\",\"productCode\":null,\"randomCode\":null,\"sn\":\"KXXXXXX\",\"swVersion\":\"1.0.27\",\"videoPassword\":\"\"},\"elapsedTime\":14,\"message\":\"Success!\",\"responseCode\":100}\n```\n\n查看 SIM 卡 ICCID(返回值在 `data` 中):\n\n```text\nGET /term?act=query_sim_iccid HTTP/1.1\n\n{\"data\":\"XXXXXXX\",\"elapsedTime\":14,\"message\":null,\"responseCode\":100}\n```\n\n查看 FM 发射状态(这个 POST 请求没有附带任何参数):\n\n```text\nPOST /term?act=query_fm_open_status HTTP/1.1\nContent-Length: 0\n\n{\"data\":false,\"elapsedTime\":35,\"message\":null,\"responseCode\":100}\n```\n\n查看 GPS 状态:\n\n```text\nGET /term?act=query_gps_status HTTP/1.1\n\n{\"data\":{\"position\":{\"accuracy\":14.33,\"altitude\":15.030368,\"angle\":337.0591,\"crs\":1,\"gpsTime\":\"20181023180046\",\"latitude\":31.907135402870722,\"longitude\":118.77761111400555,\"speed\":0.0},\"positionFix\":true,\"positionType\":1,\"satellitesInUse\":5,\"satellitesInView\":6},\"elapsedTime\":3,\"message\":null,\"responseCode\":100}\n```\n\n可以看到这里能直接查询到 GPS 的当前经纬度以及精确度,还能看到卫星的使用情况,比较讽刺的是这边 GPS 时间都得到了,记录仪系统在网络不可用的情况下却没考虑使用 GPS 时间。\n\n查询存储状态(装了 TF 卡之后就能看到存储卡容量状态了):\n\n```text\nGET /term?act=query_storage_info HTTP/1.1\n\n{\"data\":{\"TFExist\":true,\"TFTotalCapacity\":63847890944,\"TFUsedCapacity\":6595575808,\"internalTotalCapacity\":3918114816,\"internalUsedCapacity\":2259316736},\"elapsedTime\":3,\"message\":null,\"responseCode\":100}\n```\n\n查询 3G 移动网络状态:\n\n```text\nGET /term?act=query_3g_status HTTP/1.1\n\n{\"data\":{\"dialUpSuccess\":false,\"intensity\":0,\"mnc\":\"01\",\"type\":\"3G\"},\"elapsedTime\":19,\"message\":null,\"responseCode\":100}\n```\n\n### 音乐管理\n\n此记录仪支持通过手机 App 上传音乐到记录仪的存储卡,然后通过蓝牙进行播放控制,列举音乐接口:\n\n```text\nGET /term?act=list_music HTTP/1.1\n\n{\"data\":[{\"fileSize\":4008624,\"id\":0,\"length\":0,\"md5\":\"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37\",\"name\":null,\"path\":\"/mnt/media_rw/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/2/13/e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37.mp3\",\"singer\":null,\"userUuid\":\"2F06A8D7CA314388B58DC46719702844\",\"uuid\":\"e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37\"}],\"elapsedTime\":7,\"message\":\"Success!\",\"responseCode\":100}\n```\n\n查询最大音量(最大级别为 15):\n\n```text\nGET /term?act=query_max_volume HTTP/1.1\n\n{\"data\":15,\"elapsedTime\":5,\"message\":null,\"responseCode\":100}\n```\n\n查看当前音量:\n\n```text\nGET /term?act=query_volume HTTP/1.1\n\n{\"data\":10,\"elapsedTime\":9,\"message\":\"15 15\",\"responseCode\":100}\n```\n\n### 视频管理\n\n首先按小时段列举视频,请忽略不正确的系统时间:\n\n```text\nGET /term?act=list_video_hour HTTP/1.1\n\n{\"data\":[\"20000115230000\",\"20000116000000\",\"20000116010000\",\"20000116020000\",\"20000116030000\"],\"elapsedTime\":171,\"message\":null,\"responseCode\":100}\n```\n\n指定开始和结束的小时时间段列举视频:\n\n```text\nGET /term?act=list_poi&start_time=20000116030000&end_time=20000116040000 HTTP/1.1\n\n{\"data\":[],\"elapsedTime\":9,\"message\":null,\"responseCode\":100}\n```\n\n列举某个小时的所有视频,可以看到存储卡里视频的完整路径:\n\n```text\nGET /term?act=list_video_db&hour_time=20000116030000 HTTP/1.1\n\n{\"data\":[{\"createTime\":\"20000116030127\",\"id\":42,\"length\":120009,\"lockType\":0,\"name\":\"2000-01-16_03-01-27.mp4\",\"path\":\"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-01-27.mp4\",\"sn\":null,\"uuid\":\"a6035028ded14482936c1580aba8e298\",\"videoType\":0},{\"createTime\":\"20000116030327\",\"id\":43,\"length\":119940,\"lockType\":0,\"name\":\"2000-01-16_03-03-27.mp4\",\"path\":\"/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-03-27.mp4\",\"sn\":null,\"uuid\":\"291a4796c1274dfea99a978c84ca33d0\",\"videoType\":0}],\"elapsedTime\":986,\"message\":null,\"responseCode\":100}\n```\n\n获取某个视频文件的封面截图,此请求直接返回 jpg 图像数据:\n\n```text\nGET /term?act=snapshot&name=2000-01-16_03-21-26.mp4&time=0 HTTP/1.1\n\nHTTP/1.1 200 OK\nContent-Type: image/jpeg\nAccept-Ranges: bytes\nContent-Length: 42351\nContent-Disposition: attachment;filename=2000-01-16_03-19-26_0.jpg\nServer: Jetty(i-jetty 1.0.27)\n```\n\n指定开始和结束时间段获取历史轨迹,由于记录仪的 GPS 是一直开启的,还好轨迹还算比较准确,这里轨迹数据比较长我就不详细贴出来了:\n\n```text\nGET /term?act=query_history_track&start_time=20181013180104&end_time=20181013180304&len=121 HTTP/1.1\n```\n\n最后是最关键的下载录像接口,通过 `name` 参数指定视频文件名即可:\n\n```text\nGET /term?act=get_video&name=2000-01-16_03-19-26.mp4 HTTP/1.1\nUser-Agent: Lavf/57.56.100\nAccept: */*\nRange: bytes=0-\nConnection: close\nHost: 192.168.43.1:8080\nIcy-MetaData: 1\n\nHTTP/1.1 206 Partial Content\nContent-Type: application/octet-stream\nAccept-Ranges: bytes\nContent-Length: 61722040\nContent-Disposition: attachment;filename=2000-01-16_03-19-26.mp4\nContent-Range: bytes 3104-61722039/61722040\nConnection: close\nServer: Jetty(i-jetty 1.0.27)\n```\n\n有点奇葩的就是这个下载录像的请求却并没有验证用户会话 ID,因此你也可以通过任何一款播放器指定地址直接远程回放录像:\n\n```text\nhttp://192.168.43.1:8080/term?act=get_video&name=2000-01-16_03-19-26.mp4\n```\n\n## 实时视频接口\n\n通过 tcpdump 抓包就会发现记录仪的实时视频预览是通过上面扫描出来的 8886 端口,记录仪提供 RTSP 形式的实时预览接口:\n\n```text\nrtsp://192.168.43.1:8886?videoapi=mc2&transport=tcp\n```\n\n需要注意的是记录仪自带的 RTSP 服务器似乎只支持 TCP 流传输,如果使用 VLC 播放器,则需要将 Live555 流传输选项改为:RTP over RTSP (TCP) 才能正常播放。\n\n厂家可能出于预览流畅性考虑 RTSP 并没有直接提供 1080p 的视频,然而我通过 VLC 播放器进行 RTSP 实时预览还是存在两至三秒的延时,在 Chromebook 下切换使用 [VXG Media Player](https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni) 插件进行实时预览则不存在延时问题。\n\n## 总结\n\n目前该行车记录仪使用下来还存在几个主要问题:\n\n* SIM 卡无法联网的情况下系统时间没办法修改;\n* 手机 App 提供的记录仪通过扫码连接外部 Wi-Fi 或者手机热点的功能无法工作,如果可以的话系统时间同步应该就不是问题;\n* 记录仪硬件上有降噪麦克风,而且支持通过蓝牙的语音对讲功能(只用于目前手机 App 里基本不可用的结伴出行组队),但是视频录像却不支持音频录制,同样 RTSP 实时视频中也没有带音频流。\n\n这些问题只能后面有空的话再抽时间看看能否进入记录仪的 Android 系统进行修改了,祝大家玩的开心。",
      "json_metadata": "{\"tags\":[\"kubo\",\"recorder\",\"cn\"],\"links\":[\"http://z.jd.com/project/details/42623.html\",\"https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "kubo",
      "permlink": "android",
      "title": "初探酷走Android行车记录仪"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-10-30T18:12:06",
  "trx_id": "f7817c5191b4c4eaab8d45fd13e892a01e30b3d6",
  "trx_in_block": 37,
  "virtual_op": 0
}
2018/08/30 18:37:36
authorsteemitboard
bodyCongratulations @zohead! You have completed the following achievement on Steemit and have been rewarded with new badge(s) : [![](https://steemitimages.com/70x70/http://steemitboard.com/notifications/firstcommented.png)](http://steemitboard.com/@zohead) You got a First Reply <sub>_Click on the badge to view your Board of Honor._</sub> <sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub> > Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorzohead
parent permlinklinux-elf-glibc
permlinksteemitboard-notify-zohead-20180830t183735000z
title
Transaction InfoBlock #25528262/Trx 777afee7a103559df4afe957d1d90a09065f5a2e
View Raw JSON Data
{
  "block": 25528262,
  "op": [
    "comment",
    {
      "author": "steemitboard",
      "body": "Congratulations @zohead! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :\n\n[![](https://steemitimages.com/70x70/http://steemitboard.com/notifications/firstcommented.png)](http://steemitboard.com/@zohead) You got a First Reply\n\n<sub>_Click on the badge to view your Board of Honor._</sub>\n<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>\n\n\n\n> Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "zohead",
      "parent_permlink": "linux-elf-glibc",
      "permlink": "steemitboard-notify-zohead-20180830t183735000z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-30T18:37:36",
  "trx_id": "777afee7a103559df4afe957d1d90a09065f5a2e",
  "trx_in_block": 3,
  "virtual_op": 0
}
2018/08/30 13:13:00
authorhannahwu
body哈哈 还有人关注我的博客么?
json metadata{"tags":["food"],"app":"steemit/0.1"}
parent authorzohead
parent permlinkre-hannahwu-6weejs-20180814t040108775z
permlinkre-zohead-re-hannahwu-6weejs-20180830t131259156z
title
Transaction InfoBlock #25521774/Trx f615497e8b30cf0a526cf56a3afc30d4e08583cc
View Raw JSON Data
{
  "block": 25521774,
  "op": [
    "comment",
    {
      "author": "hannahwu",
      "body": "哈哈 还有人关注我的博客么?",
      "json_metadata": "{\"tags\":[\"food\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "zohead",
      "parent_permlink": "re-hannahwu-6weejs-20180814t040108775z",
      "permlink": "re-zohead-re-hannahwu-6weejs-20180830t131259156z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-30T13:13:00",
  "trx_id": "f615497e8b30cf0a526cf56a3afc30d4e08583cc",
  "trx_in_block": 33,
  "virtual_op": 0
}
zoheadreceived 0.057 STEEM, 0.072 SP author reward for @zohead / acer-chromebook-14
2018/08/20 16:16:18
authorzohead
permlinkacer-chromebook-14
sbd payout0.000 SBD
steem payout0.057 STEEM
vesting payout117.428030 VESTS
Transaction InfoBlock #25237581/Virtual Operation #5
View Raw JSON Data
{
  "block": 25237581,
  "op": [
    "author_reward",
    {
      "author": "zohead",
      "permlink": "acer-chromebook-14",
      "sbd_payout": "0.000 SBD",
      "steem_payout": "0.057 STEEM",
      "vesting_payout": "117.428030 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-20T16:16:18",
  "trx_id": "0000000000000000000000000000000000000000",
  "trx_in_block": 4294967295,
  "virtual_op": 5
}
zoheadupvoted (100.00%) @michaelleeld / 4hufsv-1
2018/08/16 15:14:42
authormichaelleeld
permlink4hufsv-1
voterzohead
weight10000 (100.00%)
Transaction InfoBlock #25121197/Trx 02537e48060f2baef76d588e3df4b659b0af44e4
View Raw JSON Data
{
  "block": 25121197,
  "op": [
    "vote",
    {
      "author": "michaelleeld",
      "permlink": "4hufsv-1",
      "voter": "zohead",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T15:14:42",
  "trx_id": "02537e48060f2baef76d588e3df4b659b0af44e4",
  "trx_in_block": 1,
  "virtual_op": 0
}
2018/08/16 14:47:09
authorzohead
body想起在镇江冒雨看音乐节的二货日子了。。 ^_^
json metadata{"tags":["cn"],"app":"steemit/0.1"}
parent authorwuxing
parent permlink5562nl
permlinkre-wuxing-5562nl-20180816t144707024z
title
Transaction InfoBlock #25120646/Trx 9b290d28b272b254f8f5293edee37400200a0791
View Raw JSON Data
{
  "block": 25120646,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "想起在镇江冒雨看音乐节的二货日子了。。 ^_^",
      "json_metadata": "{\"tags\":[\"cn\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "wuxing",
      "parent_permlink": "5562nl",
      "permlink": "re-wuxing-5562nl-20180816t144707024z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:47:09",
  "trx_id": "9b290d28b272b254f8f5293edee37400200a0791",
  "trx_in_block": 46,
  "virtual_op": 0
}
2018/08/16 14:39:09
idfollow
json["follow",{"follower":"zohead","following":"michaelleeld","what":["blog"]}]
required auths[]
required posting auths["zohead"]
Transaction InfoBlock #25120486/Trx b2ab31b11a0a33d12743582d3525057d368bb505
View Raw JSON Data
{
  "block": 25120486,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"zohead\",\"following\":\"michaelleeld\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "zohead"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:39:09",
  "trx_id": "b2ab31b11a0a33d12743582d3525057d368bb505",
  "trx_in_block": 2,
  "virtual_op": 0
}
zoheadfollowed @wuxing
2018/08/16 14:24:21
idfollow
json["follow",{"follower":"zohead","following":"wuxing","what":["blog"]}]
required auths[]
required posting auths["zohead"]
Transaction InfoBlock #25120190/Trx 2f8f88bc40bf0543fd01f9edb8f2511a1acc5575
View Raw JSON Data
{
  "block": 25120190,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"zohead\",\"following\":\"wuxing\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "zohead"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:24:21",
  "trx_id": "2f8f88bc40bf0543fd01f9edb8f2511a1acc5575",
  "trx_in_block": 30,
  "virtual_op": 0
}
2018/08/16 14:09:09
authorzohead
permlinklinux-elf-glibc
voteralphabot
weight100 (1.00%)
Transaction InfoBlock #25119886/Trx 97a41f6552ac13a2e3a38b4aaa6a45120881b5a8
View Raw JSON Data
{
  "block": 25119886,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "linux-elf-glibc",
      "voter": "alphabot",
      "weight": 100
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:09:09",
  "trx_id": "97a41f6552ac13a2e3a38b4aaa6a45120881b5a8",
  "trx_in_block": 17,
  "virtual_op": 0
}
2018/08/16 14:08:42
authorzohead
permlinkacer-chromebook-14
voteralphabot
weight100 (1.00%)
Transaction InfoBlock #25119877/Trx e8341dae8bbb6bba456e5cd284c0ca51a642dba3
View Raw JSON Data
{
  "block": 25119877,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "acer-chromebook-14",
      "voter": "alphabot",
      "weight": 100
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:08:42",
  "trx_id": "e8341dae8bbb6bba456e5cd284c0ca51a642dba3",
  "trx_in_block": 40,
  "virtual_op": 0
}
zoheadpublished a new post: linux-elf-glibc
2018/08/16 14:08:42
authorzohead
body## Linux glibc 问题 相信有不少 Linux 用户都碰到过运行第三方(非系统自带软件源)发布的程序时的 glibc 兼容性问题,这一般是由于当前 Linux 系统上的 GNU C 库(glibc)版本比较老导致的,例如我在 CentOS 6 64 位系统上运行某第三方闭源软件时会报: ```bash [root@centos6-dev ~]# ldd tester ./tester: /lib64/libc.so.6: version `GLIBC_2.17' not found (required by ./tester) ./tester: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./tester) linux-vdso.so.1 => (0x00007ffe795fe000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc7d4c73000) libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007fc7d4a55000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fc7d4851000) libm.so.6 => /lib64/libm.so.6 (0x00007fc7d45cd000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc7d43b7000) libc.so.6 => /lib64/libc.so.6 (0x00007fc7d4023000) /lib64/ld-linux-x86-64.so.2 (0x00007fc7d4e90000) ``` CentOS 6 自带的 glibc 还是很老的 2.12 版本,而下载的第三方程序依赖 glibc 2.17 版本,这种情况要么自己重新编译程序,要么只能升级系统的 glibc 版本。由于我使用的程序是第三方编写并且是闭源软件无法自己编译,升级 glibc 固然可能能解决问题,但是 glibc 做为最核心的基础库,在生产环境上直接升级毕竟动作还是太大,因此希望还是能有更好的解决途径。 ## 问题分析 首先我们可以检查一下程序使用了新版本 glibc 的哪些符号,使用 `objdump` 命令可以查看 ELF 文件的动态符号信息: ```bash [root@centos6-dev ~]# objdump -T tester | grep GLIBC_2.1.* 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.17 clock_gettime ``` 从上面的输出可以看到程序使用了 glibc 2.14 版本的 `memcpy` 函数和 glibc 2.17 版本的 `clock_gettime` 函数,而这两个常用的函数按说应该是 glibc 很早就已经支持了的,我们可以确认一下当前系统 glibc 提供的符号版本: ```bash [root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep memcpy 0000000000091300 w DF .text 0000000000000009 GLIBC_2.2.5 wmemcpy 0000000000101070 g DF .text 000000000000001b GLIBC_2.4 __wmemcpy_chk 00000000000896b0 g DF .text 0000000000000465 GLIBC_2.2.5 memcpy 00000000000896a0 g DF .text 0000000000000009 GLIBC_2.3.4 __memcpy_chk [root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep clock_gettime 000000000038f800 g DO .bss 0000000000000008 GLIBC_PRIVATE __vdso_clock_gettime ``` 这里可以看出 CentOS 6 的 glibc 库提供的 `memcpy` 实现是 2.2.5 版本的,另外 `libc` 没有直接实现 `clock_gettime` 函数,因为老版本 glibc 里 `clock_gettime` 是由 `librt` 库提供 `clock_gettime` 支持的,而且同样也是 2.2.5 版本: ```bash [root@centos6-dev ~]# objdump -T /lib64/librt.so.1 | grep clock_gettime 0000000000000000 DO *UND* 0000000000000000 GLIBC_PRIVATE __vdso_clock_gettime 0000000000003e70 g DF .text 000000000000008b GLIBC_2.2.5 clock_gettime ``` 看过这里就基本明白了,第三方程序的开发者是在自带新版本 glibc 的 Linux 系统上编译的,`memcpy` 和 `clock_gettime` 的实现默认使用了该系统上 glibc 所提供的最新版本,这样在低版本 glibc 系统中就无法正常运行。 ## 解决方法 虽然我们无法重新编译第三方程序,但如果可以修改 ELF 文件强制让 LD 库加载程序时使用老版本的 `memcpy` 和 `clock_gettime` 实现,应该就可以避免升级 glibc。 ### 分析 ELF 首先用 `readelf` 命令查看 ELF 的符号表,由于该命令输出非常多,这里只贴出我们关心的信息: ```bash [root@centos6-dev ~]# readelf -sV tester Symbol table '.dynsym' contains 4583 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND ...... 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (5) ...... 67: 0000000000000000 0 FUNC GLOBAL DEFAULT UND clock_gettime@GLIBC_2.17 (16) ...... 4582: 0000000000794260 70 FUNC WEAK DEFAULT 12 _ZNSt15basic_streambufIwS Version symbols section '.gnu.version' contains 4583 entries: Addr: 000000000045b508 Offset: 0x05b508 Link: 4 (.dynsym) 000: 0 (*local*) 0 (*local*) 2 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 004: 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 008: 4 (GLIBC_2.3.2) 3 (GLIBC_2.2.5) 0 (*local*) 5 (GLIBC_2.14) ...... 040: 2 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 10 (GLIBC_2.17) ...... 11e0: 1 (*global*) 1 (*global*) 1 (*global*) 1 (*global*) 11e4: 1 (*global*) 1 (*global*) 1 (*global*) Version needs section '.gnu.version_r' contains 6 entries: Addr: 0x000000000045d8d8 Offset: 0x05d8d8 Link: 5 (.dynstr) 000000: Version: 1 File: ld-linux-x86-64.so.2 Cnt: 1 0x0010: Name: GLIBC_2.3 Flags: none Version: 17 0x0020: Version: 1 File: libgcc_s.so.1 Cnt: 3 0x0030: Name: GCC_3.0 Flags: none Version: 13 0x0040: Name: GCC_3.3 Flags: none Version: 11 0x0050: Name: GCC_4.2.0 Flags: none Version: 10 0x0060: Version: 1 File: libm.so.6 Cnt: 1 0x0070: Name: GLIBC_2.2.5 Flags: none Version: 8 0x0080: Version: 1 File: libpthread.so.0 Cnt: 2 0x0090: Name: GLIBC_2.3.2 Flags: none Version: 15 0x00a0: Name: GLIBC_2.2.5 Flags: none Version: 7 0x00b0: Version: 1 File: libc.so.6 Cnt: 10 0x00c0: Name: GLIBC_2.8 Flags: none Version: 19 0x00d0: Name: GLIBC_2.9 Flags: none Version: 18 0x00e0: Name: GLIBC_2.17 Flags: none Version: 16 0x00f0: Name: GLIBC_2.4 Flags: none Version: 14 0x0100: Name: GLIBC_2.3.4 Flags: none Version: 12 0x0110: Name: GLIBC_2.3 Flags: none Version: 9 0x0120: Name: GLIBC_2.7 Flags: none Version: 6 0x0130: Name: GLIBC_2.14 Flags: none Version: 5 0x0140: Name: GLIBC_2.3.2 Flags: none Version: 4 0x0150: Name: GLIBC_2.2.5 Flags: none Version: 3 0x0160: Version: 1 File: libdl.so.2 Cnt: 1 0x0170: Name: GLIBC_2.2.5 Flags: none Version: 2 ``` 我们可以在 ELF 的 `.dynsym` 动态符号表中看到程序用于动态链接的所有导入导出符号,`memcpy` 和 `clock_gettime` 后面括号里的数字就是十进制的版本号(分别为 `5` 和 `16`),而我们需要格外关注的是下面的 `.gnu.version` 和 `.gnu.version_r` 符号版本信息段。 `.gnu.version` 表包含所有动态符号的版本信息,`.dynsym` 动态符号表中的每个符号都可以在 `.gnu.version` 中看到对应的条目(`.dynsym` 中一共 4583 个符号刚好与 `.gnu.version` 的结束位置 0x11e7 相等)。 从上面的输出可以看到 `.gnu.version` 表从 `0x05b508` 偏移量开始,我们可以看看对应偏移量的十六进制数据: ```text Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0005B500 00 00 00 00 02 00 03 00 ........ 0005B510 03 00 03 00 03 00 03 00 04 00 03 00 00 00 05 00 ................ 0005B520 03 00 03 00 06 00 00 00 03 00 07 00 08 00 08 00 ................ 0005B530 03 00 09 00 03 00 03 00 0A 00 07 00 03 00 00 00 ................ 0005B540 03 00 03 00 0B 00 07 00 03 00 03 00 00 00 07 00 ................ 0005B550 00 00 03 00 03 00 03 00 03 00 0C 00 09 00 00 00 ................ 0005B560 07 00 03 00 03 00 07 00 03 00 07 00 0C 00 00 00 ................ 0005B570 0D 00 03 00 07 00 07 00 0E 00 0F 00 03 00 0D 00 ................ 0005B580 03 00 03 00 03 00 03 00 02 00 03 00 03 00 10 00 ................ 0005B590 03 00 00 00 03 00 07 00 08 00 07 00 07 00 03 00 ................ 0005B5A0 03 00 0D 00 03 00 00 00 03 00 03 00 03 00 00 00 ................ ``` `.gnu.version` 中的每个条目占用两个字节,其值为符号的版本,由此可以看到其中第 0x0b 个符号(也就是 `.dynsym` 表中的 `memcpy@GLIBC_2.14` 符号)的偏移量即为 0x05b51e(0x05b508 + 0x0b x 2),该偏移量的值 0x0005 也刚好和 `.dynsym` 表中的值对应,当然 `clock_gettime` 符号对应的偏移量 0x05b58e 的值 0x0010 同样也是如此。 下面关键的 `.gnu.version_r` 表示二进制程序实际依赖的库文件版本,从输出中也能看到 `.gnu.version_r` 表是按照不同的库文件进行分段显示的,每个条目占用 0x10 也就是 16 个字节,该表是从 `0x05d8d8` 偏移量开始,我们看看 `GLIBC_2.17` 也就是 `0x05d9b8` 处的十六进制数据: ```text Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0005D9B0 B0 70 03 00 10 00 00 00 97 91 96 06 00 00 10 00 °p......—‘–..... 0005D9C0 BA 70 03 00 10 00 00 00 14 69 69 0D 00 00 0E 00 ºp.......ii..... 0005D9D0 C5 70 03 00 10 00 00 00 74 19 69 09 00 00 0C 00 Åp......t.i..... 0005D9E0 CF 70 03 00 10 00 00 00 13 69 69 0D 00 00 09 00 Ïp.......ii..... 0005D9F0 6A 70 03 00 10 00 00 00 17 69 69 0D 00 00 06 00 jp.......ii..... 0005DA00 DB 70 03 00 10 00 00 00 94 91 96 06 00 00 05 00 Ûp......”‘–..... 0005DA10 E5 70 03 00 10 00 00 00 72 19 69 09 00 00 04 00 åp......r.i..... 0005DA20 9A 70 03 00 10 00 00 00 75 1A 69 09 00 00 03 00 šp......u.i..... 0005DA30 8E 70 03 00 00 00 00 00 01 00 01 00 D8 03 00 00 Žp..........Ø... ``` `.gnu.version_r` 表中每个条目是 16 个字节的 `Elfxx_Vernaux` 结构体,其声明如下(`Elfxx_Half` 占用 2 个字节,`Elfxx_Word` 占用 4 个字节): ```c typedef struct { Elfxx_Word vna_hash; Elfxx_Half vna_flags; Elfxx_Half vna_other; Elfxx_Word vna_name; Elfxx_Word vna_next; } Elfxx_Vernaux; ``` `vna_hash` 为 4 个字节的库名称(也就是上面的 `GLIBC_2.17` 字符串)的 hash 值,`vna_other` 为对应的 `.gnu.version` 表中符号的版本值,`vna_name` 指向库名称字符串的偏移量(也可以在 ELF 头中找到),`vna_next` 为下一个条目的位置(一般固定为 `0x00000010`)。 由上面的输出我们可以看到 `GLIBC_2.17` 对应的 `0x05d9b8` 处的开始的 4 个字节 `vna_hash` hash 值为 `0x06969197`,而 `vna_other` 的值 `0x0010`(输出里的 `Version: 16`)也与 `.gnu.version` 中 `clock_gettime` 符号的值一致。同样 `GLIBC_2.14` 也与 `memcpy` 符号的值相符。 ### 修改 ELF 符号表 由于 Linux 系统中的 LD 库(也就是 `/lib64/ld-linux-x86-64.so.2` 库)加载 ELF 时检查 `.gnu.version_r` 表中的符号,我们可以使用任何一款十六进制编辑器来修改 `.gnu.version_r` 表中的符号值来强制使用老版本的函数实现。 首先我们发现 `.gnu.version_r` 的 `libc.so.6` 段下面有 10 个条目,最后一个则是我们需要的 `GLIBC_2.2.5` 版本的符号(从上面的十六进制输出中我们可以看到该符号的偏移量为 `0x05da28`,`vna_hash` 值为 `0x09691A75`,`vna_other` 版本值为 `0x0003`,`vna_name` 字符串名称指向 `0003708E` 地址),因为这样我们才可以在不修改 ELF 文件大小的前提下直接将 `libc.so.6` 段下的其它高版本条目指向老版本条目的值。 例如 `GLIBC_2.17` 对应的 `0x05d9b8` 偏移量,我们可以直接将 `vna_hash` 值改为 `GLIBC_2.2.5` 的 `0x09691A75` 值,将 `vna_other` 改为 `0003708E` 值,为了保持和 `.gnu.version` 表中的版本值一致,这里我们就不修改 `vna_other` 值了。 对于 `GLIBC_2.14` 偏移量我们也修改成同样的值,修改保存之后的 ELF 文件再使用 `readelf` 命令检查就能看到变化了(只列出了修改的 `.gnu.version-r` 表): ```bash [root@centos6-dev ~]# readelf -sV tester ...... Version needs section '.gnu.version_r' contains 6 entries: Addr: 0x000000000045d8d8 Offset: 0x05e8d8 Link: 2 (.dynstr) 000000: Version: 1 File: ld-linux-x86-64.so.2 Cnt: 1 0x0010: Name: GLIBC_2.3 Flags: none Version: 17 0x0020: Version: 1 File: libgcc_s.so.1 Cnt: 3 0x0030: Name: GCC_3.0 Flags: none Version: 13 0x0040: Name: GCC_3.3 Flags: none Version: 11 0x0050: Name: GCC_4.2.0 Flags: none Version: 10 0x0060: Version: 1 File: libm.so.6 Cnt: 1 0x0070: Name: GLIBC_2.2.5 Flags: none Version: 8 0x0080: Version: 1 File: libpthread.so.0 Cnt: 2 0x0090: Name: GLIBC_2.3.2 Flags: none Version: 15 0x00a0: Name: GLIBC_2.2.5 Flags: none Version: 7 0x00b0: Version: 1 File: libc.so.6 Cnt: 10 0x00c0: Name: GLIBC_2.8 Flags: none Version: 19 0x00d0: Name: GLIBC_2.9 Flags: none Version: 18 0x00e0: Name: GLIBC_2.2.5 Flags: none Version: 16 0x00f0: Name: GLIBC_2.4 Flags: none Version: 14 0x0100: Name: GLIBC_2.3.4 Flags: none Version: 12 0x0110: Name: GLIBC_2.3 Flags: none Version: 9 0x0120: Name: GLIBC_2.7 Flags: none Version: 6 0x0130: Name: GLIBC_2.2.5 Flags: none Version: 5 0x0140: Name: GLIBC_2.3.2 Flags: none Version: 4 0x0150: Name: GLIBC_2.2.5 Flags: none Version: 3 0x0160: Version: 1 File: libdl.so.2 Cnt: 1 0x0170: Name: GLIBC_2.2.5 Flags: none Version: 2 ``` ### patchelf 修改 ELF 文件 一般的程序如果只使用了高版本 `memcpy` 的话,一般这样修改之后程序就可以运行了。但不巧我使用的第三方程序还使用了高版本 glibc 中的 `clock_gettime`,只是这样修改的话由于 CentOS 6 的 `libc` 2.12 库并没有提供 `clock_gettime`,运行时还是会报错。 这个时候我们就需要请出大杀器 [PatchELF](https://github.com/NixOS/patchelf) 了,这个小工具由 NixOS 团队开发,可以直接增加、删除、替换 ELF 文件依赖的库文件,使用起来也非常简单。 检出 PatchELF 的源代码,按照 GitHub 仓库上介绍的步骤编译安装就可以使用了(一般发行版自带的 patchelf 工具版本较老不支持一些新的功能)。 虽然 CentOS 6 的 `libc` 库没有提供 `clock_gettime` 实现,但好在 glibc 自带的 `librt` 库里还是提供了的,因此我们可以使用 patchelf 工具修改原版的程序文件,让程序优先加载 `librt` 库,这样程序就能正确加载 `clock_gettime` 符号了: ```bash [root@centos6-dev ~]# patchelf --add-needed librt.so.1 tester ``` 然后按照上面介绍的方法用十六进制编辑器修改新生成的 ELF 文件的 `.gnu.version_r` 表(因为 patchelf 运行之后新 ELF 文件的符号表就和之前的不一样了),将 `GLIBC_2.17` 和 `GLIBC_2.14` 统一改为 `GLIBC_2.2.5` 符号,保存 ELF 文件之后就可以看到效果了: ```bash [root@centos6-dev ~]# ldd tester linux-vdso.so.1 => (0x00007fffc17ee000) librt.so.1 => /lib64/librt.so.1 (0x00007f7f84dca000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7f84bad000) libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007f7f8498f000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f7f8478b000) libm.so.6 => /lib64/libm.so.6 (0x00007f7f84507000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f7f842f1000) libc.so.6 => /lib64/libc.so.6 (0x00007f7f83f5d000) /lib64/ld-linux-x86-64.so.2 (0x00007f7f84fd2000) ``` 从 `ldd` 命令的输出中可以看到修改后的程序会加载 `librt` 库,而且也没有 glibc 版本的报错了,经过测试程序运行起来也没有问题了。
json metadata{"tags":["glibc","elf","linux","cn"],"links":["https://github.com/NixOS/patchelf"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkglibc
permlinklinux-elf-glibc
titleLinux修改ELF解决glibc兼容性问题
Transaction InfoBlock #25119877/Trx 8a8328be8648a179b348adfef87cd65ea502e921
View Raw JSON Data
{
  "block": 25119877,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## Linux glibc 问题\n\n相信有不少 Linux 用户都碰到过运行第三方(非系统自带软件源)发布的程序时的 glibc 兼容性问题,这一般是由于当前 Linux 系统上的 GNU C 库(glibc)版本比较老导致的,例如我在 CentOS 6 64 位系统上运行某第三方闭源软件时会报:\n\n```bash\n[root@centos6-dev ~]# ldd tester\n./tester: /lib64/libc.so.6: version `GLIBC_2.17' not found (required by ./tester)\n./tester: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./tester)\n        linux-vdso.so.1 =>  (0x00007ffe795fe000)\n        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc7d4c73000)\n        libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007fc7d4a55000)\n        libdl.so.2 => /lib64/libdl.so.2 (0x00007fc7d4851000)\n        libm.so.6 => /lib64/libm.so.6 (0x00007fc7d45cd000)\n        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc7d43b7000)\n        libc.so.6 => /lib64/libc.so.6 (0x00007fc7d4023000)\n        /lib64/ld-linux-x86-64.so.2 (0x00007fc7d4e90000)\n```\n\nCentOS 6 自带的 glibc 还是很老的 2.12 版本,而下载的第三方程序依赖 glibc 2.17 版本,这种情况要么自己重新编译程序,要么只能升级系统的 glibc 版本。由于我使用的程序是第三方编写并且是闭源软件无法自己编译,升级 glibc 固然可能能解决问题,但是 glibc 做为最核心的基础库,在生产环境上直接升级毕竟动作还是太大,因此希望还是能有更好的解决途径。\n\n## 问题分析\n\n首先我们可以检查一下程序使用了新版本 glibc 的哪些符号,使用 `objdump` 命令可以查看 ELF 文件的动态符号信息:\n\n```bash\n[root@centos6-dev ~]# objdump -T tester | grep GLIBC_2.1.*\n0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy\n0000000000000000      DF *UND*  0000000000000000  GLIBC_2.17  clock_gettime\n```\n\n从上面的输出可以看到程序使用了 glibc 2.14 版本的 `memcpy` 函数和 glibc 2.17 版本的 `clock_gettime` 函数,而这两个常用的函数按说应该是 glibc 很早就已经支持了的,我们可以确认一下当前系统 glibc 提供的符号版本:\n\n```bash\n[root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep memcpy\n0000000000091300  w   DF .text  0000000000000009  GLIBC_2.2.5 wmemcpy\n0000000000101070 g    DF .text  000000000000001b  GLIBC_2.4   __wmemcpy_chk\n00000000000896b0 g    DF .text  0000000000000465  GLIBC_2.2.5 memcpy\n00000000000896a0 g    DF .text  0000000000000009  GLIBC_2.3.4 __memcpy_chk\n[root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep clock_gettime\n000000000038f800 g    DO .bss   0000000000000008  GLIBC_PRIVATE __vdso_clock_gettime\n```\n\n这里可以看出 CentOS 6 的 glibc 库提供的 `memcpy` 实现是 2.2.5 版本的,另外 `libc` 没有直接实现 `clock_gettime` 函数,因为老版本 glibc 里 `clock_gettime` 是由 `librt` 库提供 `clock_gettime` 支持的,而且同样也是 2.2.5 版本:\n\n```bash\n[root@centos6-dev ~]# objdump -T /lib64/librt.so.1 | grep clock_gettime\n0000000000000000      DO *UND*  0000000000000000  GLIBC_PRIVATE __vdso_clock_gettime\n0000000000003e70 g    DF .text  000000000000008b  GLIBC_2.2.5 clock_gettime\n```\n\n看过这里就基本明白了,第三方程序的开发者是在自带新版本 glibc 的 Linux 系统上编译的,`memcpy` 和 `clock_gettime` 的实现默认使用了该系统上 glibc 所提供的最新版本,这样在低版本 glibc 系统中就无法正常运行。\n\n## 解决方法\n\n虽然我们无法重新编译第三方程序,但如果可以修改 ELF 文件强制让 LD 库加载程序时使用老版本的 `memcpy` 和 `clock_gettime` 实现,应该就可以避免升级 glibc。\n\n### 分析 ELF\n\n首先用 `readelf` 命令查看 ELF 的符号表,由于该命令输出非常多,这里只贴出我们关心的信息:\n\n```bash\n[root@centos6-dev ~]# readelf -sV tester\n\nSymbol table '.dynsym' contains 4583 entries:\n   Num:    Value          Size Type    Bind   Vis      Ndx Name\n     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND\n    ......\n    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcpy@GLIBC_2.14 (5)\n    ......\n    67: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND clock_gettime@GLIBC_2.17 (16)\n    ......\n  4582: 0000000000794260    70 FUNC    WEAK   DEFAULT   12 _ZNSt15basic_streambufIwS\n\nVersion symbols section '.gnu.version' contains 4583 entries:\n Addr: 000000000045b508  Offset: 0x05b508  Link: 4 (.dynsym)\n  000:   0 (*local*)       0 (*local*)       2 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)\n  004:   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)\n  008:   4 (GLIBC_2.3.2)   3 (GLIBC_2.2.5)   0 (*local*)       5 (GLIBC_2.14)\n  ......\n  040:   2 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)  10 (GLIBC_2.17)\n  ......\n  11e0:   1 (*global*)      1 (*global*)      1 (*global*)      1 (*global*)\n  11e4:   1 (*global*)      1 (*global*)      1 (*global*)\n\nVersion needs section '.gnu.version_r' contains 6 entries:\n Addr: 0x000000000045d8d8  Offset: 0x05d8d8  Link: 5 (.dynstr)\n  000000: Version: 1  File: ld-linux-x86-64.so.2  Cnt: 1\n  0x0010:   Name: GLIBC_2.3  Flags: none  Version: 17\n  0x0020: Version: 1  File: libgcc_s.so.1  Cnt: 3\n  0x0030:   Name: GCC_3.0  Flags: none  Version: 13\n  0x0040:   Name: GCC_3.3  Flags: none  Version: 11\n  0x0050:   Name: GCC_4.2.0  Flags: none  Version: 10\n  0x0060: Version: 1  File: libm.so.6  Cnt: 1\n  0x0070:   Name: GLIBC_2.2.5  Flags: none  Version: 8\n  0x0080: Version: 1  File: libpthread.so.0  Cnt: 2\n  0x0090:   Name: GLIBC_2.3.2  Flags: none  Version: 15\n  0x00a0:   Name: GLIBC_2.2.5  Flags: none  Version: 7\n  0x00b0: Version: 1  File: libc.so.6  Cnt: 10\n  0x00c0:   Name: GLIBC_2.8  Flags: none  Version: 19\n  0x00d0:   Name: GLIBC_2.9  Flags: none  Version: 18\n  0x00e0:   Name: GLIBC_2.17  Flags: none  Version: 16\n  0x00f0:   Name: GLIBC_2.4  Flags: none  Version: 14\n  0x0100:   Name: GLIBC_2.3.4  Flags: none  Version: 12\n  0x0110:   Name: GLIBC_2.3  Flags: none  Version: 9\n  0x0120:   Name: GLIBC_2.7  Flags: none  Version: 6\n  0x0130:   Name: GLIBC_2.14  Flags: none  Version: 5\n  0x0140:   Name: GLIBC_2.3.2  Flags: none  Version: 4\n  0x0150:   Name: GLIBC_2.2.5  Flags: none  Version: 3\n  0x0160: Version: 1  File: libdl.so.2  Cnt: 1\n  0x0170:   Name: GLIBC_2.2.5  Flags: none  Version: 2\n```\n\n我们可以在 ELF 的 `.dynsym` 动态符号表中看到程序用于动态链接的所有导入导出符号,`memcpy` 和 `clock_gettime` 后面括号里的数字就是十进制的版本号(分别为 `5` 和 `16`),而我们需要格外关注的是下面的 `.gnu.version` 和 `.gnu.version_r` 符号版本信息段。\n\n`.gnu.version` 表包含所有动态符号的版本信息,`.dynsym` 动态符号表中的每个符号都可以在 `.gnu.version` 中看到对应的条目(`.dynsym` 中一共 4583 个符号刚好与 `.gnu.version` 的结束位置 0x11e7 相等)。\n\n从上面的输出可以看到 `.gnu.version` 表从 `0x05b508` 偏移量开始,我们可以看看对应偏移量的十六进制数据:\n\n```text\nOffset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n\n0005B500                          00 00 00 00 02 00 03 00          ........\n0005B510  03 00 03 00 03 00 03 00 04 00 03 00 00 00 05 00  ................\n0005B520  03 00 03 00 06 00 00 00 03 00 07 00 08 00 08 00  ................\n0005B530  03 00 09 00 03 00 03 00 0A 00 07 00 03 00 00 00  ................\n0005B540  03 00 03 00 0B 00 07 00 03 00 03 00 00 00 07 00  ................\n0005B550  00 00 03 00 03 00 03 00 03 00 0C 00 09 00 00 00  ................\n0005B560  07 00 03 00 03 00 07 00 03 00 07 00 0C 00 00 00  ................\n0005B570  0D 00 03 00 07 00 07 00 0E 00 0F 00 03 00 0D 00  ................\n0005B580  03 00 03 00 03 00 03 00 02 00 03 00 03 00 10 00  ................\n0005B590  03 00 00 00 03 00 07 00 08 00 07 00 07 00 03 00  ................\n0005B5A0  03 00 0D 00 03 00 00 00 03 00 03 00 03 00 00 00  ................\n```\n\n`.gnu.version` 中的每个条目占用两个字节,其值为符号的版本,由此可以看到其中第 0x0b 个符号(也就是 `.dynsym` 表中的 `memcpy@GLIBC_2.14` 符号)的偏移量即为 0x05b51e(0x05b508 + 0x0b x 2),该偏移量的值 0x0005 也刚好和 `.dynsym` 表中的值对应,当然 `clock_gettime` 符号对应的偏移量 0x05b58e 的值 0x0010 同样也是如此。\n\n下面关键的 `.gnu.version_r` 表示二进制程序实际依赖的库文件版本,从输出中也能看到 `.gnu.version_r` 表是按照不同的库文件进行分段显示的,每个条目占用 0x10 也就是 16 个字节,该表是从 `0x05d8d8` 偏移量开始,我们看看 `GLIBC_2.17` 也就是 `0x05d9b8` 处的十六进制数据:\n\n```text\nOffset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n\n0005D9B0  B0 70 03 00 10 00 00 00 97 91 96 06 00 00 10 00  °p......—‘–.....\n0005D9C0  BA 70 03 00 10 00 00 00 14 69 69 0D 00 00 0E 00  ºp.......ii.....\n0005D9D0  C5 70 03 00 10 00 00 00 74 19 69 09 00 00 0C 00  Åp......t.i.....\n0005D9E0  CF 70 03 00 10 00 00 00 13 69 69 0D 00 00 09 00  Ïp.......ii.....\n0005D9F0  6A 70 03 00 10 00 00 00 17 69 69 0D 00 00 06 00  jp.......ii.....\n0005DA00  DB 70 03 00 10 00 00 00 94 91 96 06 00 00 05 00  Ûp......”‘–.....\n0005DA10  E5 70 03 00 10 00 00 00 72 19 69 09 00 00 04 00  åp......r.i.....\n0005DA20  9A 70 03 00 10 00 00 00 75 1A 69 09 00 00 03 00  šp......u.i.....\n0005DA30  8E 70 03 00 00 00 00 00 01 00 01 00 D8 03 00 00  Žp..........Ø...\n```\n\n`.gnu.version_r` 表中每个条目是 16 个字节的 `Elfxx_Vernaux` 结构体,其声明如下(`Elfxx_Half` 占用 2 个字节,`Elfxx_Word` 占用 4 个字节):\n\n```c\ntypedef struct {\n\tElfxx_Word    vna_hash;\n\tElfxx_Half    vna_flags;\n\tElfxx_Half    vna_other;\n\tElfxx_Word    vna_name;\n\tElfxx_Word    vna_next;\n} Elfxx_Vernaux;\n```\n\n`vna_hash` 为 4 个字节的库名称(也就是上面的 `GLIBC_2.17` 字符串)的 hash 值,`vna_other` 为对应的 `.gnu.version` 表中符号的版本值,`vna_name` 指向库名称字符串的偏移量(也可以在 ELF 头中找到),`vna_next` 为下一个条目的位置(一般固定为 `0x00000010`)。\n\n由上面的输出我们可以看到 `GLIBC_2.17` 对应的 `0x05d9b8` 处的开始的 4 个字节 `vna_hash` hash 值为 `0x06969197`,而 `vna_other` 的值 `0x0010`(输出里的 `Version: 16`)也与 `.gnu.version` 中 `clock_gettime` 符号的值一致。同样 `GLIBC_2.14` 也与 `memcpy` 符号的值相符。\n\n### 修改 ELF 符号表\n\n由于 Linux 系统中的 LD 库(也就是 `/lib64/ld-linux-x86-64.so.2` 库)加载 ELF 时检查 `.gnu.version_r` 表中的符号,我们可以使用任何一款十六进制编辑器来修改 `.gnu.version_r` 表中的符号值来强制使用老版本的函数实现。\n\n首先我们发现 `.gnu.version_r` 的 `libc.so.6` 段下面有 10 个条目,最后一个则是我们需要的 `GLIBC_2.2.5` 版本的符号(从上面的十六进制输出中我们可以看到该符号的偏移量为 `0x05da28`,`vna_hash` 值为 `0x09691A75`,`vna_other` 版本值为 `0x0003`,`vna_name` 字符串名称指向 `0003708E` 地址),因为这样我们才可以在不修改 ELF 文件大小的前提下直接将 `libc.so.6` 段下的其它高版本条目指向老版本条目的值。\n\n例如 `GLIBC_2.17` 对应的 `0x05d9b8` 偏移量,我们可以直接将 `vna_hash` 值改为 `GLIBC_2.2.5` 的 `0x09691A75` 值,将 `vna_other` 改为 `0003708E` 值,为了保持和 `.gnu.version` 表中的版本值一致,这里我们就不修改 `vna_other` 值了。\n\n对于 `GLIBC_2.14` 偏移量我们也修改成同样的值,修改保存之后的 ELF 文件再使用 `readelf` 命令检查就能看到变化了(只列出了修改的 `.gnu.version-r` 表):\n\n```bash\n[root@centos6-dev ~]# readelf -sV tester\n......\nVersion needs section '.gnu.version_r' contains 6 entries:\n Addr: 0x000000000045d8d8  Offset: 0x05e8d8  Link: 2 (.dynstr)\n  000000: Version: 1  File: ld-linux-x86-64.so.2  Cnt: 1\n  0x0010:   Name: GLIBC_2.3  Flags: none  Version: 17\n  0x0020: Version: 1  File: libgcc_s.so.1  Cnt: 3\n  0x0030:   Name: GCC_3.0  Flags: none  Version: 13\n  0x0040:   Name: GCC_3.3  Flags: none  Version: 11\n  0x0050:   Name: GCC_4.2.0  Flags: none  Version: 10\n  0x0060: Version: 1  File: libm.so.6  Cnt: 1\n  0x0070:   Name: GLIBC_2.2.5  Flags: none  Version: 8\n  0x0080: Version: 1  File: libpthread.so.0  Cnt: 2\n  0x0090:   Name: GLIBC_2.3.2  Flags: none  Version: 15\n  0x00a0:   Name: GLIBC_2.2.5  Flags: none  Version: 7\n  0x00b0: Version: 1  File: libc.so.6  Cnt: 10\n  0x00c0:   Name: GLIBC_2.8  Flags: none  Version: 19\n  0x00d0:   Name: GLIBC_2.9  Flags: none  Version: 18\n  0x00e0:   Name: GLIBC_2.2.5  Flags: none  Version: 16\n  0x00f0:   Name: GLIBC_2.4  Flags: none  Version: 14\n  0x0100:   Name: GLIBC_2.3.4  Flags: none  Version: 12\n  0x0110:   Name: GLIBC_2.3  Flags: none  Version: 9\n  0x0120:   Name: GLIBC_2.7  Flags: none  Version: 6\n  0x0130:   Name: GLIBC_2.2.5  Flags: none  Version: 5\n  0x0140:   Name: GLIBC_2.3.2  Flags: none  Version: 4\n  0x0150:   Name: GLIBC_2.2.5  Flags: none  Version: 3\n  0x0160: Version: 1  File: libdl.so.2  Cnt: 1\n  0x0170:   Name: GLIBC_2.2.5  Flags: none  Version: 2\n```\n\n### patchelf 修改 ELF 文件\n\n一般的程序如果只使用了高版本 `memcpy` 的话,一般这样修改之后程序就可以运行了。但不巧我使用的第三方程序还使用了高版本 glibc 中的 `clock_gettime`,只是这样修改的话由于 CentOS 6 的 `libc` 2.12 库并没有提供 `clock_gettime`,运行时还是会报错。\n\n这个时候我们就需要请出大杀器 [PatchELF](https://github.com/NixOS/patchelf) 了,这个小工具由 NixOS 团队开发,可以直接增加、删除、替换 ELF 文件依赖的库文件,使用起来也非常简单。\n\n检出 PatchELF 的源代码,按照 GitHub 仓库上介绍的步骤编译安装就可以使用了(一般发行版自带的 patchelf 工具版本较老不支持一些新的功能)。\n\n虽然 CentOS 6 的 `libc` 库没有提供 `clock_gettime` 实现,但好在 glibc 自带的 `librt` 库里还是提供了的,因此我们可以使用 patchelf 工具修改原版的程序文件,让程序优先加载 `librt` 库,这样程序就能正确加载 `clock_gettime` 符号了:\n\n```bash\n[root@centos6-dev ~]# patchelf --add-needed librt.so.1 tester\n```\n\n然后按照上面介绍的方法用十六进制编辑器修改新生成的 ELF 文件的 `.gnu.version_r` 表(因为 patchelf 运行之后新 ELF 文件的符号表就和之前的不一样了),将 `GLIBC_2.17` 和 `GLIBC_2.14` 统一改为 `GLIBC_2.2.5` 符号,保存 ELF 文件之后就可以看到效果了:\n\n```bash\n[root@centos6-dev ~]# ldd tester\n        linux-vdso.so.1 =>  (0x00007fffc17ee000)\n        librt.so.1 => /lib64/librt.so.1 (0x00007f7f84dca000)\n        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7f84bad000)\n        libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007f7f8498f000)\n        libdl.so.2 => /lib64/libdl.so.2 (0x00007f7f8478b000)\n        libm.so.6 => /lib64/libm.so.6 (0x00007f7f84507000)\n        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f7f842f1000)\n        libc.so.6 => /lib64/libc.so.6 (0x00007f7f83f5d000)\n        /lib64/ld-linux-x86-64.so.2 (0x00007f7f84fd2000)\n```\n\n从 `ldd` 命令的输出中可以看到修改后的程序会加载 `librt` 库,而且也没有 glibc 版本的报错了,经过测试程序运行起来也没有问题了。",
      "json_metadata": "{\"tags\":[\"glibc\",\"elf\",\"linux\",\"cn\"],\"links\":[\"https://github.com/NixOS/patchelf\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "glibc",
      "permlink": "linux-elf-glibc",
      "title": "Linux修改ELF解决glibc兼容性问题"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:08:42",
  "trx_id": "8a8328be8648a179b348adfef87cd65ea502e921",
  "trx_in_block": 32,
  "virtual_op": 0
}
zoheadpublished a new post: acer-chromebook-14
2018/08/16 14:08:18
authorzohead
body## Acer Chromebook 14 这两年非工作场合下我的主力电脑已经切换到 Chromebook,一直用的是 Dell Chromebook 11 这款小屏幕的笔记本,习惯了 Linux 之后 Chrome OS 用起来还是比较舒心的。 只是当我发现 Google 新推出的 Android Apps on Chrome OS 不再支持 Dell Chromebook 11 之类的老设备时(基本上所有使用赛扬 2955U 处理器的 Chromebook 都被排除了,具体可以参考 [Chrome OS Systems Supporting Android Apps](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps)),虽然觉得有点不爽,但还是下定决心准备换新的 Chromebook 设备了。 刚好最近看到一款之前关注过的 Acer Chromebook 14 笔记本,确认在 Play 商店支持的设备列表中就有了兴趣了,配置虽然和老的 Dell Chromebook 11 看起来差不了多少,但主要机身也更轻薄了,屏幕也升级到 1080p 了,立马准备拿下。 ## 基本配置 简单列一下配置: |项目|内容| |--|--| |型号|CB3-431-C0AK| |CPU|Intel® Celeron® N3160 1.6 GHz| |内存|LPDDR3 4GB| |存储|32GB eMMC 闪存| |屏幕|14 寸 IPS 1920x1080| |核显|Intel® HD Graphics 400| |无线|802.11a/b/g/n/ac| |接口|2x USB 3.0、HDMI| 其实除了 eMMC 闪存速度一般之外,其它方面我还是比较满意的,由于内存和闪存都是焊在主板上的,也基本没有升级的可能了,当然如果能像 Dell Chromebook 11 一样能自带读卡器就更好了。 ## 外观 先看看 Acer Chromebook 14 的正面和背面: ![Acer Chromebook 14 正面](https://storage.live.com/items/F799A497307A642E!6495?a.jpg) ![Acer Chromebook 14 背面](https://storage.live.com/items/F799A497307A642E!6496?a.jpg) 这款 Chromebook 直接没有风扇,全金属的机身设计的很轻薄,两面都异常简单。 再看看打开后的 B 面和 C 面: ![Acer Chromebook 14 B 面和 C 面](https://storage.live.com/items/F799A497307A642E!6497?a.jpg) 支持多点触控的触控板占了 C 面很大的面积,由于 Chromebook 对于多点触控现在支持的也非常好了,我用 Chromebook 时基本都不用鼠标了,键盘相比原来的 Dell Chromebook 11 手感也更好了: ![Acer Chromebook 14 触控板和键盘](https://storage.live.com/items/F799A497307A642E!6499?a.jpg) 充电孔和耳机插孔都在机身右侧,该 Chromebook 的所有接口都在机身左侧,下面的图也可以看看键盘效果: ![Acer Chromebook 14 接口](https://storage.live.com/items/F799A497307A642E!6498?a.jpg) ## 使用体验 Acer Chromebook 14 拿上手开机之后还是感觉比原来的 Dell Chromebook 11 屏幕改进挺明显的,老一批 Chromebook 备受诟病的屏幕可视角度问题在这里就基本没有了,只是我发现 1080p 的屏幕默认选择的“最优”分辨率是 1536x864。 我把分辨率改为 1920x1080 的话 Chrome OS 的显示体验就比较差了,网页字体、标题栏、工具栏都显得特别小;这种情况下只能将缩放调至 125% 或者 150%,不过即使这样也只是网页显示正常了,标题栏和工具栏并没有跟进缩放。这样的显示效果甚至还不及 Windows 10 高分屏设备上的 Chrome 浏览器,鉴于 Chrome OS 对高分屏的支持也比较天残,我也只能保持默认“最优”分辨率了。 Chrome OS 必备的爬墙激活这里就不说了,激活成功之后一般就可以领取 90 天的 Play Music 和两年 100 GB 的 Google Drive 空间了。无意外的话连上网络就会有系统更新了,更新到最新的 Chrome OS 64 稳定版本,系统就已经自带新的 Play 商店支持了。 在设置中开启 Play 商店之后,如果你的梯子没什么问题的话应该就可以打开 Play 商店安装常用的 App 了,Chrome OS 自带的 Android 容器为 7.1.1 版本,相比原来的 Chrome ARC 运行时来说 App 的兼容性还是有质的提升的。 我专门测试了网易云音乐、手机 QQ 等几个原来 Chrome ARC 无法运行的 App,在新的 Android 容器中运行都没有什么问题,起码 Chromebook 上使用 QQ 除了 crouton 中运行 Wine QQ 之外终于有直接的解决方案了。 按照我平常基本用 Chromebook 浏览网页,偶尔看点 HTML5 视频的使用场景,这款笔记本还是基本能达到标称的 12 小时续航时间的,这也是 Chrome OS 相对 Windows 等系统的一大优势了。 Acer Chromebook 14 这一段时间的总体使用体验还是比较令人满意的,碰到一个未解决的小问题就是原来通过 crouton 安装的 Ubuntu 14.04 系统无法以 xorg 方式正常启动图形界面了,只能以 [xiwi](https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)) 窗口形式启动图形界面,更新 crouton 也没有解决,不过还好这个影响不是很大咯。
json metadata{"tags":["acer","chromebook","chromeos","cn"],"image":["https://storage.live.com/items/F799A497307A642E!6495?a.jpg","https://storage.live.com/items/F799A497307A642E!6496?a.jpg","https://storage.live.com/items/F799A497307A642E!6497?a.jpg","https://storage.live.com/items/F799A497307A642E!6499?a.jpg","https://storage.live.com/items/F799A497307A642E!6498?a.jpg"],"links":["https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps","https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkacer
permlinkacer-chromebook-14
title新玩具 Acer Chromebook 14
Transaction InfoBlock #25119869/Trx d03fb9224e02679dfe04942c7b9a922bf28fab21
View Raw JSON Data
{
  "block": 25119869,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## Acer Chromebook 14\n\n这两年非工作场合下我的主力电脑已经切换到 Chromebook,一直用的是 Dell Chromebook 11 这款小屏幕的笔记本,习惯了 Linux 之后 Chrome OS 用起来还是比较舒心的。\n\n只是当我发现 Google 新推出的 Android Apps on Chrome OS 不再支持 Dell Chromebook 11 之类的老设备时(基本上所有使用赛扬 2955U 处理器的 Chromebook 都被排除了,具体可以参考 [Chrome OS Systems Supporting Android Apps](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps)),虽然觉得有点不爽,但还是下定决心准备换新的 Chromebook 设备了。\n\n刚好最近看到一款之前关注过的 Acer Chromebook 14 笔记本,确认在 Play 商店支持的设备列表中就有了兴趣了,配置虽然和老的 Dell Chromebook 11 看起来差不了多少,但主要机身也更轻薄了,屏幕也升级到 1080p 了,立马准备拿下。\n\n## 基本配置\n\n简单列一下配置:\n\n|项目|内容|\n|--|--|\n|型号|CB3-431-C0AK|\n|CPU|Intel® Celeron® N3160 1.6 GHz|\n|内存|LPDDR3 4GB|\n|存储|32GB eMMC 闪存|\n|屏幕|14 寸 IPS 1920x1080|\n|核显|Intel® HD Graphics 400|\n|无线|802.11a/b/g/n/ac|\n|接口|2x USB 3.0、HDMI|\n\n其实除了 eMMC 闪存速度一般之外,其它方面我还是比较满意的,由于内存和闪存都是焊在主板上的,也基本没有升级的可能了,当然如果能像 Dell Chromebook 11 一样能自带读卡器就更好了。\n\n## 外观\n\n先看看 Acer Chromebook 14 的正面和背面:\n\n![Acer Chromebook 14 正面](https://storage.live.com/items/F799A497307A642E!6495?a.jpg)\n\n![Acer Chromebook 14 背面](https://storage.live.com/items/F799A497307A642E!6496?a.jpg)\n\n这款 Chromebook 直接没有风扇,全金属的机身设计的很轻薄,两面都异常简单。\n\n再看看打开后的 B 面和 C 面:\n\n![Acer Chromebook 14 B 面和 C 面](https://storage.live.com/items/F799A497307A642E!6497?a.jpg)\n\n支持多点触控的触控板占了 C 面很大的面积,由于 Chromebook 对于多点触控现在支持的也非常好了,我用 Chromebook 时基本都不用鼠标了,键盘相比原来的 Dell Chromebook 11 手感也更好了:\n\n![Acer Chromebook 14 触控板和键盘](https://storage.live.com/items/F799A497307A642E!6499?a.jpg)\n\n充电孔和耳机插孔都在机身右侧,该 Chromebook 的所有接口都在机身左侧,下面的图也可以看看键盘效果:\n\n![Acer Chromebook 14 接口](https://storage.live.com/items/F799A497307A642E!6498?a.jpg)\n\n## 使用体验\n\nAcer Chromebook 14 拿上手开机之后还是感觉比原来的 Dell Chromebook 11 屏幕改进挺明显的,老一批 Chromebook 备受诟病的屏幕可视角度问题在这里就基本没有了,只是我发现 1080p 的屏幕默认选择的“最优”分辨率是 1536x864。\n\n我把分辨率改为 1920x1080 的话 Chrome OS 的显示体验就比较差了,网页字体、标题栏、工具栏都显得特别小;这种情况下只能将缩放调至 125% 或者 150%,不过即使这样也只是网页显示正常了,标题栏和工具栏并没有跟进缩放。这样的显示效果甚至还不及 Windows 10 高分屏设备上的 Chrome 浏览器,鉴于 Chrome OS 对高分屏的支持也比较天残,我也只能保持默认“最优”分辨率了。\n\nChrome OS 必备的爬墙激活这里就不说了,激活成功之后一般就可以领取 90 天的 Play Music 和两年 100 GB 的 Google Drive 空间了。无意外的话连上网络就会有系统更新了,更新到最新的 Chrome OS 64 稳定版本,系统就已经自带新的 Play 商店支持了。\n\n在设置中开启 Play 商店之后,如果你的梯子没什么问题的话应该就可以打开 Play 商店安装常用的 App 了,Chrome OS 自带的 Android 容器为 7.1.1 版本,相比原来的 Chrome ARC 运行时来说 App 的兼容性还是有质的提升的。\n\n我专门测试了网易云音乐、手机 QQ 等几个原来 Chrome ARC 无法运行的 App,在新的 Android 容器中运行都没有什么问题,起码 Chromebook 上使用 QQ 除了 crouton 中运行 Wine QQ 之外终于有直接的解决方案了。\n\n按照我平常基本用 Chromebook 浏览网页,偶尔看点 HTML5 视频的使用场景,这款笔记本还是基本能达到标称的 12 小时续航时间的,这也是 Chrome OS 相对 Windows 等系统的一大优势了。\n\nAcer Chromebook 14 这一段时间的总体使用体验还是比较令人满意的,碰到一个未解决的小问题就是原来通过 crouton 安装的 Ubuntu 14.04 系统无法以 xorg 方式正常启动图形界面了,只能以 [xiwi](https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)) 窗口形式启动图形界面,更新 crouton 也没有解决,不过还好这个影响不是很大咯。",
      "json_metadata": "{\"tags\":[\"acer\",\"chromebook\",\"chromeos\",\"cn\"],\"image\":[\"https://storage.live.com/items/F799A497307A642E!6495?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6496?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6497?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6499?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6498?a.jpg\"],\"links\":[\"https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps\",\"https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "acer",
      "permlink": "acer-chromebook-14",
      "title": "新玩具 Acer Chromebook 14"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:08:18",
  "trx_id": "d03fb9224e02679dfe04942c7b9a922bf28fab21",
  "trx_in_block": 26,
  "virtual_op": 0
}
2018/08/16 14:05:06
authorzohead
permlinklinux-elf-glibc
voterfastresteem
weight100 (1.00%)
Transaction InfoBlock #25119805/Trx 9d6b8c9d5fe36d49b8456be9122be89088de50f5
View Raw JSON Data
{
  "block": 25119805,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "linux-elf-glibc",
      "voter": "fastresteem",
      "weight": 100
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:05:06",
  "trx_id": "9d6b8c9d5fe36d49b8456be9122be89088de50f5",
  "trx_in_block": 34,
  "virtual_op": 0
}
zoheadpublished a new post: linux-elf-glibc
2018/08/16 14:04:36
authorzohead
body## Linux glibc 问题 相信有不少 Linux 用户都碰到过运行第三方(非系统自带软件源)发布的程序时的 glibc 兼容性问题,这一般是由于当前 Linux 系统上的 GNU C 库(glibc)版本比较老导致的,例如我在 CentOS 6 64 位系统上运行某第三方闭源软件时会报: ```bash [root@centos6-dev ~]# ldd tester ./tester: /lib64/libc.so.6: version `GLIBC_2.17' not found (required by ./tester) ./tester: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./tester) linux-vdso.so.1 => (0x00007ffe795fe000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc7d4c73000) libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007fc7d4a55000) libdl.so.2 => /lib64/libdl.so.2 (0x00007fc7d4851000) libm.so.6 => /lib64/libm.so.6 (0x00007fc7d45cd000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc7d43b7000) libc.so.6 => /lib64/libc.so.6 (0x00007fc7d4023000) /lib64/ld-linux-x86-64.so.2 (0x00007fc7d4e90000) ``` CentOS 6 自带的 glibc 还是很老的 2.12 版本,而下载的第三方程序依赖 glibc 2.17 版本,这种情况要么自己重新编译程序,要么只能升级系统的 glibc 版本。由于我使用的程序是第三方编写并且是闭源软件无法自己编译,升级 glibc 固然可能能解决问题,但是 glibc 做为最核心的基础库,在生产环境上直接升级毕竟动作还是太大,因此希望还是能有更好的解决途径。 ## 问题分析 首先我们可以检查一下程序使用了新版本 glibc 的哪些符号,使用 `objdump` 命令可以查看 ELF 文件的动态符号信息: ```bash [root@centos6-dev ~]# objdump -T tester | grep GLIBC_2.1.* 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.17 clock_gettime ``` 从上面的输出可以看到程序使用了 glibc 2.14 版本的 `memcpy` 函数和 glibc 2.17 版本的 `clock_gettime` 函数,而这两个常用的函数按说应该是 glibc 很早就已经支持了的,我们可以确认一下当前系统 glibc 提供的符号版本: ```bash [root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep memcpy 0000000000091300 w DF .text 0000000000000009 GLIBC_2.2.5 wmemcpy 0000000000101070 g DF .text 000000000000001b GLIBC_2.4 __wmemcpy_chk 00000000000896b0 g DF .text 0000000000000465 GLIBC_2.2.5 memcpy 00000000000896a0 g DF .text 0000000000000009 GLIBC_2.3.4 __memcpy_chk [root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep clock_gettime 000000000038f800 g DO .bss 0000000000000008 GLIBC_PRIVATE __vdso_clock_gettime ``` 这里可以看出 CentOS 6 的 glibc 库提供的 `memcpy` 实现是 2.2.5 版本的,另外 `libc` 没有直接实现 `clock_gettime` 函数,因为老版本 glibc 里 `clock_gettime` 是由 `librt` 库提供 `clock_gettime` 支持的,而且同样也是 2.2.5 版本: ```bash [root@centos6-dev ~]# objdump -T /lib64/librt.so.1 | grep clock_gettime 0000000000000000 DO *UND* 0000000000000000 GLIBC_PRIVATE __vdso_clock_gettime 0000000000003e70 g DF .text 000000000000008b GLIBC_2.2.5 clock_gettime ``` 看过这里就基本明白了,第三方程序的开发者是在自带新版本 glibc 的 Linux 系统上编译的,`memcpy` 和 `clock_gettime` 的实现默认使用了该系统上 glibc 所提供的最新版本,这样在低版本 glibc 系统中就无法正常运行。 ## 解决方法 虽然我们无法重新编译第三方程序,但如果可以修改 ELF 文件强制让 LD 库加载程序时使用老版本的 `memcpy` 和 `clock_gettime` 实现,应该就可以避免升级 glibc。 ### 分析 ELF 首先用 `readelf` 命令查看 ELF 的符号表,由于该命令输出非常多,这里只贴出我们关心的信息: ```bash [root@centos6-dev ~]# readelf -sV tester Symbol table '.dynsym' contains 4583 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND ...... 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (5) ...... 67: 0000000000000000 0 FUNC GLOBAL DEFAULT UND clock_gettime@GLIBC_2.17 (16) ...... 4582: 0000000000794260 70 FUNC WEAK DEFAULT 12 _ZNSt15basic_streambufIwS Version symbols section '.gnu.version' contains 4583 entries: Addr: 000000000045b508 Offset: 0x05b508 Link: 4 (.dynsym) 000: 0 (*local*) 0 (*local*) 2 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 004: 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 008: 4 (GLIBC_2.3.2) 3 (GLIBC_2.2.5) 0 (*local*) 5 (GLIBC_2.14) ...... 040: 2 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5) 10 (GLIBC_2.17) ...... 11e0: 1 (*global*) 1 (*global*) 1 (*global*) 1 (*global*) 11e4: 1 (*global*) 1 (*global*) 1 (*global*) Version needs section '.gnu.version_r' contains 6 entries: Addr: 0x000000000045d8d8 Offset: 0x05d8d8 Link: 5 (.dynstr) 000000: Version: 1 File: ld-linux-x86-64.so.2 Cnt: 1 0x0010: Name: GLIBC_2.3 Flags: none Version: 17 0x0020: Version: 1 File: libgcc_s.so.1 Cnt: 3 0x0030: Name: GCC_3.0 Flags: none Version: 13 0x0040: Name: GCC_3.3 Flags: none Version: 11 0x0050: Name: GCC_4.2.0 Flags: none Version: 10 0x0060: Version: 1 File: libm.so.6 Cnt: 1 0x0070: Name: GLIBC_2.2.5 Flags: none Version: 8 0x0080: Version: 1 File: libpthread.so.0 Cnt: 2 0x0090: Name: GLIBC_2.3.2 Flags: none Version: 15 0x00a0: Name: GLIBC_2.2.5 Flags: none Version: 7 0x00b0: Version: 1 File: libc.so.6 Cnt: 10 0x00c0: Name: GLIBC_2.8 Flags: none Version: 19 0x00d0: Name: GLIBC_2.9 Flags: none Version: 18 0x00e0: Name: GLIBC_2.17 Flags: none Version: 16 0x00f0: Name: GLIBC_2.4 Flags: none Version: 14 0x0100: Name: GLIBC_2.3.4 Flags: none Version: 12 0x0110: Name: GLIBC_2.3 Flags: none Version: 9 0x0120: Name: GLIBC_2.7 Flags: none Version: 6 0x0130: Name: GLIBC_2.14 Flags: none Version: 5 0x0140: Name: GLIBC_2.3.2 Flags: none Version: 4 0x0150: Name: GLIBC_2.2.5 Flags: none Version: 3 0x0160: Version: 1 File: libdl.so.2 Cnt: 1 0x0170: Name: GLIBC_2.2.5 Flags: none Version: 2 ``` 我们可以在 ELF 的 `.dynsym` 动态符号表中看到程序用于动态链接的所有导入导出符号,`memcpy` 和 `clock_gettime` 后面括号里的数字就是十进制的版本号(分别为 `5` 和 `16`),而我们需要格外关注的是下面的 `.gnu.version` 和 `.gnu.version_r` 符号版本信息段。 `.gnu.version` 表包含所有动态符号的版本信息,`.dynsym` 动态符号表中的每个符号都可以在 `.gnu.version` 中看到对应的条目(`.dynsym` 中一共 4583 个符号刚好与 `.gnu.version` 的结束位置 0x11e7 相等)。 从上面的输出可以看到 `.gnu.version` 表从 `0x05b508` 偏移量开始,我们可以看看对应偏移量的十六进制数据: ```text Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0005B500 00 00 00 00 02 00 03 00 ........ 0005B510 03 00 03 00 03 00 03 00 04 00 03 00 00 00 05 00 ................ 0005B520 03 00 03 00 06 00 00 00 03 00 07 00 08 00 08 00 ................ 0005B530 03 00 09 00 03 00 03 00 0A 00 07 00 03 00 00 00 ................ 0005B540 03 00 03 00 0B 00 07 00 03 00 03 00 00 00 07 00 ................ 0005B550 00 00 03 00 03 00 03 00 03 00 0C 00 09 00 00 00 ................ 0005B560 07 00 03 00 03 00 07 00 03 00 07 00 0C 00 00 00 ................ 0005B570 0D 00 03 00 07 00 07 00 0E 00 0F 00 03 00 0D 00 ................ 0005B580 03 00 03 00 03 00 03 00 02 00 03 00 03 00 10 00 ................ 0005B590 03 00 00 00 03 00 07 00 08 00 07 00 07 00 03 00 ................ 0005B5A0 03 00 0D 00 03 00 00 00 03 00 03 00 03 00 00 00 ................ ``` `.gnu.version` 中的每个条目占用两个字节,其值为符号的版本,由此可以看到其中第 0x0b 个符号(也就是 `.dynsym` 表中的 `memcpy@GLIBC_2.14` 符号)的偏移量即为 0x05b51e(0x05b508 + 0x0b x 2),该偏移量的值 0x0005 也刚好和 `.dynsym` 表中的值对应,当然 `clock_gettime` 符号对应的偏移量 0x05b58e 的值 0x0010 同样也是如此。 下面关键的 `.gnu.version_r` 表示二进制程序实际依赖的库文件版本,从输出中也能看到 `.gnu.version_r` 表是按照不同的库文件进行分段显示的,每个条目占用 0x10 也就是 16 个字节,该表是从 `0x05d8d8` 偏移量开始,我们看看 `GLIBC_2.17` 也就是 `0x05d9b8` 处的十六进制数据: ```text Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0005D9B0 B0 70 03 00 10 00 00 00 97 91 96 06 00 00 10 00 °p......—‘–..... 0005D9C0 BA 70 03 00 10 00 00 00 14 69 69 0D 00 00 0E 00 ºp.......ii..... 0005D9D0 C5 70 03 00 10 00 00 00 74 19 69 09 00 00 0C 00 Åp......t.i..... 0005D9E0 CF 70 03 00 10 00 00 00 13 69 69 0D 00 00 09 00 Ïp.......ii..... 0005D9F0 6A 70 03 00 10 00 00 00 17 69 69 0D 00 00 06 00 jp.......ii..... 0005DA00 DB 70 03 00 10 00 00 00 94 91 96 06 00 00 05 00 Ûp......”‘–..... 0005DA10 E5 70 03 00 10 00 00 00 72 19 69 09 00 00 04 00 åp......r.i..... 0005DA20 9A 70 03 00 10 00 00 00 75 1A 69 09 00 00 03 00 šp......u.i..... 0005DA30 8E 70 03 00 00 00 00 00 01 00 01 00 D8 03 00 00 Žp..........Ø... ``` `.gnu.version_r` 表中每个条目是 16 个字节的 `Elfxx_Vernaux` 结构体,其声明如下(`Elfxx_Half` 占用 2 个字节,`Elfxx_Word` 占用 4 个字节): ```c typedef struct { Elfxx_Word vna_hash; Elfxx_Half vna_flags; Elfxx_Half vna_other; Elfxx_Word vna_name; Elfxx_Word vna_next; } Elfxx_Vernaux; ``` `vna_hash` 为 4 个字节的库名称(也就是上面的 `GLIBC_2.17` 字符串)的 hash 值,`vna_other` 为对应的 `.gnu.version` 表中符号的版本值,`vna_name` 指向库名称字符串的偏移量(也可以在 ELF 头中找到),`vna_next` 为下一个条目的位置(一般固定为 `0x00000010`)。 由上面的输出我们可以看到 `GLIBC_2.17` 对应的 `0x05d9b8` 处的开始的 4 个字节 `vna_hash` hash 值为 `0x06969197`,而 `vna_other` 的值 `0x0010`(输出里的 `Version: 16`)也与 `.gnu.version` 中 `clock_gettime` 符号的值一致。同样 `GLIBC_2.14` 也与 `memcpy` 符号的值相符。 ### 修改 ELF 符号表 由于 Linux 系统中的 LD 库(也就是 `/lib64/ld-linux-x86-64.so.2` 库)加载 ELF 时检查 `.gnu.version_r` 表中的符号,我们可以使用任何一款十六进制编辑器来修改 `.gnu.version_r` 表中的符号值来强制使用老版本的函数实现。 首先我们发现 `.gnu.version_r` 的 `libc.so.6` 段下面有 10 个条目,最后一个则是我们需要的 `GLIBC_2.2.5` 版本的符号(从上面的十六进制输出中我们可以看到该符号的偏移量为 `0x05da28`,`vna_hash` 值为 `0x09691A75`,`vna_other` 版本值为 `0x0003`,`vna_name` 字符串名称指向 `0003708E` 地址),因为这样我们才可以在不修改 ELF 文件大小的前提下直接将 `libc.so.6` 段下的其它高版本条目指向老版本条目的值。 例如 `GLIBC_2.17` 对应的 `0x05d9b8` 偏移量,我们可以直接将 `vna_hash` 值改为 `GLIBC_2.2.5` 的 `0x09691A75` 值,将 `vna_other` 改为 `0003708E` 值,为了保持和 `.gnu.version` 表中的版本值一致,这里我们就不修改 `vna_other` 值了。 对于 `GLIBC_2.14` 偏移量我们也修改成同样的值,修改保存之后的 ELF 文件再使用 `readelf` 命令检查就能看到变化了(只列出了修改的 `.gnu.version-r` 表): ```bash [root@centos6-dev ~]# readelf -sV tester ...... Version needs section '.gnu.version_r' contains 6 entries: Addr: 0x000000000045d8d8 Offset: 0x05e8d8 Link: 2 (.dynstr) 000000: Version: 1 File: ld-linux-x86-64.so.2 Cnt: 1 0x0010: Name: GLIBC_2.3 Flags: none Version: 17 0x0020: Version: 1 File: libgcc_s.so.1 Cnt: 3 0x0030: Name: GCC_3.0 Flags: none Version: 13 0x0040: Name: GCC_3.3 Flags: none Version: 11 0x0050: Name: GCC_4.2.0 Flags: none Version: 10 0x0060: Version: 1 File: libm.so.6 Cnt: 1 0x0070: Name: GLIBC_2.2.5 Flags: none Version: 8 0x0080: Version: 1 File: libpthread.so.0 Cnt: 2 0x0090: Name: GLIBC_2.3.2 Flags: none Version: 15 0x00a0: Name: GLIBC_2.2.5 Flags: none Version: 7 0x00b0: Version: 1 File: libc.so.6 Cnt: 10 0x00c0: Name: GLIBC_2.8 Flags: none Version: 19 0x00d0: Name: GLIBC_2.9 Flags: none Version: 18 0x00e0: Name: GLIBC_2.2.5 Flags: none Version: 16 0x00f0: Name: GLIBC_2.4 Flags: none Version: 14 0x0100: Name: GLIBC_2.3.4 Flags: none Version: 12 0x0110: Name: GLIBC_2.3 Flags: none Version: 9 0x0120: Name: GLIBC_2.7 Flags: none Version: 6 0x0130: Name: GLIBC_2.2.5 Flags: none Version: 5 0x0140: Name: GLIBC_2.3.2 Flags: none Version: 4 0x0150: Name: GLIBC_2.2.5 Flags: none Version: 3 0x0160: Version: 1 File: libdl.so.2 Cnt: 1 0x0170: Name: GLIBC_2.2.5 Flags: none Version: 2 ``` ### patchelf 修改 ELF 文件 一般的程序如果只使用了高版本 `memcpy` 的话,一般这样修改之后程序就可以运行了。但不巧我使用的第三方程序还使用了高版本 glibc 中的 `clock_gettime`,只是这样修改的话由于 CentOS 6 的 `libc` 2.12 库并没有提供 `clock_gettime`,运行时还是会报错。 这个时候我们就需要请出大杀器 [PatchELF](https://github.com/NixOS/patchelf) 了,这个小工具由 NixOS 团队开发,可以直接增加、删除、替换 ELF 文件依赖的库文件,使用起来也非常简单。 检出 PatchELF 的源代码,按照 GitHub 仓库上介绍的步骤编译安装就可以使用了(一般发行版自带的 patchelf 工具版本较老不支持一些新的功能)。 虽然 CentOS 6 的 `libc` 库没有提供 `clock_gettime` 实现,但好在 glibc 自带的 `librt` 库里还是提供了的,因此我们可以使用 patchelf 工具修改原版的程序文件,让程序优先加载 `librt` 库,这样程序就能正确加载 `clock_gettime` 符号了: ```bash [root@centos6-dev ~]# patchelf --add-needed librt.so.1 tester ``` 然后按照上面介绍的方法用十六进制编辑器修改新生成的 ELF 文件的 `.gnu.version_r` 表(因为 patchelf 运行之后新 ELF 文件的符号表就和之前的不一样了),将 `GLIBC_2.17` 和 `GLIBC_2.14` 统一改为 `GLIBC_2.2.5` 符号,保存 ELF 文件之后就可以看到效果了: ```bash [root@centos6-dev ~]# ldd tester linux-vdso.so.1 => (0x00007fffc17ee000) librt.so.1 => /lib64/librt.so.1 (0x00007f7f84dca000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7f84bad000) libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007f7f8498f000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f7f8478b000) libm.so.6 => /lib64/libm.so.6 (0x00007f7f84507000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f7f842f1000) libc.so.6 => /lib64/libc.so.6 (0x00007f7f83f5d000) /lib64/ld-linux-x86-64.so.2 (0x00007f7f84fd2000) ``` 从 `ldd` 命令的输出中可以看到修改后的程序会加载 `librt` 库,而且也没有 glibc 版本的报错了,经过测试程序运行起来也没有问题了。
json metadata{"tags":["glibc","elf","linux"],"links":["https://github.com/NixOS/patchelf"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkglibc
permlinklinux-elf-glibc
titleLinux修改ELF解决glibc兼容性问题
Transaction InfoBlock #25119795/Trx f2f88d2960b2e018e65ab5e129d4cc23147c9c47
View Raw JSON Data
{
  "block": 25119795,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## Linux glibc 问题\n\n相信有不少 Linux 用户都碰到过运行第三方(非系统自带软件源)发布的程序时的 glibc 兼容性问题,这一般是由于当前 Linux 系统上的 GNU C 库(glibc)版本比较老导致的,例如我在 CentOS 6 64 位系统上运行某第三方闭源软件时会报:\n\n```bash\n[root@centos6-dev ~]# ldd tester\n./tester: /lib64/libc.so.6: version `GLIBC_2.17' not found (required by ./tester)\n./tester: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./tester)\n        linux-vdso.so.1 =>  (0x00007ffe795fe000)\n        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc7d4c73000)\n        libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007fc7d4a55000)\n        libdl.so.2 => /lib64/libdl.so.2 (0x00007fc7d4851000)\n        libm.so.6 => /lib64/libm.so.6 (0x00007fc7d45cd000)\n        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc7d43b7000)\n        libc.so.6 => /lib64/libc.so.6 (0x00007fc7d4023000)\n        /lib64/ld-linux-x86-64.so.2 (0x00007fc7d4e90000)\n```\n\nCentOS 6 自带的 glibc 还是很老的 2.12 版本,而下载的第三方程序依赖 glibc 2.17 版本,这种情况要么自己重新编译程序,要么只能升级系统的 glibc 版本。由于我使用的程序是第三方编写并且是闭源软件无法自己编译,升级 glibc 固然可能能解决问题,但是 glibc 做为最核心的基础库,在生产环境上直接升级毕竟动作还是太大,因此希望还是能有更好的解决途径。\n\n## 问题分析\n\n首先我们可以检查一下程序使用了新版本 glibc 的哪些符号,使用 `objdump` 命令可以查看 ELF 文件的动态符号信息:\n\n```bash\n[root@centos6-dev ~]# objdump -T tester | grep GLIBC_2.1.*\n0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy\n0000000000000000      DF *UND*  0000000000000000  GLIBC_2.17  clock_gettime\n```\n\n从上面的输出可以看到程序使用了 glibc 2.14 版本的 `memcpy` 函数和 glibc 2.17 版本的 `clock_gettime` 函数,而这两个常用的函数按说应该是 glibc 很早就已经支持了的,我们可以确认一下当前系统 glibc 提供的符号版本:\n\n```bash\n[root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep memcpy\n0000000000091300  w   DF .text  0000000000000009  GLIBC_2.2.5 wmemcpy\n0000000000101070 g    DF .text  000000000000001b  GLIBC_2.4   __wmemcpy_chk\n00000000000896b0 g    DF .text  0000000000000465  GLIBC_2.2.5 memcpy\n00000000000896a0 g    DF .text  0000000000000009  GLIBC_2.3.4 __memcpy_chk\n[root@centos6-dev ~]# objdump -T /lib64/libc.so.6 | grep clock_gettime\n000000000038f800 g    DO .bss   0000000000000008  GLIBC_PRIVATE __vdso_clock_gettime\n```\n\n这里可以看出 CentOS 6 的 glibc 库提供的 `memcpy` 实现是 2.2.5 版本的,另外 `libc` 没有直接实现 `clock_gettime` 函数,因为老版本 glibc 里 `clock_gettime` 是由 `librt` 库提供 `clock_gettime` 支持的,而且同样也是 2.2.5 版本:\n\n```bash\n[root@centos6-dev ~]# objdump -T /lib64/librt.so.1 | grep clock_gettime\n0000000000000000      DO *UND*  0000000000000000  GLIBC_PRIVATE __vdso_clock_gettime\n0000000000003e70 g    DF .text  000000000000008b  GLIBC_2.2.5 clock_gettime\n```\n\n看过这里就基本明白了,第三方程序的开发者是在自带新版本 glibc 的 Linux 系统上编译的,`memcpy` 和 `clock_gettime` 的实现默认使用了该系统上 glibc 所提供的最新版本,这样在低版本 glibc 系统中就无法正常运行。\n\n## 解决方法\n\n虽然我们无法重新编译第三方程序,但如果可以修改 ELF 文件强制让 LD 库加载程序时使用老版本的 `memcpy` 和 `clock_gettime` 实现,应该就可以避免升级 glibc。\n\n### 分析 ELF\n\n首先用 `readelf` 命令查看 ELF 的符号表,由于该命令输出非常多,这里只贴出我们关心的信息:\n\n```bash\n[root@centos6-dev ~]# readelf -sV tester\n\nSymbol table '.dynsym' contains 4583 entries:\n   Num:    Value          Size Type    Bind   Vis      Ndx Name\n     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND\n    ......\n    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcpy@GLIBC_2.14 (5)\n    ......\n    67: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND clock_gettime@GLIBC_2.17 (16)\n    ......\n  4582: 0000000000794260    70 FUNC    WEAK   DEFAULT   12 _ZNSt15basic_streambufIwS\n\nVersion symbols section '.gnu.version' contains 4583 entries:\n Addr: 000000000045b508  Offset: 0x05b508  Link: 4 (.dynsym)\n  000:   0 (*local*)       0 (*local*)       2 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)\n  004:   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)\n  008:   4 (GLIBC_2.3.2)   3 (GLIBC_2.2.5)   0 (*local*)       5 (GLIBC_2.14)\n  ......\n  040:   2 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)   3 (GLIBC_2.2.5)  10 (GLIBC_2.17)\n  ......\n  11e0:   1 (*global*)      1 (*global*)      1 (*global*)      1 (*global*)\n  11e4:   1 (*global*)      1 (*global*)      1 (*global*)\n\nVersion needs section '.gnu.version_r' contains 6 entries:\n Addr: 0x000000000045d8d8  Offset: 0x05d8d8  Link: 5 (.dynstr)\n  000000: Version: 1  File: ld-linux-x86-64.so.2  Cnt: 1\n  0x0010:   Name: GLIBC_2.3  Flags: none  Version: 17\n  0x0020: Version: 1  File: libgcc_s.so.1  Cnt: 3\n  0x0030:   Name: GCC_3.0  Flags: none  Version: 13\n  0x0040:   Name: GCC_3.3  Flags: none  Version: 11\n  0x0050:   Name: GCC_4.2.0  Flags: none  Version: 10\n  0x0060: Version: 1  File: libm.so.6  Cnt: 1\n  0x0070:   Name: GLIBC_2.2.5  Flags: none  Version: 8\n  0x0080: Version: 1  File: libpthread.so.0  Cnt: 2\n  0x0090:   Name: GLIBC_2.3.2  Flags: none  Version: 15\n  0x00a0:   Name: GLIBC_2.2.5  Flags: none  Version: 7\n  0x00b0: Version: 1  File: libc.so.6  Cnt: 10\n  0x00c0:   Name: GLIBC_2.8  Flags: none  Version: 19\n  0x00d0:   Name: GLIBC_2.9  Flags: none  Version: 18\n  0x00e0:   Name: GLIBC_2.17  Flags: none  Version: 16\n  0x00f0:   Name: GLIBC_2.4  Flags: none  Version: 14\n  0x0100:   Name: GLIBC_2.3.4  Flags: none  Version: 12\n  0x0110:   Name: GLIBC_2.3  Flags: none  Version: 9\n  0x0120:   Name: GLIBC_2.7  Flags: none  Version: 6\n  0x0130:   Name: GLIBC_2.14  Flags: none  Version: 5\n  0x0140:   Name: GLIBC_2.3.2  Flags: none  Version: 4\n  0x0150:   Name: GLIBC_2.2.5  Flags: none  Version: 3\n  0x0160: Version: 1  File: libdl.so.2  Cnt: 1\n  0x0170:   Name: GLIBC_2.2.5  Flags: none  Version: 2\n```\n\n我们可以在 ELF 的 `.dynsym` 动态符号表中看到程序用于动态链接的所有导入导出符号,`memcpy` 和 `clock_gettime` 后面括号里的数字就是十进制的版本号(分别为 `5` 和 `16`),而我们需要格外关注的是下面的 `.gnu.version` 和 `.gnu.version_r` 符号版本信息段。\n\n`.gnu.version` 表包含所有动态符号的版本信息,`.dynsym` 动态符号表中的每个符号都可以在 `.gnu.version` 中看到对应的条目(`.dynsym` 中一共 4583 个符号刚好与 `.gnu.version` 的结束位置 0x11e7 相等)。\n\n从上面的输出可以看到 `.gnu.version` 表从 `0x05b508` 偏移量开始,我们可以看看对应偏移量的十六进制数据:\n\n```text\nOffset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n\n0005B500                          00 00 00 00 02 00 03 00          ........\n0005B510  03 00 03 00 03 00 03 00 04 00 03 00 00 00 05 00  ................\n0005B520  03 00 03 00 06 00 00 00 03 00 07 00 08 00 08 00  ................\n0005B530  03 00 09 00 03 00 03 00 0A 00 07 00 03 00 00 00  ................\n0005B540  03 00 03 00 0B 00 07 00 03 00 03 00 00 00 07 00  ................\n0005B550  00 00 03 00 03 00 03 00 03 00 0C 00 09 00 00 00  ................\n0005B560  07 00 03 00 03 00 07 00 03 00 07 00 0C 00 00 00  ................\n0005B570  0D 00 03 00 07 00 07 00 0E 00 0F 00 03 00 0D 00  ................\n0005B580  03 00 03 00 03 00 03 00 02 00 03 00 03 00 10 00  ................\n0005B590  03 00 00 00 03 00 07 00 08 00 07 00 07 00 03 00  ................\n0005B5A0  03 00 0D 00 03 00 00 00 03 00 03 00 03 00 00 00  ................\n```\n\n`.gnu.version` 中的每个条目占用两个字节,其值为符号的版本,由此可以看到其中第 0x0b 个符号(也就是 `.dynsym` 表中的 `memcpy@GLIBC_2.14` 符号)的偏移量即为 0x05b51e(0x05b508 + 0x0b x 2),该偏移量的值 0x0005 也刚好和 `.dynsym` 表中的值对应,当然 `clock_gettime` 符号对应的偏移量 0x05b58e 的值 0x0010 同样也是如此。\n\n下面关键的 `.gnu.version_r` 表示二进制程序实际依赖的库文件版本,从输出中也能看到 `.gnu.version_r` 表是按照不同的库文件进行分段显示的,每个条目占用 0x10 也就是 16 个字节,该表是从 `0x05d8d8` 偏移量开始,我们看看 `GLIBC_2.17` 也就是 `0x05d9b8` 处的十六进制数据:\n\n```text\nOffset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n\n0005D9B0  B0 70 03 00 10 00 00 00 97 91 96 06 00 00 10 00  °p......—‘–.....\n0005D9C0  BA 70 03 00 10 00 00 00 14 69 69 0D 00 00 0E 00  ºp.......ii.....\n0005D9D0  C5 70 03 00 10 00 00 00 74 19 69 09 00 00 0C 00  Åp......t.i.....\n0005D9E0  CF 70 03 00 10 00 00 00 13 69 69 0D 00 00 09 00  Ïp.......ii.....\n0005D9F0  6A 70 03 00 10 00 00 00 17 69 69 0D 00 00 06 00  jp.......ii.....\n0005DA00  DB 70 03 00 10 00 00 00 94 91 96 06 00 00 05 00  Ûp......”‘–.....\n0005DA10  E5 70 03 00 10 00 00 00 72 19 69 09 00 00 04 00  åp......r.i.....\n0005DA20  9A 70 03 00 10 00 00 00 75 1A 69 09 00 00 03 00  šp......u.i.....\n0005DA30  8E 70 03 00 00 00 00 00 01 00 01 00 D8 03 00 00  Žp..........Ø...\n```\n\n`.gnu.version_r` 表中每个条目是 16 个字节的 `Elfxx_Vernaux` 结构体,其声明如下(`Elfxx_Half` 占用 2 个字节,`Elfxx_Word` 占用 4 个字节):\n\n```c\ntypedef struct {\n\tElfxx_Word    vna_hash;\n\tElfxx_Half    vna_flags;\n\tElfxx_Half    vna_other;\n\tElfxx_Word    vna_name;\n\tElfxx_Word    vna_next;\n} Elfxx_Vernaux;\n```\n\n`vna_hash` 为 4 个字节的库名称(也就是上面的 `GLIBC_2.17` 字符串)的 hash 值,`vna_other` 为对应的 `.gnu.version` 表中符号的版本值,`vna_name` 指向库名称字符串的偏移量(也可以在 ELF 头中找到),`vna_next` 为下一个条目的位置(一般固定为 `0x00000010`)。\n\n由上面的输出我们可以看到 `GLIBC_2.17` 对应的 `0x05d9b8` 处的开始的 4 个字节 `vna_hash` hash 值为 `0x06969197`,而 `vna_other` 的值 `0x0010`(输出里的 `Version: 16`)也与 `.gnu.version` 中 `clock_gettime` 符号的值一致。同样 `GLIBC_2.14` 也与 `memcpy` 符号的值相符。\n\n### 修改 ELF 符号表\n\n由于 Linux 系统中的 LD 库(也就是 `/lib64/ld-linux-x86-64.so.2` 库)加载 ELF 时检查 `.gnu.version_r` 表中的符号,我们可以使用任何一款十六进制编辑器来修改 `.gnu.version_r` 表中的符号值来强制使用老版本的函数实现。\n\n首先我们发现 `.gnu.version_r` 的 `libc.so.6` 段下面有 10 个条目,最后一个则是我们需要的 `GLIBC_2.2.5` 版本的符号(从上面的十六进制输出中我们可以看到该符号的偏移量为 `0x05da28`,`vna_hash` 值为 `0x09691A75`,`vna_other` 版本值为 `0x0003`,`vna_name` 字符串名称指向 `0003708E` 地址),因为这样我们才可以在不修改 ELF 文件大小的前提下直接将 `libc.so.6` 段下的其它高版本条目指向老版本条目的值。\n\n例如 `GLIBC_2.17` 对应的 `0x05d9b8` 偏移量,我们可以直接将 `vna_hash` 值改为 `GLIBC_2.2.5` 的 `0x09691A75` 值,将 `vna_other` 改为 `0003708E` 值,为了保持和 `.gnu.version` 表中的版本值一致,这里我们就不修改 `vna_other` 值了。\n\n对于 `GLIBC_2.14` 偏移量我们也修改成同样的值,修改保存之后的 ELF 文件再使用 `readelf` 命令检查就能看到变化了(只列出了修改的 `.gnu.version-r` 表):\n\n```bash\n[root@centos6-dev ~]# readelf -sV tester\n......\nVersion needs section '.gnu.version_r' contains 6 entries:\n Addr: 0x000000000045d8d8  Offset: 0x05e8d8  Link: 2 (.dynstr)\n  000000: Version: 1  File: ld-linux-x86-64.so.2  Cnt: 1\n  0x0010:   Name: GLIBC_2.3  Flags: none  Version: 17\n  0x0020: Version: 1  File: libgcc_s.so.1  Cnt: 3\n  0x0030:   Name: GCC_3.0  Flags: none  Version: 13\n  0x0040:   Name: GCC_3.3  Flags: none  Version: 11\n  0x0050:   Name: GCC_4.2.0  Flags: none  Version: 10\n  0x0060: Version: 1  File: libm.so.6  Cnt: 1\n  0x0070:   Name: GLIBC_2.2.5  Flags: none  Version: 8\n  0x0080: Version: 1  File: libpthread.so.0  Cnt: 2\n  0x0090:   Name: GLIBC_2.3.2  Flags: none  Version: 15\n  0x00a0:   Name: GLIBC_2.2.5  Flags: none  Version: 7\n  0x00b0: Version: 1  File: libc.so.6  Cnt: 10\n  0x00c0:   Name: GLIBC_2.8  Flags: none  Version: 19\n  0x00d0:   Name: GLIBC_2.9  Flags: none  Version: 18\n  0x00e0:   Name: GLIBC_2.2.5  Flags: none  Version: 16\n  0x00f0:   Name: GLIBC_2.4  Flags: none  Version: 14\n  0x0100:   Name: GLIBC_2.3.4  Flags: none  Version: 12\n  0x0110:   Name: GLIBC_2.3  Flags: none  Version: 9\n  0x0120:   Name: GLIBC_2.7  Flags: none  Version: 6\n  0x0130:   Name: GLIBC_2.2.5  Flags: none  Version: 5\n  0x0140:   Name: GLIBC_2.3.2  Flags: none  Version: 4\n  0x0150:   Name: GLIBC_2.2.5  Flags: none  Version: 3\n  0x0160: Version: 1  File: libdl.so.2  Cnt: 1\n  0x0170:   Name: GLIBC_2.2.5  Flags: none  Version: 2\n```\n\n### patchelf 修改 ELF 文件\n\n一般的程序如果只使用了高版本 `memcpy` 的话,一般这样修改之后程序就可以运行了。但不巧我使用的第三方程序还使用了高版本 glibc 中的 `clock_gettime`,只是这样修改的话由于 CentOS 6 的 `libc` 2.12 库并没有提供 `clock_gettime`,运行时还是会报错。\n\n这个时候我们就需要请出大杀器 [PatchELF](https://github.com/NixOS/patchelf) 了,这个小工具由 NixOS 团队开发,可以直接增加、删除、替换 ELF 文件依赖的库文件,使用起来也非常简单。\n\n检出 PatchELF 的源代码,按照 GitHub 仓库上介绍的步骤编译安装就可以使用了(一般发行版自带的 patchelf 工具版本较老不支持一些新的功能)。\n\n虽然 CentOS 6 的 `libc` 库没有提供 `clock_gettime` 实现,但好在 glibc 自带的 `librt` 库里还是提供了的,因此我们可以使用 patchelf 工具修改原版的程序文件,让程序优先加载 `librt` 库,这样程序就能正确加载 `clock_gettime` 符号了:\n\n```bash\n[root@centos6-dev ~]# patchelf --add-needed librt.so.1 tester\n```\n\n然后按照上面介绍的方法用十六进制编辑器修改新生成的 ELF 文件的 `.gnu.version_r` 表(因为 patchelf 运行之后新 ELF 文件的符号表就和之前的不一样了),将 `GLIBC_2.17` 和 `GLIBC_2.14` 统一改为 `GLIBC_2.2.5` 符号,保存 ELF 文件之后就可以看到效果了:\n\n```bash\n[root@centos6-dev ~]# ldd tester\n        linux-vdso.so.1 =>  (0x00007fffc17ee000)\n        librt.so.1 => /lib64/librt.so.1 (0x00007f7f84dca000)\n        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7f84bad000)\n        libOpenCL.so.1 => /usr/lib64/libOpenCL.so.1 (0x00007f7f8498f000)\n        libdl.so.2 => /lib64/libdl.so.2 (0x00007f7f8478b000)\n        libm.so.6 => /lib64/libm.so.6 (0x00007f7f84507000)\n        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f7f842f1000)\n        libc.so.6 => /lib64/libc.so.6 (0x00007f7f83f5d000)\n        /lib64/ld-linux-x86-64.so.2 (0x00007f7f84fd2000)\n```\n\n从 `ldd` 命令的输出中可以看到修改后的程序会加载 `librt` 库,而且也没有 glibc 版本的报错了,经过测试程序运行起来也没有问题了。",
      "json_metadata": "{\"tags\":[\"glibc\",\"elf\",\"linux\"],\"links\":[\"https://github.com/NixOS/patchelf\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "glibc",
      "permlink": "linux-elf-glibc",
      "title": "Linux修改ELF解决glibc兼容性问题"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-16T14:04:36",
  "trx_id": "f2f88d2960b2e018e65ab5e129d4cc23147c9c47",
  "trx_in_block": 38,
  "virtual_op": 0
}
2018/08/14 14:52:21
authorzohead
permlinkbuild-an-android-superbook-with-displaylink-adapter
voterobaku
weight60 (0.60%)
Transaction InfoBlock #25063166/Trx 00235a054931a165e7c02083be35f5784c812a09
View Raw JSON Data
{
  "block": 25063166,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "build-an-android-superbook-with-displaylink-adapter",
      "voter": "obaku",
      "weight": 60
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T14:52:21",
  "trx_id": "00235a054931a165e7c02083be35f5784c812a09",
  "trx_in_block": 10,
  "virtual_op": 0
}
2018/08/14 14:46:45
authorzohead
body## About Superbook A couple of days ago, I saw an interesting Kickstarter project called Superbook. The Superbook is a shell that turns your Android smartphone into a laptop. It's created by the Andromium team, backers have pledged more than eight hundred thousand dollars for this project. You should take a look at their Kickstarter project page: [http://getsuperbook.com/](http://getsuperbook.com/) After reading the introduction of Superbook, I think it is kind of familiar with LapDock 100, which is launched by Motorola couple of years ago. While LapDock is designed for Motorola smartphones like ME865 or Atrix. If you want to use LapDock for other devices, then your device must be capable of USB Host/OTG and HDMI output function, also you need to prepare kinds of converter adapters/cables. The main features of Superbook includes: * Support all Android smartphone with USB OTG function; * 11.6" HD Display with 1366x768 resolution; * 10 hours of battery, charge your phone when connected; * Support Raspberry Pi and other MiniPC type devices; * Multi-Touch trackpad & keyboard, also include an extra USB port; * Access all data in Android phone (familiar with LapDock and ASUS PadFone Station). Your Android phone will switch to **Andromium OS** UI after it is connected to Superbook, **Andromium OS** also supports running multiple Android App in different windows, which really looks like [Remix OS](http://www.jide.com/remixos-for-pc). After looking at Superbook's technical details, I know it does not depend on smartphone's HDMI or MHL video output function. Superbook is based on DisplayLink's Android video output function, you can refer to [DisplayLink for Android](http://www.displaylink.com/downloads/android) web page for more details. Now it seems that the biggest different between Superbook and Motorola Lapdock is: a DisplayLink USB graphic adapter is integrated into Superbook's USB hub. USB graphic adapter starts working after it is connected to Android phone with USB OTG port, then Superbook can use it for supporting Android phone video output. That's why Superbook can support more kinds of Android phone compared with LapDock, and it's much easier that you only need a USB cable. It happens that I ordered a DisplayLink USB 2.0 graphic adapter when I built my [Ubuntu for Android](https://zohead.com/archives/ubuntu-for-xolo-x900/?lang=en) project before. It should be possible to build a simple version of Superbook with DisplayLink USB adapter and my Motorola LapDock and ASUS Zenfone2 phone (supports USB OTG, just does not support HDMI or MHL). ## Build Superbook with phone and LapDock To start with, you need to install [DisplayLink Presenter](https://play.google.com/store/apps/details?id=com.displaylink.presenter) App on your Android phone, you don't need to do any further setting. DisplayLink Presenter should works after a DisplayLink graphic adapter or docking station is connected with your phone, you can see its introduction on the App main screen: ![DisplayLink Presenter](https://storage.live.com/items/F799A497307A642E!5369?a.png "DisplayLink Presenter") Then you can connect your phone with USB graphic adapter and Motorola Lapdock, please ignore these disordered cables ^_^: ![Connect LapDock and DisplayLink adapter](https://storage.live.com/items/F799A497307A642E!5168?a.png "Connect LapDock and DisplayLink adapter") The Zenfone2 phone is connected to LapDock's Micro-USB port, DisplayLink USB graphic adapter is connected to LapDock's extra USB port (in the red circle of above image). You also need to connect DisplayLink adapter's HDMI with LapDock's Micro-HDMI port, thus LapDock can supply power for both Android phone and USB graphic adapter when you open the lid of LapDock. Now we can check whether USB adapter device is detected, execute `busybox lsusb` command (need busybox support for Android) in any terminal emulator, we should see all attached USB devices. And this is the screen capture before and after USB adapter is connected to my phone: ![DisplayLink USB adapter device](https://storage.live.com/items/F799A497307A642E!5170?a.png "DisplayLink USB adapter device") The USB device with device ID `17e9:03c1` is our DisplayLink USB graphic adapter. If everything goes well, you should see the mirror of your phone's screen on the LapDock, you can also do text input and moving cursor operations with LapDock's keyboard and touchpad. It seems that we got a simple Superbook now ^_^. This is the image of running Android Chrome browser on my LapDock, it really performs better on a bigger screen: ![Android Chrome on LapDock Superbook](https://storage.live.com/items/F799A497307A642E!5169?a.png "Android Chrome on LapDock Superbook") You can also play games on this LapDock style Superbook, while it should be better if the game supports display in landscape mode. You will get more fan if the game supports USB or Bluetooth game stick: ![Play games on LapDock Superbook](https://storage.live.com/items/F799A497307A642E!5167?a.png "Play games on LapDock Superbook") As you can see, we only get simple phone screen mirroring with DisplayLink adapter, if you want to experience more features like future Superbook, you can install **Andromium OS** App from Google Play store: [https://play.google.com/store/apps/details?id=com.andromium.os](https://play.google.com/store/apps/details?id=com.andromium.os) **Andromium OS** App says it supports connect Android phone with compatible docking station, it should also support DisplayLink USB graphic adapter I used here. You can refer to [Andromium forum post](http://andromiumos.freeforums.net/thread/5/setting-andromium-samsung-phone-dock) for more details, the instruction steps look like the way I build the LapDock Superbook (just replace DisplayLink Presenter App with Andromium OS). You should also pay attention to the limitations in that post. ## Postscript I have received an important update from Andromium team after I writing this article, they add an extra option to upgrade Superbook screen to 1080p (with \$30 additional cost), because they have archived their 1M dollars goal. So, without any doubt, I pledged \$129 for a 1080p version Superbook. It will need months waiting for manufacturing and delivery to China, I really hope that Superbook is worth to be my first backed Kickstarter project.
json metadata{"tags":["superbook","android","displaylink"],"image":["https://storage.live.com/items/F799A497307A642E!5369?a.png","https://storage.live.com/items/F799A497307A642E!5168?a.png","https://storage.live.com/items/F799A497307A642E!5170?a.png","https://storage.live.com/items/F799A497307A642E!5169?a.png","https://storage.live.com/items/F799A497307A642E!5167?a.png"],"links":["http://getsuperbook.com/","http://www.jide.com/remixos-for-pc","http://www.displaylink.com/downloads/android","https://zohead.com/archives/ubuntu-for-xolo-x900/?lang=en","https://play.google.com/store/apps/details?id=com.displaylink.presenter","https://play.google.com/store/apps/details?id=com.andromium.os","http://andromiumos.freeforums.net/thread/5/setting-andromium-samsung-phone-dock"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinksuperbook
permlinkbuild-an-android-superbook-with-displaylink-adapter
titleBuild an Android Superbook with DisplayLink adapter
Transaction InfoBlock #25063054/Trx f68a0211455e9dcd2f3041f20bd860479e7e3ca9
View Raw JSON Data
{
  "block": 25063054,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## About Superbook\n\nA couple of days ago, I saw an interesting Kickstarter project called Superbook. The Superbook is a shell that turns your Android smartphone into a laptop. It's created by the Andromium team, backers have pledged more than eight hundred thousand dollars for this project.\n\nYou should take a look at their Kickstarter project page:\n\n[http://getsuperbook.com/](http://getsuperbook.com/)\n\nAfter reading the introduction of Superbook, I think it is kind of familiar with LapDock 100, which is launched by Motorola couple of years ago. While LapDock is designed for Motorola smartphones like ME865 or Atrix. If you want to use LapDock for other devices, then your device must be capable of USB Host/OTG and HDMI output function, also you need to prepare kinds of converter adapters/cables.\n\nThe main features of Superbook includes:\n\n* Support all Android smartphone with USB OTG function;\n* 11.6\" HD Display with 1366x768 resolution;\n* 10 hours of battery, charge your phone when connected;\n* Support Raspberry Pi and other MiniPC type devices;\n* Multi-Touch trackpad & keyboard, also include an extra USB port;\n* Access all data in Android phone (familiar with LapDock and ASUS PadFone Station).\n\nYour Android phone will switch to **Andromium OS** UI after it is connected to Superbook, **Andromium OS** also supports running multiple Android App in different windows, which really looks like [Remix OS](http://www.jide.com/remixos-for-pc).\n\nAfter looking at Superbook's technical details, I know it does not depend on smartphone's HDMI or MHL video output function. Superbook is based on DisplayLink's Android video output function, you can refer to [DisplayLink for Android](http://www.displaylink.com/downloads/android) web page for more details.\n\nNow it seems that the biggest different between Superbook and Motorola Lapdock is: a DisplayLink USB graphic adapter is integrated into Superbook's USB hub. USB graphic adapter starts working after it is connected to Android phone with USB OTG port, then Superbook can use it for supporting Android phone video output. That's why Superbook can support more kinds of Android phone compared with LapDock, and it's much easier that you only need a USB cable.\n\nIt happens that I ordered a DisplayLink USB 2.0 graphic adapter when I built my [Ubuntu for Android](https://zohead.com/archives/ubuntu-for-xolo-x900/?lang=en) project before. It should be possible to build a simple version of Superbook with DisplayLink USB adapter and my Motorola LapDock and ASUS Zenfone2 phone (supports USB OTG, just does not support HDMI or MHL).\n\n## Build Superbook with phone and LapDock\n\nTo start with, you need to install [DisplayLink Presenter](https://play.google.com/store/apps/details?id=com.displaylink.presenter) App on your Android phone, you don't need to do any further setting. DisplayLink Presenter should works after a DisplayLink graphic adapter or docking station is connected with your phone, you can see its introduction on the App main screen:\n\n![DisplayLink Presenter](https://storage.live.com/items/F799A497307A642E!5369?a.png \"DisplayLink Presenter\")\n\nThen you can connect your phone with USB graphic adapter and Motorola Lapdock, please ignore these disordered cables ^_^:\n\n![Connect LapDock and DisplayLink adapter](https://storage.live.com/items/F799A497307A642E!5168?a.png \"Connect LapDock and DisplayLink adapter\")\n\nThe Zenfone2 phone is connected to LapDock's Micro-USB port, DisplayLink USB graphic adapter is connected to LapDock's extra USB port (in the red circle of above image). You also need to connect DisplayLink adapter's HDMI with LapDock's Micro-HDMI port, thus LapDock can supply power for both Android phone and USB graphic adapter when you open the lid of LapDock.\n\nNow we can check whether USB adapter device is detected, execute `busybox lsusb` command (need busybox support for Android) in any terminal emulator, we should see all attached USB devices. And this is the screen capture before and after USB adapter is connected to my phone:\n\n![DisplayLink USB adapter device](https://storage.live.com/items/F799A497307A642E!5170?a.png \"DisplayLink USB adapter device\")\n\nThe USB device with device ID `17e9:03c1` is our DisplayLink USB graphic adapter.\n\nIf everything goes well, you should see the mirror of your phone's screen on the LapDock, you can also do text input and moving cursor operations with LapDock's keyboard and touchpad. It seems that we got a simple Superbook now ^_^.\n\nThis is the image of running Android Chrome browser on my LapDock, it really performs better on a bigger screen:\n\n![Android Chrome on LapDock Superbook](https://storage.live.com/items/F799A497307A642E!5169?a.png \"Android Chrome on LapDock Superbook\")\n\nYou can also play games on this LapDock style Superbook, while it should be better if the game supports display in landscape mode. You will get more fan if the game supports USB or Bluetooth game stick:\n\n![Play games on LapDock Superbook](https://storage.live.com/items/F799A497307A642E!5167?a.png \"Play games on LapDock Superbook\")\n\nAs you can see, we only get simple phone screen mirroring with DisplayLink adapter, if you want to experience more features like future Superbook, you can install **Andromium OS** App from Google Play store:\n\n[https://play.google.com/store/apps/details?id=com.andromium.os](https://play.google.com/store/apps/details?id=com.andromium.os)\n\n**Andromium OS** App says it supports connect Android phone with compatible docking station, it should also support DisplayLink USB graphic adapter I used here.\n\nYou can refer to [Andromium forum post](http://andromiumos.freeforums.net/thread/5/setting-andromium-samsung-phone-dock) for more details, the instruction steps look like the way I build the LapDock Superbook (just replace DisplayLink Presenter App with Andromium OS). You should also pay attention to the limitations in that post.\n\n## Postscript\n\nI have received an important update from Andromium team after I writing this article, they add an extra option to upgrade Superbook screen to 1080p (with \\$30 additional cost), because they have archived their 1M dollars goal. So, without any doubt, I pledged \\$129 for a 1080p version Superbook. It will need months waiting for manufacturing and delivery to China, I really hope that Superbook is worth to be my first backed Kickstarter project.",
      "json_metadata": "{\"tags\":[\"superbook\",\"android\",\"displaylink\"],\"image\":[\"https://storage.live.com/items/F799A497307A642E!5369?a.png\",\"https://storage.live.com/items/F799A497307A642E!5168?a.png\",\"https://storage.live.com/items/F799A497307A642E!5170?a.png\",\"https://storage.live.com/items/F799A497307A642E!5169?a.png\",\"https://storage.live.com/items/F799A497307A642E!5167?a.png\"],\"links\":[\"http://getsuperbook.com/\",\"http://www.jide.com/remixos-for-pc\",\"http://www.displaylink.com/downloads/android\",\"https://zohead.com/archives/ubuntu-for-xolo-x900/?lang=en\",\"https://play.google.com/store/apps/details?id=com.displaylink.presenter\",\"https://play.google.com/store/apps/details?id=com.andromium.os\",\"http://andromiumos.freeforums.net/thread/5/setting-andromium-samsung-phone-dock\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "superbook",
      "permlink": "build-an-android-superbook-with-displaylink-adapter",
      "title": "Build an Android Superbook with DisplayLink adapter"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T14:46:45",
  "trx_id": "f68a0211455e9dcd2f3041f20bd860479e7e3ca9",
  "trx_in_block": 41,
  "virtual_op": 0
}
2018/08/14 14:24:39
authorhannahwu
body😂欢迎⊙ω⊙对电子产品不感冒 哈哈哈
json metadata{"tags":["acer"],"app":"steemit/0.1"}
parent authorzohead
parent permlinkacer-chromebook-14
permlinkre-zohead-acer-chromebook-14-20180814t142435662z
title
Transaction InfoBlock #25062612/Trx d892339a46dfd89cb096b0819124a8c294b665a3
View Raw JSON Data
{
  "block": 25062612,
  "op": [
    "comment",
    {
      "author": "hannahwu",
      "body": "😂欢迎⊙ω⊙对电子产品不感冒 哈哈哈",
      "json_metadata": "{\"tags\":[\"acer\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "zohead",
      "parent_permlink": "acer-chromebook-14",
      "permlink": "re-zohead-acer-chromebook-14-20180814t142435662z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T14:24:39",
  "trx_id": "d892339a46dfd89cb096b0819124a8c294b665a3",
  "trx_in_block": 18,
  "virtual_op": 0
}
2018/08/14 14:23:03
authorzohead
permlinkacer-chromebook-14
voterhannahwu
weight10000 (100.00%)
Transaction InfoBlock #25062580/Trx d4ec3799f05f663610ad8ddaa56a6c5157db4d95
View Raw JSON Data
{
  "block": 25062580,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "acer-chromebook-14",
      "voter": "hannahwu",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T14:23:03",
  "trx_id": "d4ec3799f05f663610ad8ddaa56a6c5157db4d95",
  "trx_in_block": 11,
  "virtual_op": 0
}
zoheadunfollowed @xierdear
2018/08/14 04:17:45
idfollow
json["follow",{"follower":"zohead","following":"xierdear","what":[]}]
required auths[]
required posting auths["zohead"]
Transaction InfoBlock #25050478/Trx 486d67b073ab583c49448dffd241f833ee0edc08
View Raw JSON Data
{
  "block": 25050478,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"zohead\",\"following\":\"xierdear\",\"what\":[]}]",
      "required_auths": [],
      "required_posting_auths": [
        "zohead"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T04:17:45",
  "trx_id": "486d67b073ab583c49448dffd241f833ee0edc08",
  "trx_in_block": 12,
  "virtual_op": 0
}
2018/08/14 04:12:45
idfollow
json["follow",{"follower":"zohead","following":"xierdear","what":["blog"]}]
required auths[]
required posting auths["zohead"]
Transaction InfoBlock #25050378/Trx 5adc09e134501b943b5f76035867672e4497b651
View Raw JSON Data
{
  "block": 25050378,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"zohead\",\"following\":\"xierdear\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "zohead"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T04:12:45",
  "trx_id": "5adc09e134501b943b5f76035867672e4497b651",
  "trx_in_block": 9,
  "virtual_op": 0
}
ax3upvoted (1.00%) @zohead / acer-chromebook-14
2018/08/14 04:05:12
authorzohead
permlinkacer-chromebook-14
voterax3
weight100 (1.00%)
Transaction InfoBlock #25050227/Trx 0972f4aa4e81cd19d0b27a2118f4f86739b6ea6a
View Raw JSON Data
{
  "block": 25050227,
  "op": [
    "vote",
    {
      "author": "zohead",
      "permlink": "acer-chromebook-14",
      "voter": "ax3",
      "weight": 100
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T04:05:12",
  "trx_id": "0972f4aa4e81cd19d0b27a2118f4f86739b6ea6a",
  "trx_in_block": 19,
  "virtual_op": 0
}
zoheadpublished a new post: acer-chromebook-14
2018/08/14 04:05:00
authorzohead
body## Acer Chromebook 14 这两年非工作场合下我的主力电脑已经切换到 Chromebook,一直用的是 Dell Chromebook 11 这款小屏幕的笔记本,习惯了 Linux 之后 Chrome OS 用起来还是比较舒心的。 只是当我发现 Google 新推出的 Android Apps on Chrome OS 不再支持 Dell Chromebook 11 之类的老设备时(基本上所有使用赛扬 2955U 处理器的 Chromebook 都被排除了,具体可以参考 [Chrome OS Systems Supporting Android Apps](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps)),虽然觉得有点不爽,但还是下定决心准备换新的 Chromebook 设备了。 刚好最近看到一款之前关注过的 Acer Chromebook 14 笔记本,确认在 Play 商店支持的设备列表中就有了兴趣了,配置虽然和老的 Dell Chromebook 11 看起来差不了多少,但主要机身也更轻薄了,屏幕也升级到 1080p 了,立马准备拿下。 ## 基本配置 简单列一下配置: |项目|内容| |--|--| |型号|CB3-431-C0AK| |CPU|Intel® Celeron® N3160 1.6 GHz| |内存|LPDDR3 4GB| |存储|32GB eMMC 闪存| |屏幕|14 寸 IPS 1920x1080| |核显|Intel® HD Graphics 400| |无线|802.11a/b/g/n/ac| |接口|2x USB 3.0、HDMI| 其实除了 eMMC 闪存速度一般之外,其它方面我还是比较满意的,由于内存和闪存都是焊在主板上的,也基本没有升级的可能了,当然如果能像 Dell Chromebook 11 一样能自带读卡器就更好了。 ## 外观 先看看 Acer Chromebook 14 的正面和背面: ![Acer Chromebook 14 正面](https://storage.live.com/items/F799A497307A642E!6495?a.jpg) ![Acer Chromebook 14 背面](https://storage.live.com/items/F799A497307A642E!6496?a.jpg) 这款 Chromebook 直接没有风扇,全金属的机身设计的很轻薄,两面都异常简单。 再看看打开后的 B 面和 C 面: ![Acer Chromebook 14 B 面和 C 面](https://storage.live.com/items/F799A497307A642E!6497?a.jpg) 支持多点触控的触控板占了 C 面很大的面积,由于 Chromebook 对于多点触控现在支持的也非常好了,我用 Chromebook 时基本都不用鼠标了,键盘相比原来的 Dell Chromebook 11 手感也更好了: ![Acer Chromebook 14 触控板和键盘](https://storage.live.com/items/F799A497307A642E!6499?a.jpg) 充电孔和耳机插孔都在机身右侧,该 Chromebook 的所有接口都在机身左侧,下面的图也可以看看键盘效果: ![Acer Chromebook 14 接口](https://storage.live.com/items/F799A497307A642E!6498?a.jpg) ## 使用体验 Acer Chromebook 14 拿上手开机之后还是感觉比原来的 Dell Chromebook 11 屏幕改进挺明显的,老一批 Chromebook 备受诟病的屏幕可视角度问题在这里就基本没有了,只是我发现 1080p 的屏幕默认选择的“最优”分辨率是 1536x864。 我把分辨率改为 1920x1080 的话 Chrome OS 的显示体验就比较差了,网页字体、标题栏、工具栏都显得特别小;这种情况下只能将缩放调至 125% 或者 150%,不过即使这样也只是网页显示正常了,标题栏和工具栏并没有跟进缩放。这样的显示效果甚至还不及 Windows 10 高分屏设备上的 Chrome 浏览器,鉴于 Chrome OS 对高分屏的支持也比较天残,我也只能保持默认“最优”分辨率了。 Chrome OS 必备的爬墙激活这里就不说了,激活成功之后一般就可以领取 90 天的 Play Music 和两年 100 GB 的 Google Drive 空间了。无意外的话连上网络就会有系统更新了,更新到最新的 Chrome OS 64 稳定版本,系统就已经自带新的 Play 商店支持了。 在设置中开启 Play 商店之后,如果你的梯子没什么问题的话应该就可以打开 Play 商店安装常用的 App 了,Chrome OS 自带的 Android 容器为 7.1.1 版本,相比原来的 Chrome ARC 运行时来说 App 的兼容性还是有质的提升的。 我专门测试了网易云音乐、手机 QQ 等几个原来 Chrome ARC 无法运行的 App,在新的 Android 容器中运行都没有什么问题,起码 Chromebook 上使用 QQ 除了 crouton 中运行 Wine QQ 之外终于有直接的解决方案了。 按照我平常基本用 Chromebook 浏览网页,偶尔看点 HTML5 视频的使用场景,这款笔记本还是基本能达到标称的 12 小时续航时间的,这也是 Chrome OS 相对 Windows 等系统的一大优势了。 Acer Chromebook 14 这一段时间的总体使用体验还是比较令人满意的,碰到一个未解决的小问题就是原来通过 crouton 安装的 Ubuntu 14.04 系统无法以 xorg 方式正常启动图形界面了,只能以 [xiwi](https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)) 窗口形式启动图形界面,更新 crouton 也没有解决,不过还好这个影响不是很大咯。
json metadata{"tags":["chromebook","acer","chromeos"],"image":["https://storage.live.com/items/F799A497307A642E!6495?a.jpg","https://storage.live.com/items/F799A497307A642E!6496?a.jpg","https://storage.live.com/items/F799A497307A642E!6497?a.jpg","https://storage.live.com/items/F799A497307A642E!6499?a.jpg","https://storage.live.com/items/F799A497307A642E!6498?a.jpg"],"links":["https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps","https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkacer
permlinkacer-chromebook-14
title新玩具 Acer Chromebook 14
Transaction InfoBlock #25050223/Trx 80b4030d38f6bf96e4e0791d38335869350cdfad
View Raw JSON Data
{
  "block": 25050223,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## Acer Chromebook 14\n\n这两年非工作场合下我的主力电脑已经切换到 Chromebook,一直用的是 Dell Chromebook 11 这款小屏幕的笔记本,习惯了 Linux 之后 Chrome OS 用起来还是比较舒心的。\n\n只是当我发现 Google 新推出的 Android Apps on Chrome OS 不再支持 Dell Chromebook 11 之类的老设备时(基本上所有使用赛扬 2955U 处理器的 Chromebook 都被排除了,具体可以参考 [Chrome OS Systems Supporting Android Apps](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps)),虽然觉得有点不爽,但还是下定决心准备换新的 Chromebook 设备了。\n\n刚好最近看到一款之前关注过的 Acer Chromebook 14 笔记本,确认在 Play 商店支持的设备列表中就有了兴趣了,配置虽然和老的 Dell Chromebook 11 看起来差不了多少,但主要机身也更轻薄了,屏幕也升级到 1080p 了,立马准备拿下。\n\n## 基本配置\n\n简单列一下配置:\n\n|项目|内容|\n|--|--|\n|型号|CB3-431-C0AK|\n|CPU|Intel® Celeron® N3160 1.6 GHz|\n|内存|LPDDR3 4GB|\n|存储|32GB eMMC 闪存|\n|屏幕|14 寸 IPS 1920x1080|\n|核显|Intel® HD Graphics 400|\n|无线|802.11a/b/g/n/ac|\n|接口|2x USB 3.0、HDMI|\n\n其实除了 eMMC 闪存速度一般之外,其它方面我还是比较满意的,由于内存和闪存都是焊在主板上的,也基本没有升级的可能了,当然如果能像 Dell Chromebook 11 一样能自带读卡器就更好了。\n\n## 外观\n\n先看看 Acer Chromebook 14 的正面和背面:\n\n![Acer Chromebook 14 正面](https://storage.live.com/items/F799A497307A642E!6495?a.jpg)\n\n![Acer Chromebook 14 背面](https://storage.live.com/items/F799A497307A642E!6496?a.jpg)\n\n这款 Chromebook 直接没有风扇,全金属的机身设计的很轻薄,两面都异常简单。\n\n再看看打开后的 B 面和 C 面:\n\n![Acer Chromebook 14 B 面和 C 面](https://storage.live.com/items/F799A497307A642E!6497?a.jpg)\n\n支持多点触控的触控板占了 C 面很大的面积,由于 Chromebook 对于多点触控现在支持的也非常好了,我用 Chromebook 时基本都不用鼠标了,键盘相比原来的 Dell Chromebook 11 手感也更好了:\n\n![Acer Chromebook 14 触控板和键盘](https://storage.live.com/items/F799A497307A642E!6499?a.jpg)\n\n充电孔和耳机插孔都在机身右侧,该 Chromebook 的所有接口都在机身左侧,下面的图也可以看看键盘效果:\n\n![Acer Chromebook 14 接口](https://storage.live.com/items/F799A497307A642E!6498?a.jpg)\n\n## 使用体验\n\nAcer Chromebook 14 拿上手开机之后还是感觉比原来的 Dell Chromebook 11 屏幕改进挺明显的,老一批 Chromebook 备受诟病的屏幕可视角度问题在这里就基本没有了,只是我发现 1080p 的屏幕默认选择的“最优”分辨率是 1536x864。\n\n我把分辨率改为 1920x1080 的话 Chrome OS 的显示体验就比较差了,网页字体、标题栏、工具栏都显得特别小;这种情况下只能将缩放调至 125% 或者 150%,不过即使这样也只是网页显示正常了,标题栏和工具栏并没有跟进缩放。这样的显示效果甚至还不及 Windows 10 高分屏设备上的 Chrome 浏览器,鉴于 Chrome OS 对高分屏的支持也比较天残,我也只能保持默认“最优”分辨率了。\n\nChrome OS 必备的爬墙激活这里就不说了,激活成功之后一般就可以领取 90 天的 Play Music 和两年 100 GB 的 Google Drive 空间了。无意外的话连上网络就会有系统更新了,更新到最新的 Chrome OS 64 稳定版本,系统就已经自带新的 Play 商店支持了。\n\n在设置中开启 Play 商店之后,如果你的梯子没什么问题的话应该就可以打开 Play 商店安装常用的 App 了,Chrome OS 自带的 Android 容器为 7.1.1 版本,相比原来的 Chrome ARC 运行时来说 App 的兼容性还是有质的提升的。\n\n我专门测试了网易云音乐、手机 QQ 等几个原来 Chrome ARC 无法运行的 App,在新的 Android 容器中运行都没有什么问题,起码 Chromebook 上使用 QQ 除了 crouton 中运行 Wine QQ 之外终于有直接的解决方案了。\n\n按照我平常基本用 Chromebook 浏览网页,偶尔看点 HTML5 视频的使用场景,这款笔记本还是基本能达到标称的 12 小时续航时间的,这也是 Chrome OS 相对 Windows 等系统的一大优势了。\n\nAcer Chromebook 14 这一段时间的总体使用体验还是比较令人满意的,碰到一个未解决的小问题就是原来通过 crouton 安装的 Ubuntu 14.04 系统无法以 xorg 方式正常启动图形界面了,只能以 [xiwi](https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)) 窗口形式启动图形界面,更新 crouton 也没有解决,不过还好这个影响不是很大咯。",
      "json_metadata": "{\"tags\":[\"chromebook\",\"acer\",\"chromeos\"],\"image\":[\"https://storage.live.com/items/F799A497307A642E!6495?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6496?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6497?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6499?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6498?a.jpg\"],\"links\":[\"https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps\",\"https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "acer",
      "permlink": "acer-chromebook-14",
      "title": "新玩具 Acer Chromebook 14"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T04:05:00",
  "trx_id": "80b4030d38f6bf96e4e0791d38335869350cdfad",
  "trx_in_block": 31,
  "virtual_op": 0
}
2018/08/14 04:01:03
authorzohead
body好像 steemit 更新的比博客勤快多了呢。。。
json metadata{"tags":["food"],"app":"steemit/0.1"}
parent authorhannahwu
parent permlink6weejs
permlinkre-hannahwu-6weejs-20180814t040108775z
title
Transaction InfoBlock #25050144/Trx 3ffd8d100974d7211a759047fe1f4a014fd495ed
View Raw JSON Data
{
  "block": 25050144,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "好像 steemit 更新的比博客勤快多了呢。。。",
      "json_metadata": "{\"tags\":[\"food\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "hannahwu",
      "parent_permlink": "6weejs",
      "permlink": "re-hannahwu-6weejs-20180814t040108775z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-14T04:01:03",
  "trx_id": "3ffd8d100974d7211a759047fe1f4a014fd495ed",
  "trx_in_block": 29,
  "virtual_op": 0
}
2018/08/13 18:08:51
authorsteemitboard
bodyCongratulations @zohead! You have received a personal award! [![](https://steemitimages.com/70x70/http://steemitboard.com/@zohead/birthday1.png)](http://steemitboard.com/@zohead) 1 Year on Steemit <sub>_Click on the badge to view your Board of Honor._</sub> > Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorzohead
parent permlinkacer-chromebook-14
permlinksteemitboard-notify-zohead-20180813t180850000z
title
Transaction InfoBlock #25038303/Trx db1d296ca57984a96f21c5fff448306d2d8306e6
View Raw JSON Data
{
  "block": 25038303,
  "op": [
    "comment",
    {
      "author": "steemitboard",
      "body": "Congratulations @zohead! You have received a personal award!\n\n[![](https://steemitimages.com/70x70/http://steemitboard.com/@zohead/birthday1.png)](http://steemitboard.com/@zohead)  1 Year on Steemit\n<sub>_Click on the badge to view your Board of Honor._</sub>\n\n\n> Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "zohead",
      "parent_permlink": "acer-chromebook-14",
      "permlink": "steemitboard-notify-zohead-20180813t180850000z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-13T18:08:51",
  "trx_id": "db1d296ca57984a96f21c5fff448306d2d8306e6",
  "trx_in_block": 0,
  "virtual_op": 0
}
zoheadpublished a new post: acer-chromebook-14
2018/08/13 16:16:18
authorzohead
body## Acer Chromebook 14 这两年非工作场合下我的主力电脑已经切换到 Chromebook,一直用的是 Dell Chromebook 11 这款小屏幕的笔记本,习惯了 Linux 之后 Chrome OS 用起来还是比较舒心的。 只是当我发现 Google 新推出的 Android Apps on Chrome OS 不再支持 Dell Chromebook 11 之类的老设备时(基本上所有使用赛扬 2955U 处理器的 Chromebook 都被排除了,具体可以参考 [Chrome OS Systems Supporting Android Apps](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps)),虽然觉得有点不爽,但还是下定决心准备换新的 Chromebook 设备了。 刚好最近看到一款之前关注过的 Acer Chromebook 14 笔记本,确认在 Play 商店支持的设备列表中就有了兴趣了,配置虽然和老的 Dell Chromebook 11 看起来差不了多少,但主要机身也更轻薄了,屏幕也升级到 1080p 了,立马准备拿下。 ## 基本配置 简单列一下配置: |项目|内容| |--|--| |型号|CB3-431-C0AK| |CPU|Intel® Celeron® N3160 1.6 GHz| |内存|LPDDR3 4GB| |存储|32GB eMMC 闪存| |屏幕|14 寸 IPS 1920x1080| |核显|Intel® HD Graphics 400| |无线|802.11a/b/g/n/ac| |接口|2x USB 3.0、HDMI| 其实除了 eMMC 闪存速度一般之外,其它方面我还是比较满意的,由于内存和闪存都是焊在主板上的,也基本没有升级的可能了,当然如果能像 Dell Chromebook 11 一样能自带读卡器就更好了。 ## 外观 先看看 Acer Chromebook 14 的正面和背面: ![Acer Chromebook 14 正面](https://storage.live.com/items/F799A497307A642E!6495?a.jpg) ![Acer Chromebook 14 背面](https://storage.live.com/items/F799A497307A642E!6496?a.jpg) 这款 Chromebook 直接没有风扇,全金属的机身设计的很轻薄,两面都异常简单。 再看看打开后的 B 面和 C 面: ![Acer Chromebook 14 B 面和 C 面](https://storage.live.com/items/F799A497307A642E!6497?a.jpg) 支持多点触控的触控板占了 C 面很大的面积,由于 Chromebook 对于多点触控现在支持的也非常好了,我用 Chromebook 时基本都不用鼠标了,键盘相比原来的 Dell Chromebook 11 手感也更好了: ![Acer Chromebook 14 触控板和键盘](https://storage.live.com/items/F799A497307A642E!6499?a.jpg) 充电孔和耳机插孔都在机身右侧,该 Chromebook 的所有接口都在机身左侧,下面的图也可以看看键盘效果: ![Acer Chromebook 14 接口](https://storage.live.com/items/F799A497307A642E!6498?a.jpg) ## 使用体验 Acer Chromebook 14 拿上手开机之后还是感觉比原来的 Dell Chromebook 11 屏幕改进挺明显的,老一批 Chromebook 备受诟病的屏幕可视角度问题在这里就基本没有了,只是我发现 1080p 的屏幕默认选择的“最优”分辨率是 1536x864。 我把分辨率改为 1920x1080 的话 Chrome OS 的显示体验就比较差了,网页字体、标题栏、工具栏都显得特别小;这种情况下只能将缩放调至 125% 或者 150%,不过即使这样也只是网页显示正常了,标题栏和工具栏并没有跟进缩放。这样的显示效果甚至还不及 Windows 10 高分屏设备上的 Chrome 浏览器,鉴于 Chrome OS 对高分屏的支持也比较天残,我也只能保持默认“最优”分辨率了。 Chrome OS 必备的爬墙激活这里就不说了,激活成功之后一般就可以领取 90 天的 Play Music 和两年 100 GB 的 Google Drive 空间了。无意外的话连上网络就会有系统更新了,更新到最新的 Chrome OS 64 稳定版本,系统就已经自带新的 Play 商店支持了。 在设置中开启 Play 商店之后,如果你的梯子没什么问题的话应该就可以打开 Play 商店安装常用的 App 了,Chrome OS 自带的 Android 容器为 7.1.1 版本,相比原来的 Chrome ARC 运行时来说 App 的兼容性还是有质的提升的。 我专门测试了网易云音乐、手机 QQ 等几个原来 Chrome ARC 无法运行的 App,在新的 Android 容器中运行都没有什么问题,起码 Chromebook 上使用 QQ 除了 crouton 中运行 Wine QQ 之外终于有直接的解决方案了。 按照我平常基本用 Chromebook 浏览网页,偶尔看点 HTML5 视频的使用场景,这款笔记本还是基本能达到标称的 12 小时续航时间的,这也是 Chrome OS 相对 Windows 等系统的一大优势了。 Acer Chromebook 14 这一段时间的总体使用体验还是比较令人满意的,碰到一个未解决的小问题就是原来通过 crouton 安装的 Ubuntu 14.04 系统无法以 xorg 方式正常启动图形界面了,只能以 [xiwi](https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)) 窗口形式启动图形界面,更新 crouton 也没有解决,不过还好这个影响不是很大咯。
json metadata{"tags":["acer","chromebook","chromeos"],"image":["https://storage.live.com/items/F799A497307A642E!6495?a.jpg","https://storage.live.com/items/F799A497307A642E!6496?a.jpg","https://storage.live.com/items/F799A497307A642E!6497?a.jpg","https://storage.live.com/items/F799A497307A642E!6499?a.jpg","https://storage.live.com/items/F799A497307A642E!6498?a.jpg"],"links":["https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps","https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkacer
permlinkacer-chromebook-14
title新玩具 Acer Chromebook 14
Transaction InfoBlock #25036053/Trx 329ce31ef3ddb474fd30543511fcb2f72a4e80f9
View Raw JSON Data
{
  "block": 25036053,
  "op": [
    "comment",
    {
      "author": "zohead",
      "body": "## Acer Chromebook 14\n\n这两年非工作场合下我的主力电脑已经切换到 Chromebook,一直用的是 Dell Chromebook 11 这款小屏幕的笔记本,习惯了 Linux 之后 Chrome OS 用起来还是比较舒心的。\n\n只是当我发现 Google 新推出的 Android Apps on Chrome OS 不再支持 Dell Chromebook 11 之类的老设备时(基本上所有使用赛扬 2955U 处理器的 Chromebook 都被排除了,具体可以参考 [Chrome OS Systems Supporting Android Apps](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps)),虽然觉得有点不爽,但还是下定决心准备换新的 Chromebook 设备了。\n\n刚好最近看到一款之前关注过的 Acer Chromebook 14 笔记本,确认在 Play 商店支持的设备列表中就有了兴趣了,配置虽然和老的 Dell Chromebook 11 看起来差不了多少,但主要机身也更轻薄了,屏幕也升级到 1080p 了,立马准备拿下。\n\n## 基本配置\n\n简单列一下配置:\n\n|项目|内容|\n|--|--|\n|型号|CB3-431-C0AK|\n|CPU|Intel® Celeron® N3160 1.6 GHz|\n|内存|LPDDR3 4GB|\n|存储|32GB eMMC 闪存|\n|屏幕|14 寸 IPS 1920x1080|\n|核显|Intel® HD Graphics 400|\n|无线|802.11a/b/g/n/ac|\n|接口|2x USB 3.0、HDMI|\n\n其实除了 eMMC 闪存速度一般之外,其它方面我还是比较满意的,由于内存和闪存都是焊在主板上的,也基本没有升级的可能了,当然如果能像 Dell Chromebook 11 一样能自带读卡器就更好了。\n\n## 外观\n\n先看看 Acer Chromebook 14 的正面和背面:\n\n![Acer Chromebook 14 正面](https://storage.live.com/items/F799A497307A642E!6495?a.jpg)\n\n![Acer Chromebook 14 背面](https://storage.live.com/items/F799A497307A642E!6496?a.jpg)\n\n这款 Chromebook 直接没有风扇,全金属的机身设计的很轻薄,两面都异常简单。\n\n再看看打开后的 B 面和 C 面:\n\n![Acer Chromebook 14 B 面和 C 面](https://storage.live.com/items/F799A497307A642E!6497?a.jpg)\n\n支持多点触控的触控板占了 C 面很大的面积,由于 Chromebook 对于多点触控现在支持的也非常好了,我用 Chromebook 时基本都不用鼠标了,键盘相比原来的 Dell Chromebook 11 手感也更好了:\n\n![Acer Chromebook 14 触控板和键盘](https://storage.live.com/items/F799A497307A642E!6499?a.jpg)\n\n充电孔和耳机插孔都在机身右侧,该 Chromebook 的所有接口都在机身左侧,下面的图也可以看看键盘效果:\n\n![Acer Chromebook 14 接口](https://storage.live.com/items/F799A497307A642E!6498?a.jpg)\n\n## 使用体验\n\nAcer Chromebook 14 拿上手开机之后还是感觉比原来的 Dell Chromebook 11 屏幕改进挺明显的,老一批 Chromebook 备受诟病的屏幕可视角度问题在这里就基本没有了,只是我发现 1080p 的屏幕默认选择的“最优”分辨率是 1536x864。\n\n我把分辨率改为 1920x1080 的话 Chrome OS 的显示体验就比较差了,网页字体、标题栏、工具栏都显得特别小;这种情况下只能将缩放调至 125% 或者 150%,不过即使这样也只是网页显示正常了,标题栏和工具栏并没有跟进缩放。这样的显示效果甚至还不及 Windows 10 高分屏设备上的 Chrome 浏览器,鉴于 Chrome OS 对高分屏的支持也比较天残,我也只能保持默认“最优”分辨率了。\n\nChrome OS 必备的爬墙激活这里就不说了,激活成功之后一般就可以领取 90 天的 Play Music 和两年 100 GB 的 Google Drive 空间了。无意外的话连上网络就会有系统更新了,更新到最新的 Chrome OS 64 稳定版本,系统就已经自带新的 Play 商店支持了。\n\n在设置中开启 Play 商店之后,如果你的梯子没什么问题的话应该就可以打开 Play 商店安装常用的 App 了,Chrome OS 自带的 Android 容器为 7.1.1 版本,相比原来的 Chrome ARC 运行时来说 App 的兼容性还是有质的提升的。\n\n我专门测试了网易云音乐、手机 QQ 等几个原来 Chrome ARC 无法运行的 App,在新的 Android 容器中运行都没有什么问题,起码 Chromebook 上使用 QQ 除了 crouton 中运行 Wine QQ 之外终于有直接的解决方案了。\n\n按照我平常基本用 Chromebook 浏览网页,偶尔看点 HTML5 视频的使用场景,这款笔记本还是基本能达到标称的 12 小时续航时间的,这也是 Chrome OS 相对 Windows 等系统的一大优势了。\n\nAcer Chromebook 14 这一段时间的总体使用体验还是比较令人满意的,碰到一个未解决的小问题就是原来通过 crouton 安装的 Ubuntu 14.04 系统无法以 xorg 方式正常启动图形界面了,只能以 [xiwi](https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)) 窗口形式启动图形界面,更新 crouton 也没有解决,不过还好这个影响不是很大咯。",
      "json_metadata": "{\"tags\":[\"acer\",\"chromebook\",\"chromeos\"],\"image\":[\"https://storage.live.com/items/F799A497307A642E!6495?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6496?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6497?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6499?a.jpg\",\"https://storage.live.com/items/F799A497307A642E!6498?a.jpg\"],\"links\":[\"https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps\",\"https://github.com/dnschneid/crouton/wiki/crouton-in-a-Chromium-OS-window-(xiwi)\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "acer",
      "permlink": "acer-chromebook-14",
      "title": "新玩具 Acer Chromebook 14"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-13T16:16:18",
  "trx_id": "329ce31ef3ddb474fd30543511fcb2f72a4e80f9",
  "trx_in_block": 10,
  "virtual_op": 0
}
steemdelegated 18.044 SP to @zohead
2018/08/13 15:30:45
delegateezohead
delegatorsteem
vesting shares29347.277290 VESTS
Transaction InfoBlock #25035143/Trx 0e2c8c1ae114005d61e1472648af02473b7089e3
View Raw JSON Data
{
  "block": 25035143,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "29347.277290 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-13T15:30:45",
  "trx_id": "0e2c8c1ae114005d61e1472648af02473b7089e3",
  "trx_in_block": 4,
  "virtual_op": 0
}
zoheadupdated their account properties
2018/08/13 15:13:51
accountzohead
json metadata{"profile":{"profile_image":"https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443","name":"Uranus Zhou","about":"Soul Of Free Loop","website":"https://zohead.com/"}}
memo keySTM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo
Transaction InfoBlock #25034805/Trx 6c2079676842079b0eff9fe5592f69a5e1ffd902
View Raw JSON Data
{
  "block": 25034805,
  "op": [
    "account_update",
    {
      "account": "zohead",
      "json_metadata": "{\"profile\":{\"profile_image\":\"https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443\",\"name\":\"Uranus Zhou\",\"about\":\"Soul Of Free Loop\",\"website\":\"https://zohead.com/\"}}",
      "memo_key": "STM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-08-13T15:13:51",
  "trx_id": "6c2079676842079b0eff9fe5592f69a5e1ffd902",
  "trx_in_block": 43,
  "virtual_op": 0
}
steemdelegated 5.620 SP to @zohead
2018/05/17 03:57:54
delegateezohead
delegatorsteem
vesting shares9140.765374 VESTS
Transaction InfoBlock #22499056/Trx fb1fe544205b4b4f264b1824491dd54e0bce4a35
View Raw JSON Data
{
  "block": 22499056,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "9140.765374 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-05-17T03:57:54",
  "trx_id": "fb1fe544205b4b4f264b1824491dd54e0bce4a35",
  "trx_in_block": 7,
  "virtual_op": 0
}
steemdelegated 18.256 SP to @zohead
2018/01/09 07:15:48
delegateezohead
delegatorsteem
vesting shares29691.853323 VESTS
Transaction InfoBlock #18820312/Trx 6009abf3af8f4141ebcf2804b24e305fac534e32
View Raw JSON Data
{
  "block": 18820312,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "29691.853323 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-01-09T07:15:48",
  "trx_id": "6009abf3af8f4141ebcf2804b24e305fac534e32",
  "trx_in_block": 42,
  "virtual_op": 0
}
steemdelegated 18.410 SP to @zohead
2017/08/04 05:12:36
delegateezohead
delegatorsteem
vesting shares29942.699772 VESTS
Transaction InfoBlock #14271337/Trx d8a2e69a053d63893c574357b9b0033e240374a7
View Raw JSON Data
{
  "block": 14271337,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "zohead",
      "delegator": "steem",
      "vesting_shares": "29942.699772 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2017-08-04T05:12:36",
  "trx_id": "d8a2e69a053d63893c574357b9b0033e240374a7",
  "trx_in_block": 16,
  "virtual_op": 0
}
2017/07/27 09:59:51
idfollow
json["follow",{"follower":"zohead","following":"hannahwu","what":["blog"]}]
required auths[]
required posting auths["zohead"]
Transaction InfoBlock #14046818/Trx d8f4211cb27d6b3a2574e6058255d64d0dfee212
View Raw JSON Data
{
  "block": 14046818,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"zohead\",\"following\":\"hannahwu\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "zohead"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2017-07-27T09:59:51",
  "trx_id": "d8f4211cb27d6b3a2574e6058255d64d0dfee212",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemcreated a new account: @zohead
2017/07/21 06:53:12
active{"account_auths":[],"key_auths":[["STM6ABQSukpVQaUYwvvnXC5aoVM7g3DGBXQq7mLz2L7NrgXU6zgw2",1]],"weight_threshold":1}
creatorsteem
delegation57000.000000 VESTS
extensions[]
fee0.500 STEEM
json metadata
memo keySTM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo
new account namezohead
owner{"account_auths":[],"key_auths":[["STM7VLwkYHrxWAJrLtjPyKyc3A4Pigsx6M1EFDtv5haaPGY9QkMb3",1]],"weight_threshold":1}
posting{"account_auths":[],"key_auths":[["STM7fvVpcoYsrKR857KVR1NDV3o4S8CP75rvQQxXVAG1ViGr8LMKV",1]],"weight_threshold":1}
Transaction InfoBlock #13870434/Trx 853894c42d009463c199edb101c6b0d42070dbd9
View Raw JSON Data
{
  "block": 13870434,
  "op": [
    "account_create_with_delegation",
    {
      "active": {
        "account_auths": [],
        "key_auths": [
          [
            "STM6ABQSukpVQaUYwvvnXC5aoVM7g3DGBXQq7mLz2L7NrgXU6zgw2",
            1
          ]
        ],
        "weight_threshold": 1
      },
      "creator": "steem",
      "delegation": "57000.000000 VESTS",
      "extensions": [],
      "fee": "0.500 STEEM",
      "json_metadata": "",
      "memo_key": "STM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo",
      "new_account_name": "zohead",
      "owner": {
        "account_auths": [],
        "key_auths": [
          [
            "STM7VLwkYHrxWAJrLtjPyKyc3A4Pigsx6M1EFDtv5haaPGY9QkMb3",
            1
          ]
        ],
        "weight_threshold": 1
      },
      "posting": {
        "account_auths": [],
        "key_auths": [
          [
            "STM7fvVpcoYsrKR857KVR1NDV3o4S8CP75rvQQxXVAG1ViGr8LMKV",
            1
          ]
        ],
        "weight_threshold": 1
      }
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2017-07-21T06:53:12",
  "trx_id": "853894c42d009463c199edb101c6b0d42070dbd9",
  "trx_in_block": 17,
  "virtual_op": 0
}

Account Metadata

POSTING JSON METADATA
profile{"profile_image":"https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443","name":"Uranus Zhou","about":"Soul Of Free Loop","website":"https://zohead.com/"}
JSON METADATA
profile{"profile_image":"https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443","name":"Uranus Zhou","about":"Soul Of Free Loop","website":"https://zohead.com/"}
{
  "posting_json_metadata": {
    "profile": {
      "profile_image": "https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443",
      "name": "Uranus Zhou",
      "about": "Soul Of Free Loop",
      "website": "https://zohead.com/"
    }
  },
  "json_metadata": {
    "profile": {
      "profile_image": "https://cn.gravatar.com/avatar/6155f7f918b0a6ed5030311b5300c443",
      "name": "Uranus Zhou",
      "about": "Soul Of Free Loop",
      "website": "https://zohead.com/"
    }
  }
}

Auth Keys

Owner
Single Signature
Public Keys
STM7VLwkYHrxWAJrLtjPyKyc3A4Pigsx6M1EFDtv5haaPGY9QkMb31/1
Active
Single Signature
Public Keys
STM6ABQSukpVQaUYwvvnXC5aoVM7g3DGBXQq7mLz2L7NrgXU6zgw21/1
Posting
Single Signature
Public Keys
STM7fvVpcoYsrKR857KVR1NDV3o4S8CP75rvQQxXVAG1ViGr8LMKV1/1
Memo
STM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo
{
  "owner": {
    "account_auths": [],
    "key_auths": [
      [
        "STM7VLwkYHrxWAJrLtjPyKyc3A4Pigsx6M1EFDtv5haaPGY9QkMb3",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "active": {
    "account_auths": [],
    "key_auths": [
      [
        "STM6ABQSukpVQaUYwvvnXC5aoVM7g3DGBXQq7mLz2L7NrgXU6zgw2",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "posting": {
    "account_auths": [],
    "key_auths": [
      [
        "STM7fvVpcoYsrKR857KVR1NDV3o4S8CP75rvQQxXVAG1ViGr8LMKV",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "memo": "STM6Yismp7JKyPUC291BiDUXLzvKnShuei5WCYfugnZvg7kmq92Eo"
}

Witness Votes

0 / 30
No active witness votes.
[]