分類: 3C資訊

  • 源碼學習系列之SpringBoot自動配置(篇二)

    源碼學習系列之SpringBoot自動配置(篇二)

    源碼學習系列之SpringBoot自動配置(篇二)之HttpEncodingAutoConfiguration 源碼分析

    繼上一篇博客之後,本博客繼續跟一下SpringBoot的自動配置源碼

    ok,先複習一下上一篇的內容,從前面的學習,我們知道了SpringBoot的自動配置主要是由一個選擇器AutoConfigurationImportSelector,先通過選擇器將自動配置的類加載到Spring容器

    注意點:

    • List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);獲取的候選配置的類名
    • 由SpringFactoriesLoader加載器負責加載配置類名,已經裝載配置類到容器,SpringFactoriesLoader的loadSpringFactories方法讀取自動配置工程的META-INF/spring.factories配置文件,加載配置類的全類名,包裝成Properties對象,然後再加載到容器里
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        /* 將spring.factories的類都裝載到Spring容器*/
         public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
    
            try {
            //將META-INF/spring.factories文件里配置的屬性都裝載到Enumeration數據結構里
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
                //遍歷獲取屬性,然後再獲取對應的配置類全類名
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
    
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
            }
        }
    

    ok,Springboot的自動配置類都在這個包里,源碼很多,所以本博客只是簡單跟一下源碼

    自動配置可以說是SpringBoot框架的一個很重要的功能,其強大的功能就是通過很多配置類實現的,當然這麼多配置,可以參考SpringBoot官方的配置參考:
    https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/common-application-properties.html

    SpringBoot的自動配置類很多,顯然不是每個配置類都生效的,比如你沒引對應的jar,那對應的配置類肯定是不起效的,ok,本博客以HttpEncodingAutoConfiguration自動編碼配置類為實例,記錄一下SpringBoot的自動配置

    先補充一些@Conditional註解的用法:詳情可以參考我上篇博客

    @Conditional派生註解 作用(都是判斷是否符合指定的條件)
    @ConditionalOnJava 系統的java版本是否符合要求
    @ConditionalOnBean 有指定的Bean類
    @ConditionalOnMissingBean 沒有指定的bean類
    @ConditionalOnExpression 符合指定的SpEL表達式
    @ConditionalOnClass 有指定的類
    @ConditionalOnMissingClass 沒有指定的類
    @ConditionalOnSingleCandidate 容器只有一個指定的bean,或者這個bean是首選bean
    @ConditionalOnProperty 指定的property屬性有指定的值
    @ConditionalOnResource 路徑下存在指定的資源
    @ConditionalOnWebApplication 系統環境是web環境
    @ConditionalOnNotWebApplication 系統環境不是web環境
    @ConditionalOnjndi JNDI存在指定的項

    通過上篇博客的學習,我們已經知道了SpringBoot有很多自動配置類,所以本博客拿HttpEncodingAutoConfiguration類來看看

    補充:

    • @Configuration proxyBeanMethods屬性:默認是開啟的,開啟后允許其它配置類調用這個類的@bean方法,詳情參看Spring官方文檔:
    package org.springframework.boot.autoconfigure.web.servlet;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
    import org.springframework.boot.autoconfigure.http.HttpProperties;
    import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
    import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.web.filter.CharacterEncodingFilter;
    
    @Configuration(
        proxyBeanMethods = false
    )//指定是一個配置列,關了proxyBeanMethods,其它配置類就不能調相應的@bean類
    @EnableConfigurationProperties({HttpProperties.class})//讓使用 @ConfigurationProperties註解的HttpProperties類生效,HttpProperties類通過ConfigurationProperties註解,將屬性配置一個一個加載進來
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )//指定系統環境是Web環境配置才起效,並且指定類型是SERVLET
    @ConditionalOnClass({CharacterEncodingFilter.class})//系統有CharacterEncodingFilter過濾器類,則配置類起效,CharacterEncodingFilter類:SpringMVC中進行亂碼解決的過濾器
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )//判斷配置文件是否有spring.http.encoding.enabled屬性,如果沒配置,也是默認為true的,因為配置了`matchIfMissing =true`
    public class HttpEncodingAutoConfiguration {
        //創建一個Encoding對象
        private final Encoding properties;
        // 構造函數里通過properties.getEncoding();進行屬性映射,獲取默認的配置
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
        @Bean
        @ConditionalOnMissingBean//如果系統沒有CharacterEncodingFilter類,就執行characterEncodingFilter方法
        public CharacterEncodingFilter characterEncodingFilter() {
            //重新創建一個編碼過濾器
            OrderedCharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            //設置默認的配置
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
            return filter;
        }
    
        @Bean
        public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
        }
    
        private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
            private final Encoding properties;
    
            LocaleCharsetMappingsCustomizer(Encoding properties) {
                this.properties = properties;
            }
    
            public void customize(ConfigurableServletWebServerFactory factory) {
                if(this.properties.getMapping() != null) {
                    factory.setLocaleCharsetMappings(this.properties.getMapping());
                }
    
            }
    
            public int getOrder() {
                return 0;
            }
        }
    }
    

    通過對HttpEncodingAutoConfiguration源碼的學習,可以看出,其實主要是用@Conditional及其派生註解,這些註解都是要在特定情況才會起效,起效了,才會將組件加載到Spring容器里

    ok,然後我們怎麼知道哪些配置是起效的?在SpringBoot項目里,是可以通過配置,開啟打印的,可以在application.properties加上debug=true屬性就可以

    控制台打印的Positive matches就表示有效的配置類

    console打印的Negative matches表示不起效的配置類:
    比如我的項目沒有加aop的,aop自動配置類就不起效

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

    ※評比南投搬家公司費用收費行情懶人包大公開

  • 010.Kubernetes二進制部署kube-controller-manager

    010.Kubernetes二進制部署kube-controller-manager

    一 部署高可用kube-controller-manager

    1.1 高可用kube-controller-manager介紹


    本實驗部署一個三實例 kube-controller-manager 的集群,啟動后將通過競爭選舉機制產生一個 leader 節點,其它節點為阻塞狀態。當 leader 節點不可用時,阻塞的節點將再次進行選舉產生新的 leader 節點,從而保證服務的可用性。

    為保證通信安全,本文檔先生成 x509 證書和私鑰,kube-controller-manager 在如下兩種情況下使用該證書:

    • 與 kube-apiserver 的安全端口通信;
    • 在安全端口(https,10252) 輸出 prometheus 格式的 metrics。

    1.2 創建kube-controller-manager證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cat > kube-controller-manager-csr.json <<EOF
      3 {
      4   "CN": "system:kube-controller-manager",
      5   "hosts": [
      6     "127.0.0.1",
      7     "172.24.8.71",
      8     "172.24.8.72",
      9     "172.24.8.73"
     10   ],
     11   "key": {
     12     "algo": "rsa",
     13     "size": 2048
     14   },
     15   "names": [
     16     {
     17       "C": "CN",
     18       "ST": "Shanghai",
     19       "L": "Shanghai",
     20       "O": "system:kube-controller-manager",
     21       "OU": "System"
     22     }
     23   ]
     24 }
     25 EOF
     26 #創建kube-controller-manager的CA證書請求文件



    解釋:

    hosts 列表包含所有 kube-controller-manager 節點 IP;

    CN 和 O 均為 system:kube-controller-manager,kubernetes 內置的 ClusterRoleBindings system:kube-controller-manager 賦予 kube-controller-manager 工作所需的權限。



      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
      3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
      4 -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


    1.3 分發證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     scp kube-controller-manager*.pem root@${master_ip}:/etc/kubernetes/cert/
      7   done


    1.4 創建和分發kubeconfig


    kube-controller-manager 使用 kubeconfig 文件訪問 apiserver,該文件提供了 apiserver 地址、嵌入的 CA 證書和 kube-controller-manager 證書:

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# kubectl config set-cluster kubernetes \
      4   --certificate-authority=/opt/k8s/work/ca.pem \
      5   --embed-certs=true \
      6   --server=${KUBE_APISERVER} \
      7   --kubeconfig=kube-controller-manager.kubeconfig
      8 
      9 [root@k8smaster01 work]# kubectl config set-credentials system:kube-controller-manager \
     10   --client-certificate=kube-controller-manager.pem \
     11   --client-key=kube-controller-manager-key.pem \
     12   --embed-certs=true \
     13   --kubeconfig=kube-controller-manager.kubeconfig
     14 
     15 [root@k8smaster01 work]# kubectl config set-context system:kube-controller-manager \
     16   --cluster=kubernetes \
     17   --user=system:kube-controller-manager \
     18   --kubeconfig=kube-controller-manager.kubeconfig
     19 
     20 [root@k8smaster01 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
     21 
     22 [root@k8smaster01 ~]# cd /opt/k8s/work
     23 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     24 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     25   do
     26     echo ">>> ${master_ip}"
     27     scp kube-controller-manager.kubeconfig root@${master_ip}:/etc/kubernetes/
     28   done


    1.5 創建kube-controller-manager的systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# cat > kube-controller-manager.service.template <<EOF
      4 [Unit]
      5 Description=Kubernetes Controller Manager
      6 Documentation=https://github.com/GoogleCloudPlatform/kubernetes
      7 
      8 [Service]
      9 WorkingDirectory=${K8S_DIR}/kube-controller-manager
     10 ExecStart=/opt/k8s/bin/kube-controller-manager \\
     11   --profiling \\
     12   --cluster-name=kubernetes \\
     13   --controllers=*,bootstrapsigner,tokencleaner \\
     14   --kube-api-qps=1000 \\
     15   --kube-api-burst=2000 \\
     16   --leader-elect \\
     17   --use-service-account-credentials\\
     18   --concurrent-service-syncs=2 \\
     19   --bind-address=##MASTER_IP## \\
     20   --secure-port=10252 \\
     21   --tls-cert-file=/etc/kubernetes/cert/kube-controller-manager.pem \\
     22   --tls-private-key-file=/etc/kubernetes/cert/kube-controller-manager-key.pem \\
     23   --port=0 \\
     24   --authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     25   --client-ca-file=/etc/kubernetes/cert/ca.pem \\
     26   --requestheader-allowed-names="" \\
     27   --requestheader-client-ca-file=/etc/kubernetes/cert/ca.pem \\
     28   --requestheader-extra-headers-prefix="X-Remote-Extra-" \\
     29   --requestheader-group-headers=X-Remote-Group \\
     30   --requestheader-username-headers=X-Remote-User \\
     31   --authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     32   --cluster-signing-cert-file=/etc/kubernetes/cert/ca.pem \\
     33   --cluster-signing-key-file=/etc/kubernetes/cert/ca-key.pem \\
     34   --experimental-cluster-signing-duration=8760h \\
     35   --horizontal-pod-autoscaler-sync-period=10s \\
     36   --concurrent-deployment-syncs=10 \\
     37   --concurrent-gc-syncs=30 \\
     38   --node-cidr-mask-size=24 \\
     39   --service-cluster-ip-range=${SERVICE_CIDR} \\
     40   --pod-eviction-timeout=6m \\
     41   --terminated-pod-gc-threshold=10000 \\
     42   --root-ca-file=/etc/kubernetes/cert/ca.pem \\
     43   --service-account-private-key-file=/etc/kubernetes/cert/ca-key.pem \\
     44   --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     45   --logtostderr=true \\
     46   --v=2
     47 Restart=on-failure
     48 RestartSec=5
     49 
     50 [Install]
     51 WantedBy=multi-user.target
     52 EOF


    1.6 分發systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for (( i=0; i < 3; i++ ))
      4   do
      5     sed -e "s/##MASTER_NAME##/${MASTER_NAMES[i]}/" -e "s/##MASTER_IP##/${MASTER_IPS[i]}/" kube-controller-manager.service.template > kube-controller-manager-${MASTER_IPS[i]}.service
      6   done						#修正相應IP
      7 [root@k8smaster01 work]# ls kube-controller-manager*.service
      8 [root@k8smaster01 ~]# cd /opt/k8s/work
      9 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     10 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     11   do
     12     echo ">>> ${master_ip}"
     13     scp kube-controller-manager-${master_ip}.service root@${master_ip}:/etc/systemd/system/kube-controller-manager.service
     14   done						#分發system


    二 啟動並驗證

    2.1 啟動kube-controller-manager 服務

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     ssh root@${master_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager"
      7     ssh root@${master_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager"
      8   done


    2.2 檢查kube-controller-manager 服務

      1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
      2 [root@k8smaster01 ~]# for master_ip in ${MASTER_IPS[@]}
      3   do
      4     echo ">>> ${master_ip}"
      5     ssh root@${master_ip} "systemctl status kube-controller-manager|grep Active"
      6   done



    2.3 查看輸出的 metrics

      1 [root@k8smaster01 ~]# curl -s --cacert /opt/k8s/work/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem https://172.24.8.71:10252/metrics |head


    注意:以上命令在 kube-controller-manager 節點上執行。

    2.4 查看權限

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:kube-controller-manager



    ClusteRole system:kube-controller-manager 的權限很小,只能創建 secret、serviceaccount 等資源對象,各 controller 的權限分散到 ClusterRole system:controller:XXX 中。

    當在 kube-controller-manager 的啟動參數中添加 –use-service-account-credentials=true 參數,這樣 main controller 會為各 controller 創建對應的 ServiceAccount XXX-controller。內置的 ClusterRoleBinding system:controller:XXX 將賦予各 XXX-controller ServiceAccount 對應的 ClusterRole system:controller:XXX 權限。

      1 [root@k8smaster01 ~]# kubectl get clusterrole|grep controller



    如deployment controller:

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:controller:deployment-controller


    2.5 查看當前leader

      1 [root@k8smaster01 ~]# kubectl get endpoints kube-controller-manager --namespace=kube-system  -o yaml



    kubelet 認證和授權:https://kubernetes.io/docs/admin/kubelet-authentication-authorization/#kubelet-authorization
    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 代碼注入及其拓展–逆向開發

    代碼注入及其拓展–逆向開發

    今天繼續講述逆向開發中另一個比較重要的課程是代碼注入內容,本篇篇幅比較長,但還是有很多乾貨的,希望大家通過此篇文章更加了解逆向開發中的要點和知識點.我們將分解幾個內容,進行講解:

    1. Framework注入
    2. Dylib注入
    3. MethodSwizzle
    4. 微信示例講解
    5. 總結

    讓代碼執行自己的代碼,整體方案如下:

    如何讓別人的app來執行自己的代碼呢? 這就要通過代碼注入的方式來達到,而代碼注入的方式有兩種: 一種是通過framework, 一種是dylib方式,另種方案,可以通過Runtime機制

    代碼注入思路:

    DYLD會動態加載動態庫Framework中所有動態庫,在frameworks中加入自己的一個動態庫,然後在動態庫中hook和注入代碼.

    一、FrameWork注入

     1.準備工作

    • 微信6.6.5(越獄應用)
    • MachOView軟件

      MachOView的下載地址:

      如果想看源碼如下:MachOView源碼:

    • yololib工具(給MachOView注入framework)

      yololib工具下載地址:

    • 簽名文件appsign文件

    2.流程

    2.1 加入準備工作,導入微信6.6.5版本以及腳本appSign.sh重簽名文件

    2.2 將appSign導入到項目腳本中

     

     

     2.3 有了上面的兩個步驟后,然後編譯一下工程,會出現一個temp工程,裡面包含了payload文件

    2.4 显示包內容,查看可執行文件

     2.5 我們通過MachOView軟件查看WeChat

    我們看到有很多的DYLIB,代表的是加載動態庫

    2.6  我們在項目中新建framework

     

    2.7 新建文件用於驗證

    2.8 想要達到剛加載就運行,代碼要寫在load方法

     2.9 編譯一下,查看app包位置會多出一個framework

    2.10 显示包內容,在framework查看

    由上可知,WJHookFrameWork已經加入成功。

    2.11 但是運行並沒有成功,沒有執行load里的代碼

    原因:用MachOView打開可執行的WeChat,沒有找到WJHookFrameWork

    下面我們講述怎麼將WJHookFramework寫入到MachoView文件中?

    3. WJHookFramework寫入到MachOView文件中

    需要使用yololib工具,建議將yololib放到 /usr/local/bin

    3.1 解壓越獄包

    3.2 將WeChat.app显示包內容,找到WeChat可執行的文件

    需要增加執行權限: chmod +x WeChat

    3.3 寫入WeChat可執行文件

    yololib WeChat Frameworks/WJHookFrameWork.framework/WJHookFrameWork

    通過上面的過程,查看MachOView文件Load commands中是否有WJHookFrameWork

    上面圖显示已經加入成功。

    3.4 刪除原有的ipa,打包payload

    zip -ry WeChat.ipa Payload

    將WeChat.ipa放入App目錄中,刪除其他的文件夾。

     

    3.5 再次運行,發現成功!!!

    上面就是framework方式代碼注入。大家可以私信我,如有不懂!!!

     二、Dylib注入

    2.1 新建工程,添加腳本到build phases 


    加入腳本文件

    2.2添加第三方庫dylib(mac os的,非ios)

    2.3 添加依賴

    2.4 修改第三方類庫僅限mac使用,修改Base SDK

    2.5 修改signing 

    2.6 腳本中注入動態庫的代碼

    # ${SRCROOT} 它是工程文件所在的目錄
    TEMP_PATH="${SRCROOT}/Temp"
    #資源文件夾,我們提前在工程目錄下新建一個APP文件夾,裏面放ipa包
    ASSETS_PATH="${SRCROOT}/APP"
    #目標ipa包路徑
    TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
    #清空Temp文件夾
    rm -rf "${SRCROOT}/Temp"
    mkdir -p "${SRCROOT}/Temp"
    
    #----------------------------------------
    # 1. 解壓IPA到Temp下
    unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
    # 拿到解壓的臨時的APP的路徑
    TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
    # echo "路徑是:$TEMP_APP_PATH"
    
    #----------------------------------------
    # 2. 將解壓出來的.app拷貝進入工程下
    # BUILT_PRODUCTS_DIR 工程生成的APP包的路徑
    # TARGET_NAME target名稱
    TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
    echo "app路徑:$TARGET_APP_PATH"
    
    rm -rf "$TARGET_APP_PATH"
    mkdir -p "$TARGET_APP_PATH"
    cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"
    
    #----------------------------------------
    # 3. 刪除extension和WatchAPP.個人證書沒法簽名Extention
    rm -rf "$TARGET_APP_PATH/PlugIns"
    rm -rf "$TARGET_APP_PATH/Watch"
    
    #----------------------------------------
    # 4. 更新info.plist文件 CFBundleIdentifier
    #  設置:"Set : KEY Value" "目標文件路徑"
    /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
    
    #----------------------------------------
    # 5. 給MachO文件上執行權限
    # 拿到MachO文件的路徑
    APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
    #上可執行權限
    chmod +x "$TARGET_APP_PATH/$APP_BINARY"
    
    #----------------------------------------
    # 6. 重簽名第三方 FrameWorks
    TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
    if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
    then
    for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
    do
    
    #簽名
    /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
    done
    fi
    
    #注入
    yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libHankHook.dylib"

    2.7 編譯運行成功(也和上面一樣在類中加入load代碼)

     

    上面就是dylib方式代碼注入,希望對大家有所幫助!!!

     通過上面的兩種方式實現代碼注入,讓別人的app運行自己的app,下面總結如下:

     三、MethodSwizzle

    3.1 概念

    iOS 中實現AOP編程思想的方式其中之一是Method Swizzling,而 Method Swizzling 是利用Runtime特性把一個方法和另個方法的實現做替換,程序運行時修改Dispatch Table里SEL和IMP之間的映射關係.

    通過swizzling method改變目標函數selector所指向實現,在新的實現中來實現所要改的內容即可.

    3.2 特點

    • 繼承: 修改較多,無法敢保證他人一定繼承基類
    • 類別: 類別中重寫方法會覆蓋到原有的實現,其實,在真實的開發中,重寫方法並不是為了取代它,而是為了添加一些實現; 如果幾個類別實現了同樣的方法, 但只有一個類別的方法會被調用.
    • AOP優勢: 減少了重複代碼

    3.3 代碼

    @implementation NSURL (HKURL)
    
    +(void)load
    {
        Method URLWithStr = class_getClassMethod(self, @selector(URLWithString:));
        
        Method HKURL = class_getClassMethod(self, @selector(HKURLWithStr:));
        
        //交換
        method_exchangeImplementations(URLWithStr, HKURL);
    }
    
    +(instancetype)HKURLWithStr:(NSString *)str{
        //調用系統原來的方法
        NSURL * url = [NSURL HKURLWithStr:str];
        if (url == nil) {
            str = @"https://www.blog.com";
        }
        url = [NSURL HKURLWithStr:str];
        
        return url;
    }

    在上面的代碼中,利用method swizzling的交換方法.其他Runtime的使用方法,以及為什麼寫在load方法中,請參考本人另篇博客

    拓展: 為什麼寫在load中?

    • load方法在源文件被裝載到程序中會被自動調用,不需要手動調用,也不需要該類使用不使用無關,在main()前被執行.
    • 當子類重寫了load,假如子類的類別重寫了load,load的調用順序會這樣: 父類、子類、子類類別
    • 但是如果有多個子類category都重寫了load,每個子類category中load都會調用一次
    • 假如子類沒有重寫load,子類的默認load也不會去調用父類的load.此與正常繼承不太一樣.
    • 在正常的開發中, 除了method swizzle ,其他的邏輯代碼盡量不放在load,load方法中的代碼邏輯要盡量簡單

     

    四、微信示例Demo

    4.1 微信–破壞註冊

    4.1.1 將微信程序運行出來,如下圖所示

    4.1.2 根據上面紅色找出類名,方法名

    4.1.3 通過插件class-dump導出微信的.h文件

    class-dump是將OC運行時聲明的信息導出來的工具, 其實可以導出.h文件. 用此工具將未經過加密的app的頭文件導出來.

    使用它同樣也要講此工具拷貝到MAC的目錄下/usr/local/bin下.

    4.1.4 經過sublime text來找出對應的文件

    4.1.5 通過第三部分講解,利用runtime交換方法

    4.1.6 結果

     

    4.2 竊取微信密碼

    4.2.1 找到輸入密碼框的內容

    從上面看出,登錄按鈕為一個FixTitleColorButton對象,Target名字存放的地址為0x280afaa40,Action名字存放地址是0x280afac00。

    4.2.2 查看賬號密碼的輸入框

    發現賬號密碼輸入框對象屬於都一個對象,叫做WCUITextField

    4.2.3 利用LLDB查看登錄具體的Target和Action

    從上面卡出,登錄按鈕在WCAccountMainLoginViewController頁面中;

    登錄點擊方法叫做onNext

    4.2.4 利用Sublime查看WeChat文件

    發現確實有onNext()方法,並從中看出賬號輸入框和密碼輸入框都是WCAccountTextFieldItem中,但是並沒有發現textFileld,但是可以看到WCAccountTextFieldItem是繼承於WCBaseTextFieldItem,我們再看看WCBaseTextFieldItem文件內容

    看出一個m_textField對象,通過tex字段取出string。

    4.2.5 在賬號欄中輸入賬號和密碼

    po [(WCAccountMainLoginViewController *)0x1128bbc00 valueForKey:@"_textFieldUserPwdItem"]
    po [(WCAccountTextFieldItem *)0x28328e880 valueForKey:@"m_textField"]
    po [(WCUITextField *)0x112163a00 text]

    通過LLDB調試輸入的密碼是123456。

    4.2.6 Hook登錄,獲取密碼

    + (void)load {
        NSLog(@"來了,老弟");
        Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), sel_registerName("onNext"));
        //1.保存原始的IMP
        old_onNext = method_getImplementation(onNext);
        //2.SET
        method_setImplementation(onNext, (IMP)my_next);
    }
    
    IMP (*old_onNext)(id self,SEL _cmd);
    
    void my_next(id self,SEL _cmd){
        // 獲取密碼
        NSString *pwd = [[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
        NSString *accountTF = [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(text)];
        NSLog(@"密碼是!%@",pwd);
        // 將密碼追加在賬號欄的後面
        [[[self valueForKey:@"_textFieldUserNameItem"] valueForKey:@"m_textField"] performSelector:@selector(setText:) withObject:[NSString stringWithFormat:@"%@+%@",accountTF,pwd]];
        //調用原來的方法
        old_onNext(self,_cmd);
    }

    上面用的是setIMP和getIMP的方式,對原方法進行Hook,也可以用class_replaceMethod(),method_exchangeImplementations()。

     

    五、總結

    首先從代碼注入的方式:framework和dylib兩種方式,然後講到Method swizzling方式嘗試Hook,最後又以demo的方式來闡述代碼注入和Hook,希望對大家理解逆向開發的代碼注入有所幫助!!!,歡迎大家繼續關注!!!

     

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 從沒聽說過的併發的名詞-管程

    從沒聽說過的併發的名詞-管程

    在接觸併發之前,我只聽說過進程、線程,管程這個詞倒是頭回聽說,抱着認真好學的態度,去找了找關於 管程 的資料,不學不知道,原來併發里的兩大難題–互斥和同步都可以用管程來解決,可以說,管程是一把解決併發的萬能鑰匙。

     

    那什麼是管程呢?原來管程並不像進程、線程這樣來形容一個特指東西的名詞,管程是指管理共享變量以及讀共享變量的操作過程,讓他們支持併發。Java 中的 Monitor,我們經常將它翻譯成 “監視器”,其實它還有個更學術的名字就是管程。

     

    管程有三種模型,其中廣泛應用的是 MESA 模型,Java 管程實現參考的也是 MESA 模型,所以我就着重學習這個模型。前面提到了管程可以解決併發領域互斥和同步的兩大核心問題,下面我們先看看管程是如何解決互斥問題的。

     

    • 互斥問題的解決

     

    互斥指的同一時刻只允許有一個線程訪問共享資源,管程解決互斥問題的思路很簡單,就是將共享變量及對共享變量的操作統一都封裝起來,如圖:

     

     

    線程 A 和線程 B 如果想訪問共享變量 queue,只能通過管程提供的入隊和出隊操作,入隊和出隊操作保證互斥性,只允許一個線程進入,而對外暴露的就只有管程,看上去有點面向對象封裝的意思。

     

    • 同步問題的解決

     

    在管程解決互斥問題的解決方案中,我們看到了其實共享變量和對共享變量的操作都是被封裝起來的,要想訪問共享變量就要訪問管程,所以同步的解決辦法就是在管程的入口添加一個等待隊列,當多線程想同時進入管程內部時,只允許一個線程進入,其他線程在等待隊列中等待。

     

    進入到管程內部,有可能執行修改共享變量的方法還有條件,比如要執行入隊操作,必須保證隊列不滿;要執行出隊操作,必須保證隊列不空,管程對每個條件的變量還對應有一個等待隊列,如圖:

     

     

    這裏的入口等待隊列與條件等待隊列是完全不同的兩個隊列,當進入管程內部的線程因執行方法的條件不滿足會進入條件等待隊列,等待被其他線程喚醒,喚醒後會重新進入入口的等待隊列,競爭資源。

     

    Java 內置的管程

     

    Java 內置管程與 MESA 模型類似,在 MESA 模型中,條件變量可以有多個, Java 語言內置的管程里只有一個條件變量。

     

     

    Java 內置的管程方案就是 syncronized ,使用 syncronized 修飾的代碼塊,在編譯器會自動生成相關加鎖和解鎖代碼,但是只會支持一個條件變量。

     

     

    總結一下 :

     

     

     

    以上是管程的相關介紹,後續我們會進入 Java JUC 工具包的學習,看看除了 syncronized 其他強大的併發編程類都有哪些獨特的用處。

     

     這裡有一篇 

     

    感興趣的同學可以瀏覽一下哦~

        

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • variable precision SWAR算法

    variable precision SWAR算法

          計算二進制形式中1的數量這種問題,在各種刷題網站上比較常見,以往都是選擇最笨的遍歷方法“矇混”過關。在了解Redis的過程中接觸到了variable precision SWAR算法(以下簡稱VP-SWAR算法),算法異常簡潔,是目前已知的同類方法中最快的。但如果對於位運算不是很熟悉的話,卻不一定容易理解,所以有必要記錄一下。

          下面先看看VP-SWAR算法的完整實現,然後再逐行解釋。

      public int vpSWAR(int i){
        i = (i & 0x55555555) + ((i>>1) & 0x55555555);
        i = (i & 0x33333333) + ((i>>2) & 0x33333333);
        i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
        i = (i * 0x01010101) >> 24;
        return i;
      }

          VP-SWAR算法分為四步,第一步

    i = (i & 0x55555555) + ((i>>1) & 0x55555555);
    

          第一步的作用是計算每兩位為一組的二進制形式包含1的個數。要理解這句話,我們需要從二進制的角度看看到底發生了什麼。首先, 0x55555555 的二進製表示為 0101 0101 0101 0101 0101 0101 0101 0101 ,這個数字的規律是基數位為1,偶數位為0。為簡單起見,我們只考慮兩位,總共有四種情況,即:

     

    i b i & b 結果
    00 01 00
    01 01 01
    10 01 00
    11 01 01

           觀察發現, i & (0b01) 是i的基數位對應b的1位,i的偶數位對應着b的0位, i & (0b01) 的結果會將I的偶數位置為0,而基數位保持不變,得到的結果就是i的基數位包含1的個數。 (i >> 1) & 0x55555555 先將i右移一位,也就是將i的基數位對應b的0位,i的偶數位對應着b的1位,然後再與 0x55555555 按位與,計算出來的是i的偶數位包含1的個數。兩個計算結果相加就得到i每兩位為一組中包含的1的數量,我們最後需要的就是這每兩位一組的和。

          第二步是在第一步的基礎上,計算每四位為一組包含1的個數。按照每2位為一組分組用到了 0x55555555 這個數,那麼自然的,按照每4位為一組分組自然就需要 0b0011 這種形式,這就是使用 0x33333333 的原因。理論上, i & (0b0011) 總共有16種情況,但是四位二進制位最多包含4個1,用二進製表示為 0b0100 ,所以經過第一步之後,i最多有5種取值,如下:

    i b i & b 結果
    0000 0011 0000
    0001 0011 0001
    0010 0011 0010
    0011 0011 0011
    0100 0011 0000

     

          觀察發現, i & (0b0011) 得到的是i的低兩位包含的1的個數,  (i >> 2) & 0b0011 )得到的是i的高兩位包含的1的個數,兩個結果相加得到每四位包含的1的個數。注意,這裏並不是說任何數與 0b0011 按位與得到的都是低兩位包含的1的個數,這裏的前提是第一步的計算,因為經過第一步計算之後,每兩位包含多少個1已經記錄了下來,再和 0b0011 按位與才得到正確的結果。例如, 0x0010 & 0x 0011=0x0010 ,但是我們不能說 0x0010 包含兩個1,但是如果 0x0010 是經過第一步的計算得來,那才說明 0x0010 記錄原始數據低兩位有兩個1。

          第三步在第二步基礎上,計算每8位有多少個1,由 0x010x0011 ,很自然想到 0x00001111 ,其對應的32位的十六進制數就是 0x0F0F0F0F

          第四步就很有意思了,它不再是計算每16位包含1的個數,而是直接計算32位包含1的個數。對於32位的數來說,可以將其按每8位一組分為4組,分別用ABCD表示,例如 0x01020304 用這種形式表示為:

     

     

          假設 0x01020304 是經過前三步計算之後得到的結果,那麼要計算其總共包含多少個1,只需計算A+B+C+D。而ABCD表示的是不同的位區間範圍,不能直接相加,該如何快速計算A+B+C+D的值呢?這裏又用到了移位運算,將B、C、D分別左移8位、16位、24位,使其分別與A對齊:

     

           我們發現,將数字i分別左移0位、8位、16位、24位然後相加的結果,就是 i * 0x01010101 ,因為 i + (i << 8) + (i << 16) + (i << 24) = i * (1 + 1 << 8 + 1 << 16 + 1 << 24) = i * 0x01010101 。對於32位数字來說,左移之後超過32位的部分會被捨棄,低位補0,將左移之後得到的四個数字相加,結果的高8位的值就是原32位數包含的1的個數,要得到這個值,只需要將結果右移24位,將值放在低8位即可。

          到這裏,整個算法就結束了,右移的結果就是1的數量。在Redis中,BITCOUNT命令同時使用了查表法和VP-SWAR這兩種方法。當要計算的位數小於128位時,使用查表法,否則使用VP-SWAR算法。其中查表法的做法是,程序先存一個256長度的表,按順序記錄從0-255(即 0b00000000 – 0b11111111) 數中二進制1的個數,然後對於輸入參數每8位查一次表。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 對於計算機相關專業我們在迷茫什麼

    對於計算機相關專業我們在迷茫什麼

    計算機相關專業初識–對於計算機相關專業我們在迷茫什麼

    前言

    由於種種原因,迫使我寫下這篇博客,我相信,初入計算機相關專業的萌新肯定很迷茫,我該學什麼,我該如何去學,我該如何學好等等問題纏繞心頭。有很多學弟學妹問我該如何去學計算機相關專業,作為過來人,我決定將我的所知所得寫下來,讓初入計算機相關專業的萌新的學習之路走得更順暢一些。

    一、什麼是計算機

    對於剛學習計算機相關專業的萌新來說,了解一下計算機的工作原理是十分必要的,但是在這裏我們不過多闡述,讓大家簡單了解一下就好。

    讓我們先來看一下對於計算機名詞的解釋:

    計算機(computer)俗稱電腦,是現代一種用於高速計算的电子計算機器,可以進行數值計算,又可以進行邏輯計算,還具有存儲記憶功能。是能夠按照程序運行,自動、高速處理海量數據的現代化智能电子設備。

    划重點:

    • 我們注意到,計算機就是一種用於進行數值計算的現代化智能电子設備。需要理解的是為什麼是進行數值計算,在這裏,你會疑惑,為什麼是數值計算呢,我輸入的明明不是数字呀?這個問題很容易解釋清楚,因為計算機只是一種电子設備,它不具有人類獨立思考和不斷學習的能力,它的所有功能都是事先設定好的,所以當計算機面對輸入字符的時候,會將它統一按照ASCII(計算機編碼系統)規則轉換為數值“0”和“1”(二進制數值),所以,在計算機里,數據存儲都是用“0”和“1”(即二進制數值)來實現。

    • 還有一點值得注意,按照程序運行,那麼問題來了,程序是什麼?程序就是一組計算機能識別和執行的指令, 它以某些程序設計語言編寫,運行於某種目標結構體繫上 。舉個例子,程序就像是用英語(程序設計語言,例如c,c++)寫的文章,要讓一個懂的英語的人(編譯器,如C的編譯器gcc,這裏要注意編譯器和IDE的區別,通常IDE包含編譯器)同時也會閱讀這篇文章的人(結構體系)來閱讀、理解、標記這篇文章。

    有學妹問過我,為什麼簡單的代碼,能實現豐富的效果。其實這取決於編譯器的強大能力。下面來簡單介紹一下,編輯器,編譯器,IDE(集成開發環境)的區別。

    • 編輯器:編輯器就是用來編輯的軟件,比如windows自帶的記事本就是一個編輯器, 記事本沒有語法高亮,不显示行號,當一段可執行代碼寫完后無法通過內置環境執行,必須手動輸入命令執行編譯等等一些弊端,所以很少有程序員會用記事本去寫代碼 , 寫代碼比較好用的編輯器軟件有vscode,vim,sublime,notepad++,emacs,atom等等 ,雖然編輯器原始功能不足,但是開發人員為了使編輯器更加友好,所以有很多內置插件可供使用,完全可以手動打造一個IDE。
    • 編譯器:簡單來說,編譯器就是將“一種語言(一般為高級語言,如c,c++,java,python等,計算機不可直接識別和執行)”翻譯為“另一種語言(一般為低級語言,低級語言即機器語言,機器語言是用二進制代碼錶示的計算機能直接識別和執行的一種機器指令的集合)”的程序。舉個例子,用Dev-C++寫好一段可執行"hello,world!"C語言代碼之後,我們要讓它在屏幕打印出來我們想要它輸出的"hello,world!",就需要通過gcc編譯器執行編譯后才能显示。其他語言同理。
    • IDE:集成開發環境,用於程序開發環境的應用程序,一般包含代碼編輯器編譯器調試器圖像用戶界面等工具。集成了代碼編寫程序分析程序編譯程序調試等功能。如 jetbrains 的用於Java開發的 IntelliJ IDEA 、用於JavaScript開發的WebStorm、用於Python開發Pycharm,微軟的 Visual Studio系列 ,IBM的Eclipse。

    二、我們該學什麼

    很多初入計算機相關專業的萌新,總是很迷茫,不知道自己該學什麼,通常是他們知道如何去學好學校開設的每一門課程,就是不知道自己該向哪些方向學習,這些方向指的是專業技能和就業方向,諸如web開發、Android/IOS開發、數據分析、人工智能、網絡安全、遊戲開發、軟件測試等等。有這種疑惑很正常,迷茫也是正常的,但我們總要讓自己了解自己所需,然後腳踏實地,一步一步去充實自己的能力。而我想做的也很簡單,就是幫助大家解除心裏的疑惑。那麼,我們開始進入正題。

    1. 我們該如何選擇適合自己的方向

    對於這個問題,其實是很難回答清楚的,因為每個人的興趣都不相同,所以就很難去站在自己的角度去回答疑問者的問題。但是,原理都是想通的,我相信我的經驗會幫助到你們。

    • 通常,學校每學期都會開設一門或多門語言(程序設計語言,下文同),那麼,喜歡一門語言,首先要愛上它的語言風格,諸如Java的嚴謹,Python的自由,總有一款適合你;其次,在學習語言的過程中,一定要了解它能幹什麼,市場環境如何,工作崗位多少等綜合因素,再決定要不要去深入這門語言,並且主攻自己感興趣的那個方向。

    • 對於學校沒有開設,但是自己又想學習的語言而言,該如何去選擇。首先,學校開設的語言基本是市場比較流行的語言,也符合市場需求,所以,完全可以在學校開設的語言中去選擇自己想要了解並學習的語言。此外,我們可以藉助 TIOBE ( TIOBE 編程社區指數是編程語言流行度的指標,該榜單每月更新一次,指數基於全球技術工程師、課程和第三方供應商的數量。包括流行的搜索引擎,如谷歌、必應、雅虎、維基百科、亞馬遜、YouTube 和百度都用於指數計算。 )去了解語言的流行程度,流行程度決定市場需求,以此來參考自己想要了解並學習的語言,在此附上2019年11月語言排名。

    2. 主流編程語言主要應用場景

    • Java

      1. 企業級應用開發: 大到全國聯網的系統,小到中小企業的應用解決方案,Java都佔有極為重要的地位 。
      2. web後端開發: JSP+Servlet+JavaBean 是一種比較流行的開發模式。
      3. 移動領域:手機遊戲。
      4. Android App開發: android 開發只用到了JAVA的語法和JAVA SE的一小部分API。
    • C

      C語言是一門基礎語言,是其他一些語言的基礎,例如MATLAB,Object-C,Lua等.同時也是學習來比較難的語言,達到精通的程度沒有3-10年左右很難,C語言沒有比較完善的開發框架,是面向過程的一門語言,講究算法跟邏輯。

      1. 科研
      2. 服務器: 網絡核心設備,如路由器、交換機、防火牆。
      3. 操作系統:類unix系統(Linux/freebsd)
      4. 嵌入式開發: 在一個特定的硬件環境上開發與構建特定的可編程軟件系統的綜合技術。
      5. 自動化控制
    • Python

      1. 圖形處理
      2. 數學處理
      3. 文本處理
      4. 數據庫編程
      5. 網絡編程
      6. 多媒體應用
      7. pymo引擎: 運行於Symbian S60V3,Symbian S60V5,Symbian 3,Android,Windows,Linux,Mac Os,Maemo,MeeGo系統上的AVG遊戲引擎。
      8. 黑客編程
      9. 網絡安全
    • C++

      1. 遊戲開發
      2. 科學計算
      3. 網絡軟件
      4. 操作系統
      5. 設備驅動程序
      6. 移動設備
      7. 嵌入式開發
      8. 科研
      9. 編譯器
    • C#

      1. web後端開發
      2. 桌面軟件開發
      3. 人工智能
      4. 遊戲開發
    • JavaScript
      唯一能用於前後端開發的語言web前端開發
      1. web前端開發
      2. node web後端開發
      3. 操作系統
      4. 後台
      5. 桌面軟件開發
      6. 混合App
      7. 小程序
    • PHP

      1. web後端開發
      2. 桌面軟件開發
      3. 命令行腳本
    • SQL

      1. 操作數據庫
    • Swift

      1. 蘋果生態系統應用開發
    • Ruby

      1. web開發
    • R

      數據科學闖天下,左手Python右手R

      1. 機器學習
      2. 數據分析
      3. 科學計算
    • Go

      1. web後端開發
      2. 高性能服務器應用

    3. 主流編程語言學習路徑(將持續更新,僅供參考)

    • JavaScript

    4. 主流編程語言入門學習書籍推薦

    語言 書籍
    C 《嗨翻C語言》
    C++ 《C++權威教程》
    Java 《Java輕鬆學》
    Python 《Python編程從入門到實戰》
    JavaScript 《JavaScript入門經典》
    PHP 《PHP編程實戰》
    SQL 《SQL基礎教程》
    Swift 《Swift編程權威指南》
    Ruby 《Ruby從入門到精通》
    R 《R語言實戰》
    Go 《Go語言聖經》

    5. 編程學習網站推薦

    網站 網址
    菜鳥教程
    W3School
    實驗樓
    猿學
    慕課網
    SegmentFault
    博客園
    GitHub
    掘金
    學習數據科學
    易百教程
    看雲

    三、總結

    通篇寫完,感覺自己也重新學到了很多,學習就是一個反覆複習的過程,每次學習都能帶給自己不一樣的收穫。希望以上內容可以給初入計算機相關專業的萌新帶來一些幫助,後面我會不斷更新和優化本文,請大家持續關注。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 【自然語言處理】利用LDA對希拉里郵件進行主題分析

    【自然語言處理】利用LDA對希拉里郵件進行主題分析

    首先是讀取數據集,並將csv中ExtractedBodyText為空的給去除掉

    import pandas as pd
    import re
    import os
    
    dir_path=os.path.dirname(os.path.abspath(__file__))
    data_path=dir_path+"/Database/HillaryEmails.csv"
    df=pd.read_csv(data_path)
    df=df[['Id','ExtractedBodyText']].dropna()

    對於這些郵件信息,並不是所有的詞都是有意義的,也就是先要去除掉一些噪聲數據:

    def clean_email_text(text):
        text = text.replace('\n'," ") #新行,我們是不需要的
        text = re.sub(r"-", " ", text) #把 "-" 的兩個單詞,分開。(比如:july-edu ==> july edu)
        text = re.sub(r"\d+/\d+/\d+", "", text) #日期,對主體模型沒什麼意義
        text = re.sub(r"[0-2]?[0-9]:[0-6][0-9]", "", text) #時間,沒意義
        text = re.sub(r"[\w]+@[\.\w]+", "", text) #郵件地址,沒意義
        text = re.sub(r"/[a-zA-Z]*[:\//\]*[A-Za-z0-9\-_]+\.+[A-Za-z0-9\.\/%&=\?\-_]+/i", "", text) #網址,沒意義
        pure_text = ''
        # 以防還有其他特殊字符(数字)等等,我們直接把他們loop一遍,過濾掉
        for letter in text:
            # 只留下字母和空格
            if letter.isalpha() or letter==' ':
                pure_text += letter
        # 再把那些去除特殊字符后落單的單詞,直接排除。
        # 我們就只剩下有意義的單詞了。
        text = ' '.join(word for word in pure_text.split() if len(word)>1)
        return text

    然後取出ExtractedBodyText的那一列,對每一行email進行噪聲過濾,並返回一個對象:

    docs = df['ExtractedBodyText']
    docs = docs.apply(lambda s: clean_email_text(s))  

    然後我們呢把裏面的email提取出來:

    doclist=docs.values

    接下來,我們使用gensim庫來進行LDA模型的構建,gensim可用指令pip install -U gensim安裝。但是,要注意輸入到模型中的數據的格式。例如:[[一條郵件字符串],[另一條郵件字符串], ...]轉換成[[一,條,郵件,在,這裏],[第,二,條,郵件,在,這裏],[今天,天氣,腫么,樣],...]。對於英文的分詞,只需要對空白處分割即可。同時,有些詞語(不同於噪聲)是沒有意義的,我們要過濾掉那些沒有意義的詞語,這裏簡單的寫一個停止詞列表:

    stoplist = ['very', 'ourselves', 'am', 'doesn', 'through', 'me', 'against', 'up', 'just', 'her', 'ours',
                'couldn', 'because', 'is', 'isn', 'it', 'only', 'in', 'such', 'too', 'mustn', 'under', 'their',
                'if', 'to', 'my', 'himself', 'after', 'why', 'while', 'can', 'each', 'itself', 'his', 'all', 'once',
                'herself', 'more', 'our', 'they', 'hasn', 'on', 'ma', 'them', 'its', 'where', 'did', 'll', 'you',
                'didn', 'nor', 'as', 'now', 'before', 'those', 'yours', 'from', 'who', 'was', 'm', 'been', 'will',
                'into', 'same', 'how', 'some', 'of', 'out', 'with', 's', 'being', 't', 'mightn', 'she', 'again', 'be',
                'by', 'shan', 'have', 'yourselves', 'needn', 'and', 'are', 'o', 'these', 'further', 'most', 'yourself',
                'having', 'aren', 'here', 'he', 'were', 'but', 'this', 'myself', 'own', 'we', 'so', 'i', 'does', 'both',
                'when', 'between', 'd', 'had', 'the', 'y', 'has', 'down', 'off', 'than', 'haven', 'whom', 'wouldn',
                'should', 've', 'over', 'themselves', 'few', 'then', 'hadn', 'what', 'until', 'won', 'no', 'about',
                'any', 'that', 'for', 'shouldn', 'don', 'do', 'there', 'doing', 'an', 'or', 'ain', 'hers', 'wasn',
                'weren', 'above', 'a', 'at', 'your', 'theirs', 'below', 'other', 'not', 're', 'him', 'during', 'which']

    然後我們將輸入轉換成gensim所需的格式,並過濾掉停用詞:

    texts = [[word for word in doc.lower().split() if word not in stoplist] for doc in doclist]

    再將這所有的單詞放入到一個詞袋中,把每個單詞用一個数字index指代:

    from gensim import corpora, models, similarities
    import gensim
    dictionary = corpora.Dictionary(texts)

    再分別統計每一篇email中每個詞語在這個詞袋中出現的次數,並返回一個列表:

    corpus = [dictionary.doc2bow(text) for text in texts]

     這個列表告訴我們,第14(從0開始是第一)個郵件中,一共6個有意義的單詞(經過我們的文本預處理,並去除了停止詞后)其中,51號單詞出現1次,505號單詞出現1次,以此類推。。。

    最後,就可以開始構建我們的模型了:

    lda = gensim.models.ldamodel.LdaModel(corpus=corpus, id2word=dictionary, num_topics=20)
    print(lda.print_topic(10, topn=5))

     可以看到,第11個主題最常用的單詞,接下來,我們看下所有的主題:

    for i in lda.print_topics(num_topics=20, num_words=5):
        print(i)

     我們再看下第一篇email屬於哪一個主題:

    print(lda.get_document_topics(corpus[0]))

     屬於第四個主題的概率是0.95

    相關代碼和數據:鏈接: https://pan.baidu.com/s/1sl1I5IeQFDHjVwf2a0C89g 提取碼: xqqf 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    南投搬家前需注意的眉眉角角,別等搬了再說!

  • SQlALchemy session詳解

    系列文章:

    概念

    session用於創建程序和數據庫之間的會話,所有對象的載入和保存都需通過session對象 。

    通過sessionmaker調用創建一個工廠,並關聯Engine以確保每個session都可以使用該Engine連接資源:

    from sqlalchemy.orm import sessionmaker
    
    # 創建session
    DbSession = sessionmaker(bind=engine)
    session = DbSession()

    操作

    session的常見操作方法包括:

    1. flush:預提交,提交到數據庫文件,還未寫入數據庫文件中
    2. commit:提交了一個事務,把內存的數據直接寫入數據庫
    3. rollback:回滾
    4. close:關閉

    在事務處理時,需注意一下兩點:

    1. 在事務處理過程發生異常時,進行rollback操作,否則會在下次操作時報錯:
    Can’t reconnect until invalid transaction is rolled back 
    1. 一般情況下,在一個事務處理完成之後要關閉session,以確保數據操作的準確性。

    建議封裝上下文方法:

    from contextlib import contextmanager
    
    @contextmanager
    def session_maker(session=session):
        try:
            yield session
            session.commit()
        except:
            session.rollback()
            raise
        finally:
            session.close()

    調用:

    def update_user():
        with session_maker() as db_session:
            db_session.query(Users).filter_by(name='test2').update({'email': 'test2@qq.com'})

    線程安全

    session不是線程安全的,並且我們一般session對象都是全局的,那麼在多線程情況下,當多個線程共享一個session時,數據處理就會發生錯誤。

    為了保證線程安全,需使用scoped_session方法:

    db_session = scoped_session(sessionmaker(bind=engine))

    內部原理

    session對象包含了三個重要的部分:

    1. 標識映射(Identity Map)
    2. 對象的狀態 / 狀態跟蹤
    3. 事務

    標識映射

    標識映射是與ORM關聯的集合,通過標識映射保證了數據庫操作的準確性。

    具體的實現原理是:維護一個Python字典(IdentityMap),關聯這個Session對象到數據庫ID的映射,當應用程序想要獲取一個session對象時,若該對象不存在,標識映射會加載該對象並緩存,若該對象已存在,則直接獲取。這樣的好處是:

    1. 已經被請求過的session對象緩存下來,不需要連接加載多次,造成額外的開銷;
    2. 避免了數據不一致

    狀態跟蹤

    一個Session對象從創建到銷毀,依次經歷四種狀態,分別是:

    1. Transient:剛new出來的對象,還不在會話中,也沒有保存到數據庫。
    2. Pending:transient的對象調用add后,就會變成pending狀態,這時會加入sqlalchemy的監管範圍,數據並未更新到數據庫。
    3. Persistent:該狀態表明數據庫里已經記錄了該對象,在兩種情況下對象處於該狀態:一是通過flush()方法刷新pending對象,二是從數據庫query()得到對象。
    4. Detached:在會話中的事務提交之後,所有的對象都將是Detached狀態。

    所謂的狀態跟蹤,就是跟蹤以上四個狀態,保證數據的準確性並在合理的時機丟棄對象以保證合理開銷,那麼具體是怎麼實現的呢?

    我們可以看到,只有在pending狀態時,對象的內存數據和數據庫中的數據不一致,在Persistent狀態時,內存數據和數據庫數據已經一致,那麼此後任意時刻丟棄該對象數據都是可以的,這時就需要找個合適的時機丟棄對象,過早或過晚都有其缺陷。於是,就讓垃圾回收器來做決定,在內存不夠的時候釋放對象,回收內存。

    Session對象採用了弱引用機制,所謂弱引用,就是說,在保存了對象的引用的情況下,對象仍然可能被垃圾回收器回收。在某一時刻通過引用訪問對象時,對象可能存在也可能不存在,如果對象不存在,就重新從數據庫中加載對象。而如果不希望對象被回收,只需要另外保存一個對象的強引用即可 。

    session對象包括三個屬性:

    1. new:剛加入會話的對象
    2. dirty:剛被修改的對象
    3. deleted:在會話中被刪除的對象

    三個屬性共同的特點就是內存的數據和數據庫數據不一致,也就是對象處於pending狀態,這也就表明了session保存了所有對象處於pending狀態的強引用。

    以上。

    代碼可參照:

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 花一天時間試玩vsphere6.7(EXSI)服務器版的vmware

    花一天時間試玩vsphere6.7(EXSI)服務器版的vmware

    花一天時間試玩vsphere6.7(EXSI)服務器版的vmware

    要註冊賬號(2019年11月14註冊):

    登陸網址:https://my.vmware.com/cn/group/vmware/home
    賬號:haha@grr.la
    密碼:0nV+SJ8fZbdE8g==

    看了下。不管是標準版,企業版,測試版的6.7下載的軟件包都是同一個,區別應該在於激活密鑰

    主要有兩個鏡像:EXXI鏡像,VCenter鏡像

    EXSI和VMware有點像,不過可以直接裝在裸機上,Vcenter需要裝在windows server上,管理EXSI平台

    鏈接:https://pan.baidu.com/s/1x8K4dnMdhg7uz11Hyi7v2A
    提取碼:tqaw

    VMware vSphere 6 Enterprise Plus
    0A65P-00HD0-3Z5M1-M097M-22P7H

    參考文檔:https://pan.baidu.com/s/18Cq9marptM-Rbym9l7RvtQ

    這個是新的文檔:https://www.lanzous.com/i7ewj1i

    上面如果失效:https://blog.csdn.net/techgroup/article/details/100053477

    一、安裝EXSI參考:https://blog.csdn.net/weixin_42758707/article/details/100525596

    需要注意的地方:不需要DHCP服務器,路由器分配IP就行,ip最好設置為固定IP,可以開啟ssh

    填寫密鑰的地方:

    開啟ssh后可以使用ssh登陸

    二、創建虛擬機根vmware一樣,要先上傳鏡像再創建,可以創建Linux,MacOS,windows

    也可以從ovf導入

    三、其他功能

    支持虛擬機資源監控,克隆虛擬機,建立快照,導出為模版等功能

    支持用VMware連ESXI平台(CTRL+L),連接之後可以創建或刪除虛擬機,還可以對虛擬機開關機

    四、VMware vSphere Client6.0(看樣子官方不再長期支持了)

    https://yq.aliyun.com/articles/636965

    https://wsgzao.github.io/post/vmware-vsphere-client/

    五、解決ssh無法密碼登陸的問題(提示密鑰登陸)

    CTRL+ALT+F1進入命令行,修改/etc/ssh/sshd_config 重啟services.sh

    [root@localhost:~] vi /etc/ssh/sshd_config 
    [root@localhost:~] grep PasswordAuth /etc/ssh/sshd_config
    PasswordAuthentication yes
    [root@localhost:~] services.sh restart

    六、安裝vcenter

    安裝到windows server2016中,應該還有其他辦法

    安裝文檔:https://www.lanzous.com/i7eh24j

    安裝很簡單,整個安裝過程比較慢,可能要半個小時甚至更長

    牛逼,8G,不搞了

    換了個電腦,繼續實驗

    七、vcenter管理ESXI

    vcenter地址:vcenter.jd.com(172.18.3.252)

    exsi1地址:172.18.3.151

    exsi2地址:172.18.3.255

    步驟:先創建數據中心,然後數據中心中添加主機(exsi節點)

    管理參考:https://www.cnblogs.com/djlsunshine/p/11372482.html

    新建數據中心:

    添加主機(添加exsi節點)

    設置是否啟用鎖定模式,如果啟用了鎖定模式,管理員就不能夠使用vSphere客戶端直接登錄到ESXI主機,只能通過vCenter Server對主機進行管理。在這裏不啟用鎖定模式

    激活參考:https://www.lanzous.com/i7eikla

    激活碼複製粘貼全部導入了。一個機器一個激活碼:https://blog.csdn.net/lizhiyuan_eagle/article/details/79989216

    然後就可以看到exsi中的虛擬機:

    遇到的小問題,我克隆的第一個exsi變成第二個exsi,被識別數來了,只能再裝一台:

    vcenter同樣支持管理虛擬機,連接虛擬機

    八、遷移exsi1的虛擬機到exsi2

    冷遷移,直接遷

    取消掛載cdrom再遷移

    熱遷移,要添加虛擬交換機在每個exsi網絡上

    主機—配置—虛擬交換機—添加網絡:

    exsi2一樣,添加個虛擬交換機,ip不能一樣

    然後就可以遷移了

    整個遷移過程網絡沒有丟包,ssh也沒有中斷

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

  • spring 是如何注入對象的和bean 創建過程分析

    文章目錄:

    1. 【本文】spring 是如何注入對象的

    首先需要知道一個大致實現

    • 這個注入過程肯定是在 BeanPostProcessor 中實現的

    • spring 是在 beanFactory.getBean 進行 bean 實例化的,即懶加載

    • 根據第二條,也就是說在 getBean 的時候才會去調用所有 BeanPostProcessor

    • 第二篇文章說到,BeanFactory 的 refresh 過程只是註冊 BeanPostProcessor,真正執行在 getBean 方法中

    • MergedBeanDefinitionPostProcessor 也是一種 BeanPostProcessor 它重新弄了個一個生命周期函數,替代了 BeanPostProcessor 默認的生命周期函數,這麼看吧,我貼一小段源碼

      for (BeanPostProcessor bp : getBeanPostProcessors()) {
          if (bp instanceof MergedBeanDefinitionPostProcessor) {
              MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
              bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
          }
      }

      它允許你在非 BeanFactoryProcess 中去修改 Bean 定義

    • InstantiationAwareBeanPostProcessor 也是一種 BeanPostProcessor 它也重新定義了一個生命周期函數,它允許把屬性值注入到屬性對象中

    @Autowired 加載定義的過程

    我們先不看 bean 的創建過程,就看 MergedBeanDefinitionPostProcessor 的實現子類,這裏看名字猜測 AutowiredAnnotationBeanPostProcessor 應該就是干這件事的,所以我們接下來可以直接看 AutowiredAnnotationBeanPostProcessor 的 postProcessMergedBeanDefinition 方法的代碼。

    順着方法的調用,可以知道在 buildAutowiringMetadata 是真正查找這些註解的地方,最後 checkConfigMembersMember 註冊進了 bean 定義,具體如何查找的讀者自行查看源碼。

    這裏只是將 Member 註冊進了 bean 定義,真正實例化在填充 Bean 的過程中,下面說到 bean 的創建過程可以知道是何時注入的。

    Bean 的創建過程

    前面說到 spring 是在 getBean 的過程中進行 Bean 創建的,創建 bean 分為幾個步驟

    1. 獲取 bean 定義
    2. new Bean()
    3. 執行生命周期函數 (前)
    4. 創建依賴項
    5. 填充 bean
    6. 執行生命周期函數(后)

    入口為 BeanFactory.getBean ,BeanFactory 的實現類為 DefaultListableBeanFactory 這些你可以在 BeanFactory 的 refresh 過程中找到

    根據源碼,如果 bean 還不存在時,就會執行 bean 的創建流程

    獲取 bean 定義在這段源碼中

    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

    緊跟着,根據 Bean 定義搜索其依賴項,並創建 bean ,可以看出是遞歸創建 bean

    String[] dependsOn = mbd.getDependsOn();
    for (String dep : dependsOn) {
        getBean(dep);
    }

    然後就創建 bean 了

    if (mbd.isSingleton()) {
        createBean(beanName, mbd, args);
    }
    
    // 真正的執行在 doCreateBean 過程中
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);

    創建 bean 第一步 new Bean

    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }

    創建 bean 第二步,執行所有的 processor ,包含 MergedBeanDefinitionPostProcessor ,所以在這一步註冊注入選項

    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

    創建 bean 第三步,填充 bean ,這裏做的 @Autowired 注入

    populateBean(beanName, mbd, instanceWrapper);

    最終的處理過程在 AutowiredAnnotationBeanPostProcessor 的 postProcessPropertyValues 函數中

    metadata.inject(bean, beanName, pvs);

    因為在前面已經獲取過依賴項,並且把其丟進了容器,所以這裡是直接用反射寫進去就可以了

    創建 bean 第四步,初始化 bean ,這裡有一個方法注入,方法注入原來發生在初始化 bean 過程中,還有就是生命周期函數執行了,包含 BeanPostProcessor 的前置後置生命周期,初始化方法等

    小說明 :AutowiredAnnotationBeanPostProcessor 即是 一個 MergedBeanDefinitionPostProcessor 也是一個 InstantiationAwareBeanPostProcessor

    一點小推廣

    創作不易,希望可以支持下我的開源軟件,及我的小工具,歡迎來 gitee 點星,fork ,提 bug 。

    Excel 通用導入導出,支持 Excel 公式
    博客地址:
    gitee:

    使用模板代碼 ,從數據庫生成代碼 ,及一些項目中經常可以用到的小工具
    博客地址:
    gitee:

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!