<!DOCTYPE html>
<html>
<head>   
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

<title>keychain_cat - 查看/修改keychain2数据的工具 - 夢沉抹大拉</title>
<meta name="description" content="生命不息，折腾不止" />

<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/assets/images/profile.png"/>

<link rel="stylesheet" type="text/css" href="/assets/css/normalize.min.css" />
<link rel="stylesheet" type="text/css" href="/assets/css/screen.min.css" />
<!-- This is for syntax highlight -->
<link rel="stylesheet" type="text/css" href="/assets/css/syntax.min.css">
<!-- Customisation  -->
<link rel="stylesheet" type="text/css" href="/assets/css/main.min.css">
</head>
<body class="home-template">
<main class="content" role="main">

	<article class="post">
		<header class="post-header">
			
				<span class="blog-title"><a id="blog-logo" href="/">夢沉抹大拉</a></span>
			 
			&nbsp;&nbsp;
			<span class="post-meta">
			
				<span style="color:#646A58">theos on</span>
			
				<time datetime="2014-12-26">
					<a href="/archive/2014/">26 Dec 2014</a>
				</time>
			</span>
		</header>

		<h2 class="post-title">keychain_cat - 查看/修改keychain2数据的工具</h2>

		<section class="post-content">
			<p>最近需要修改iOS keychain中kSecClassGenericPassword的数据，不过没有找到方便的工具，于是抽时间自己写了一个。工具可以批量查询/删除，或者更新kSecClassGenericPassword中的单条<code>v_Data</code>记录，方便进行调试。<a href="https://github.com/upbit/My-iDevice-Tools/blob/master/keychain_cat.mm">keychain_cat的源码</a> 放在GitHub上。</p>

<hr />

<h2 id="使用方法">使用方法</h2>

<pre><code>$ ./keychain_cat -h
Usage: ./keychain_cat [options]
-d --dump                       Dump Keychain AccessGroups
-U --update                     UPDATE v_Data with specified value &lt;-g&gt; &lt;-s&gt; &lt;-a&gt; &lt;-v&gt;
-D --delete                     DELETE keychain with &lt;-g&gt; (-s) (-a)
-g --group &lt;AccessGroup&gt;        kSecAttrAccessGroup
-s --service &lt;Service&gt;          kSecAttrService
-a --account &lt;Account&gt;          kSecAttrAccount
-v --value &lt;v_Data&gt;             (UPDATE only) kSecValueData

&lt;SecClass selector&gt;
  -G --generic-password         <PASSWORD>
  -N --internet-password        <PASSWORD>
  -I --identity                 kSecClassIdentity
  -C --certificate              kSecClassCertificate
  -K --classKey                 kSecClassKey

-h --help                       Show this help
</code></pre>

<h3 id="1-查看-accessgroup">[1] 查看 AccessGroup</h3>

<p>因为修改需要知道 keychain-access-groups。这里参考 <a href="https://github.com/upbit/Keychain-Dumper/blob/master/main.m#L56">Keychain-Dumper</a> 里的 dumpKeychainEntitlements()，实现列举机器上所有 AccessGroup功能：</p>

<pre><code>$ ./keychain_cat -d
&gt;&gt; keychain-access-groups:
  6WX5RKLG95.com.supercell.reef
  88L2Q4487U.com.tencent.mttlite
  apple
  com.apple.ProtectedCloudStorage
  com.apple.PublicCloudStorage
  com.apple.apsd
  com.apple.assistant
  com.apple.cloudd
  com.apple.ind
  com.apple.security.sos
</code></pre>

<h3 id="2-查看某个-accessgroup-下的所有数据">[2] 查看某个 AccessGroup 下的所有数据</h3>

<p>知道 AccessGroup 的名称，就可以使用 –group 来指定查询目标：</p>

<pre><code>./keychain_cat -g 6WX5RKLG95.com.supercell.reef
&lt;AccessGroup:6WX5RKLG95.com.supercell.reef, Service:com.supercell, Account:appRated&gt;
{
  accc = "&lt;SecAccessControlRef: 0x15563b70&gt;";
  acct = appRated;
  agrp = "6WX5RKLG95.com.supercell.reef";
  cdat = "2014-11-08 23:33:28 +0000";
  class = genp;
  invi = 1;
  labl = Supercell;
  mdat = "2014-11-08 23:33:28 +0000";
  pdmn = ak;
  svce = "com.supercell";
  sync = 0;
  tomb = 0;
  "v_Data" = TRUE;
}
...
&lt;AccessGroup:6WX5RKLG95.com.supercell.reef, Service:com.supercell, Account:THLevel&gt;
{
  accc = "&lt;SecAccessControlRef: 0x15563b80&gt;";
  acct = THLevel;
  agrp = "6WX5RKLG95.com.supercell.reef";
  cdat = "2014-12-07 12:03:07 +0000";
  class = genp;
  invi = 1;
  labl = Supercell;
  mdat = "2014-12-07 12:03:07 +0000";
  pdmn = ak;
  svce = "com.supercell";
  sync = 0;
  tomb = 0;
  "v_Data" = 13;
}
</code></pre>

<h3 id="3-查看某个-accessgroupserviceaccount-的数据">[3] 查看某个 AccessGroup,Service,Account 的数据</h3>

<p>当然还可以用 –service 和 –account 来进一步筛选结果。特别的，如果可能v_Data会自动解析成NSString:</p>

<pre><code>$ ./keychain_cat -g 6WX5RKLG95.com.supercell.reef -s com.supercell -a THLevel
&lt;AccessGroup:6WX5RKLG95.com.supercell.reef, Service:com.supercell, Account:THLevel&gt;
{
  accc = "&lt;SecAccessControlRef: 0x16665dc0&gt;";
  acct = THLevel;
  agrp = "6WX5RKLG95.com.supercell.reef";
  cdat = "2014-12-07 12:03:07 +0000";
  class = genp;
  invi = 1;
  labl = Supercell;
  mdat = "2014-12-07 12:03:07 +0000";
  pdmn = ak;
  svce = "com.supercell";
  sync = 0;
  tomb = 0;
  "v_Data" = 13;
}
</code></pre>

<h3 id="4-修改某个-comsupercellthlevel-的数据">[4] 修改某个 com.supercell,THLevel 的数据</h3>

<p>当指定 –group,–service,–account 后，会定位到单独的一条记录，此时可以使用 –update 来更新数据。使用 –value 指定要修改的v_Data内容（字符串）。</p>

<p>比如修改 com.supercell,THLevel 的v_Data为 99：</p>

<pre><code>./keychain_cat -g 6WX5RKLG95.com.supercell.reef -s com.supercell -a THLevel -v 99 -U
Origin: {
  accc = "&lt;SecAccessControlRef: 0x146798d0&gt;";
  acct = THLevel;
  agrp = "6WX5RKLG95.com.supercell.reef";
  cdat = "2014-12-07 12:03:07 +0000";
  invi = 1;
  labl = Supercell;
  mdat = "2014-12-07 12:03:07 +0000";
  pdmn = ak;
  svce = "com.supercell";
  sync = 0;
  tomb = 0;
  "v_Data" = &lt;3133&gt;;
}
&gt;&gt; Update v_Data to: &lt;3939&gt;
</code></pre>

<p>可以看到，写入的NSData实际是 &lt;3939&gt;，此时去掉 –update,–value 查看，会看到THLevel已经被改成字符串99了。</p>

<h2 id="局限性">局限性</h2>

<p>目前只支持修改 kSecClassGenericPassword 的v_Data，并且只支持字符串输入。</p>

<p>如果有其它需求，可以在GitHub上给我提issue：<a href="https://github.com/upbit/My-iDevice-Tools/issues">https://github.com/upbit/My-iDevice-Tools/issues</a></p>

		</section>

		<footer class="post-footer">
		<!-- If we want to display author's name and bio -->
		
			<section class="author">
				<header> <a href="/about/"> <img class="profile" src="/assets/images/profile.png" alt="Author's profile picture"></a></header>
				<article>
					<!-- Author Name -->
					<h4> 木桩 </h4>
					<!-- Author Bio -->
					<p> 
					这是一个追寻抹大拉之地的geeker<BR />
					生命不息，折腾不止。
					</p>
				</article>
			</section>				
		

		
		<section class="share">
			<h4>Share this post</h4>
			<a class="icon-twitter" href="http://twitter.com/share?text=keychain_cat - 查看/修改keychain2数据的工具&amp;url=http://blog.imaou.com/theos/2014/12/26/keychain_cat_tool.html"
				onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
				<span class="hidden">Twitter</span>
			</a>
			<a class="icon-facebook" href="https://www.facebook.com/sharer/sharer.php?u=http://blog.imaou.com/theos/2014/12/26/keychain_cat_tool.html"
				onclick="window.open(this.href, 'facebook-share','width=580,height=296');return false;">
				<span class="hidden">Facebook</span>
			</a>
			<a class="icon-google-plus" href="https://plus.google.com/share?url=http://blog.imaou.com/theos/2014/12/26/keychain_cat_tool.html"
			   onclick="window.open(this.href, 'google-plus-share', 'width=490,height=530');return false;">
				<span class="hidden">Google+</span>
			</a>
		</section>
		
		
		
		<section class="disqus">
			<div id="disqus_thread"></div>
			<script type="text/javascript">
				/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
				var disqus_shortname = 'magdalene'; // required: replace example with your forum shortname

				/* * * DON'T EDIT BELOW THIS LINE * * */
				(function() {
					var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
					dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
					(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
					})();
			</script>
			<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
			<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
		</section>
		
			
		</footer>

	</article>

</main>

	<footer class="site-footer">
		<a class="subscribe icon-feed" href="/archive/2019/"><span class="tooltip">Archive 2019</span></a>
		<div class="inner">
			 <section class="copyright">All content copyright <a href="/">夢沉抹大拉</a> &copy;  &bull; All rights reserved.</section>
			 <section class="poweredby">Made with Jekyll using <a href="http://github.com/rosario/kasper">Kasper theme</a></section>
		</div>
	</footer>

	<script asynsc type="text/javascript" src="/assets/js/jquery-1.11.1.min.js"></script>
	<script asynsc type="text/javascript" src="/assets/js/jquery.fitvids.js"></script>
	<script asynsc type="text/javascript" src="/assets/js/index.js"></script>

	<script type="text/javascript">
		(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
		(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
		m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
		})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

		ga('create', 'UA-53731588-1', 'auto');
		ga('send', 'pageview');

	</script>

</body>
</html>