Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/mobile: reverse binding: undefined #33705

Closed
mvasi90 opened this issue Aug 18, 2019 · 8 comments
Closed

x/mobile: reverse binding: undefined #33705

mvasi90 opened this issue Aug 18, 2019 · 8 comments
Labels
Milestone

Comments

@mvasi90
Copy link

@mvasi90 mvasi90 commented Aug 18, 2019

What version of Go are you using (go version)?

$ go version  go1.12.6 linux/amd64
$ gomobile version +e8b3e61 Wed Aug 14 14:30:26 2019 +0000 (android); androidSDK=/home/user/Android/Sdk/platforms/android-29

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/user/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/user/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/lib/go"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build422159366=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Get the fields of the Build class.
https://play.golang.org/p/PkV4bxUJf8z

$ gomobile bind -target android -ldflags "-w -s" -bootclasspath /home/user/Android/Sdk/platforms/android-29/android.jar .

The java.lang.Float.MIN_VALUE works. Why? The field type must be Integer? String does not work?

What did you expect to see?

Board String

What did you see instead?

Test1() -> Error: b.BOARD undefined (type "Java/android/os".Build has no field or method BOARD)
Test2() -> Error: undefined: Build.BOARD
@mvasi90

This comment has been minimized.

Copy link
Author

@mvasi90 mvasi90 commented Aug 18, 2019

The Go project uses its bug tracker only for tacking bugs and for Proposal Process.
For asking questions is recommended stackoverflow and other third-party platforms.

This proposal was published more than 2 years ago:

Static methods and constants
After importing, the resulting packages SomeClass and InnerClass will contain the static methods and static final constants from their Java classes. For example
import "Java/java/lang/Float"
will expose (among others) the constant Float.MIN_VALUE and the function Float.ParseFloat.

Then, the same person answer this:

I'm sorry, but Java fields are not (yet) exposed by reverse bindings.

MIN_VALUE is a field of class Float, and works.
It is contradicting itself because it gives an example how to access Java fields and below it says that it is not possible to access Java fields.

Why do you assign unskilled programmers to do the job?
And then, to solve a problem, they recommend talking to other people (third-party platforms). When they are responsible.

They should be more explicit.
Two years later and still unable to implement a solution.

#16876

@odeke-em

This comment has been minimized.

Copy link
Member

@odeke-em odeke-em commented Aug 19, 2019

Hello @mvasi90, thank you for filing this bug!

Before proceeding, I'd highly recommend reading through Go's code of conduct https://golang.org/conduct and also I'd recommend, "always assume good intent and be kind", "no is considered temporary, yes is forever".
Most of us are entirely volunteers who happily invest/sacrifice our own time, money and resources to work on the Go project! Regardless of one's position, it can be very discouraging to see not too kind words, so please try to express yourself differently. Everyone is welcome to the Go project and the humility of the folks you saw on that bug is the same humility that doesn't parade their very high proficiency; picture the work it took to get Go supported for mobile devices and how they've made it look easy; that work is only possible because of their amazing talents and skilled work!

I'll assume good intent and that this is perhaps a miscommunication while expressing your frustrations.

With that having been said, let's try to get to the filed issue:

I believe you filed a bug about reverse bindings being undefined and that the message about MIN_VALUE was unrelated?

The java.lang.Float.MIN_VALUE works. Why? The field type must be Integer? String does not work?

Could you please elaborate your question here? I suspect you are directly using that value as a constant and if its constant expression looks like an integer that fits into 256 bits, the compiler will default its type to an integer. Perhaps return or use it as a float32 and you'll be good to go. .String() for integers even in plain Go is undefined, so unlike Java where you'll have an autoboxed/promoted type to Integer and finally java.lang.Integer.toString() method, in Go you'll need to use fmt's "%d" format specifier or strconv for formatting integers.

I am not an x/mobile expert but I shall kindly page @eliasnaur if he has time to reply to your issue.

@odeke-em odeke-em changed the title gomobile reverse binding: undefined x/mobile: reverse binding: undefined Aug 19, 2019
@gopherbot gopherbot added this to the Unreleased milestone Aug 19, 2019
@gopherbot gopherbot added the mobile label Aug 19, 2019
@mvasi90

This comment has been minimized.

Copy link
Author

@mvasi90 mvasi90 commented Aug 19, 2019

First: I apologize for not being word perfect in english.

Second: If it seems that I use destructive criticism or I have a strange behavior, it must be due to the lack of tolerance for nonsense.

Most of us are entirely volunteers who happily invest/sacrifice our own time, money and resources to work on the Go project.

Now I understand. I thought it was a serious project.
I am wasting my time researching undocumented things (for more than two years). But I see that what is important and what must be respected is the time of others because they are volunteers.

Regardless of one's position, it can be very discouraging to see not too kind words, so please try to express yourself differently

Okay, I apologize for my severity.

I'll assume good intent and that this is perhaps a miscommunication while expressing your frustrations

Yes, of course. Thank you. I'm working every day (7 days a week), 11-17 hours per day. I'm designing and implementing all (from scratch): my own protocols, advanced security tests, I'm creating SeLinux policies (domain transition, multi category security, etc) on ArchLinux (which is not fully compatible with existing policies, nor does it have official support), I'm using my own Cluster (with Corosync messaging layer and Pacemaker resource manager) better than Docker swarm, etc etc.
You can imagine how frustrated I am, and what this lost time means to me.
I no longer have patience to see how people learn to walk. (Of course, @eliasnaur is not "learning to walk", he already has experience. It is a generic comment)

I prefer Go before C / C ++ because it speeds up the work and already ensures typical programmer concerns (pointers, etc.) and I can focus more on my work.

I believe you filed a bug about reverse bindings being undefined and that the message about MIN_VALUE was unrelated?

No. Float.MIN_VALUE are working, but Build.BOARD does not.

I suspect you are directly using that value as a constant and if its constant expression looks like an integer that fits into 256 bits, the compiler will default its type to an integer. Perhaps return or use it as a float32 and you'll be good to go. .String() for integers even in plain Go is undefined, so unlike Java where you'll have an autoboxed/promoted type to Integer and finally java.lang.Integer.toString() method, in Go you'll need to use fmt's "%d" format specifier or strconv for formatting integers.

I want to be kind, I will not answer that. Thanks for your time.

At this step it is not necessary to write this, but I will:
Does not work:

package b

import "Java/android/os/Build"

func Test() {
     _ = Build.BOARD
}
package android.os;

public class Build {
...
  public static final String BOARD = null;
...
}

Note: BOARD field is final, once "instantiated" (initialized), its value cannot be modified. Even if Java (Dalvik) sends it to Go, it doesn't work.

Works:

package b

import "Java/java/lang/Float"

func Test3() {
   _ = Float.MIN_VALUE
}
package java.lang;

public class Float {
...
    public static final float MIN_VALUE = 1.4E-45F;
...
}

What is the difference?

  1. Float.MIN_VALUE type is float and Build.BOARD type is String.
  2. Float class can be instantiated and Build not.

Indeed, if it cannot be instantiated, there is no object. And Go cannot receive "object reference", such as b os.Build. But you should be able to access the constant fields just like Float.MIN_VALUE.

@eliasnaur

This comment has been minimized.

Copy link
Contributor

@eliasnaur eliasnaur commented Aug 19, 2019

Static methods and constants
After importing, the resulting packages SomeClass and InnerClass will contain the static methods and static final constants from their Java classes. For example
import "Java/java/lang/Float"
will expose (among others) the constant Float.MIN_VALUE and the function Float.ParseFloat.

Then, the same person answer this:

I'm sorry, but Java fields are not (yet) exposed by reverse bindings.

MIN_VALUE is a field of class Float, and works.
It is contradicting itself because it gives an example how to access Java fields and below it says that it is not possible to access Java fields.

The comment above refers to fields that are not constant (final static). PackageInfo.installLocation is not constant and therefore not supported.

You correctly point out that Build.BOARD is final static and therefore should be supported. It's been a while since I worked with gomobile, but my guess is that another restriction is that the field must also be a primitive type to be supported. You and I know that String is immutable and could be treated as a primitive constant, but go mobile doesn't know that (yet).

If you're still interested, I encourage you to take a stab at adding support of constant String fields to gomobile.

@mvasi90

This comment has been minimized.

Copy link
Author

@mvasi90 mvasi90 commented Aug 19, 2019

Okay. If I had known that the constants depend on the type, I would have started the project with C++. It's not too late, but I lost valuable time. Well, no problem.

If you're still interested, I encourage you to take a stab at adding support of constant String fields to gomobile.

I don't like to leave the projects unfinished, And I am very busy, I am doing the work of an entire company (system administrators, security, database, multiplatform client and server programming, etc etc)
In my spare time I am doing security tests with a ESP32 microcontroller, trying to access eFuse secret keys or bypass ECDSA signature validation.
If I do not find important vulnerabilities, I will begin to develop the second part of current project in this type of microcontrollers.

I think I don't be able to contribute for now. But of course, I appreciate your work. And if you can, please leave documented when something does not work.

@bcmills

This comment has been minimized.

Copy link
Member

@bcmills bcmills commented Aug 19, 2019

I'm having trouble figuring out what the feature request in this issue is: I see some questions in the original post, some discussion of previous proposals, and a comment from @eliasnaur suggesting a specific contribution, but I'm having trouble seeing the scope of that contribution or the component to which it would be applied, and moreover I'm hesitant to tag this issue help wanted due to the fairly hostile tone so far.

If there is a concrete feature request or bug fix to be resolved here, please open a new issue and directly describe the specific changes that are needed. Thanks.

@bcmills bcmills closed this Aug 19, 2019
@jay11ca39

This comment has been minimized.

Copy link

@jay11ca39 jay11ca39 commented Sep 6, 2019

Hello @mvasi90 ,
It is not related to this issue..

Acutally I want to explore reverse binding.. Can you please let me know which is the best place to start.. I couldn't find any concrete sample or documentation at:
https://github.com/golang/mobile/tree/master/example

Any help will be appreciated..:)

Edit: I want to define one java class and want to call a function of that class from go layer.

@mvasi90

This comment has been minimized.

Copy link
Author

@mvasi90 mvasi90 commented Sep 6, 2019

@jay11ca39 This is not a good place to ask that because they don't want to mix things up. At any moment the cleaning lady appears and throws a fight at you.

I strongly recommend that you take time to learn C or Rust for this purpose. You can make your own wrappers manually. x/mobile is interesting but it is not "mature" or stable.
Missing documentation. Volunteers do not specify the limits of existent wrappers. When someone asks something specific they answer incorrectly: For example: treat the Java fields and the constants as two different things. But a constant is a field.

A field may be a class (instance) field, such as java.io.Reader.lock , a static field, such as java.lang.Integer.MAX_VALUE , or an enum constant, such as java.lang.Thread.State.WAITING.
https://docs.oracle.com/javase/tutorial/reflect/member/field.html

Now, if you still want to do it in Golang, you can investigate here:
https://godoc.org/golang.org/x/mobile/cmd/gobind#hdr-Reverse_bindings
#16876

You can't access some Java fields from Go. I recommend you to invoke setters and getters from Go. If an existent class (Java source) does not have setters and getters you can make a wrapper class in Java an use it from Go. Also you can pass data between Java and Go with interface callback.

For reverse binding the best example is the asset package. Here is an example in which I also incorporate the use of callbacks:

Golang:

package mylib
import (
	"io/ioutil"
	"golang.org/x/mobile/asset"
)
// Send back information to Java from Golang (async)
type Callback interface {
	R(data []byte) // Response: byte slice (array in Java)
        E(error byte)  // Error: single byte (better than string or byte slice)
}

// call from Java Mylib.test(); (before this, you should set the context, read below: java side)
func Test(cb Callback) {
   // go func(){}() is optional, used to run the process in the background with goroutine (asynchronous operations)
   go func(cb Callback) {
      f, err := asset.Open("secret-encrypted.raw")
	if err != nil {
		// call java (callback)
                cb.E(0x01) // or 1 instead 0x01

                // I do not recommend sending compromising information to Java.
                // To make reverse engineering more difficult you can send single byte value as flag. 
                // Example: 0x01, 0x02, etc (for every error). And process errors with a switch in Java side.
                // cb.E("Unable to open file") // don't do this    
                // cb.E([]byte("Unable to open file")) // or this
		return
	}
	defer f.Close()
	b, err := ioutil.ReadAll(f)
	if err != nil {
                cb.E(0x02) // this is another error. You can process it from Java or better treat them all equally sending every time 0x01 or random number
		return
	}
        // call decrypt function
        // send back to Java decrypted data
        cb.R(decrypted_byte_slice)
   }(cb)
}

Java example (with Fragment):

import mylib.Mylib;
import mylib.Callback;
import go.Seq;


public class MyFragment extends Fragment {
private Context c;
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        c = context;
    }
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Seq.setContext(c);
        Mylib.test(new Callback() {
            @Override
            public void r(byte[] data) {
                  // process response
            }
 
           @Override
           public void e(byte error) {
                  switch(error) {
                         case 0x01:
                               // process error 0x01
                         break;
                          // ...
                  }
           }
        });
    }
}

Note: If you don't want to call Go from a Java Fragment, but from Activity, you can put all Go code inside:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);
        // ...
        // here Go call
    }

With the previous example you can do more than simple call Java from Go. As you can see, you can do background operations. Performing operations on tasks other than the main one adds a little more difficulty when reverse engineering is used. You can set a little random sleep (for example from 1 to 5 seconds) before sending the response to Java. (Android = Java).

  • If you don't want to call Go from java first and wait for response callback, but directly call Java from Go, if you are calling Java (Android) method you should build the Go lib using --bootclasspath param:

    gomobile bind -target android -ldflags "-w -s" -bootclasspath /home/user/Android/Sdk/platforms/android-<your-version>/android.jar .

    Note: -w and -s flags make the compiler remove the symbols table and debug information.
    https://golang.org/cmd/link/

    If you don't delete that debug information, you're giving away your Golang source code. And deleting it makes the compiled binary much smaller.

  • Or for your own Java/Android package and class you can use the param -classpath /path/to/your/java/classes. Android Studio stores the compiled classes in ProjectName/app/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes. But you can build a simple java package manually with javac. For example make a copy of your package and build it manually (as Java SE desktop) to help gomobile to make wrappers.

More low level example: (you can make your own C wrappers)
https://github.com/golang/mobile/blob/master/asset/asset_android.go

More info:
I know that all developers want to protect their content. Here is an example to verify the Android app signatures with Go:

  1. Check the package of running app.
    ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", os.Getpid()))
    cmdline contains the package-id of your app. If package is the same, go to step 2
    Note: The native library (Go in this case) have the same PID as the Android app.
    Note: cmdline contents starts with package-id and ends with \0 (null) character. Parse it correctly and verify that after your package-id, the next byte is \0 because a hacker can append chars to your own package-id.

    Example: com.myweb.myapp\0\0\0. Hacked version: com.myweb.myapp2\0\0\0
    Don't check: strings.HasPrefix() because returns true. Instead check if the next byte is \0 or the length of parsed package-name is equal.
    Recommendation:
    Parse package-id (removing \0), don't validate it, create masterkey using the package id bytes and later using the signature bytes (see below).

  2. Get the apk file path to read and parse the signatures directly from Go
    This step can be easy or more complicated.
    Easy if you send the signature from Java to Go and that will make the hacker who lives in his mother's basement bitter your life.
    Complicated if you find the loaded apk path, read it, parse signature (V1, V2 and/or V3) and verify.
    Verify does not mean having a condition (if true ok), instead use a master key created with signature as seed (and previously, step 1, precreated with package-id bytes as seed)

    1. Get files of all links stored in /proc//fd/. Readlink and check if any file have your package name and if ends with .apk.

    2. Read the apk file and get signatures.
      Creating your own implementation of Signature Scheme parser learning this:
      https://source.android.com/security/apksigning
      https://source.android.com/security/apksigning/v2
      https://source.android.com/security/apksigning/v3

      Or using any existent library (like apkverifier created by Avast team):
      https://github.com/avast/apkverifier

Example:

f, err := os.Open(fmt.Sprintf("/proc/%d/fd", os.Getpid()))
if err == nil {
    fs, err := f.Readdir(-1)
        if err == nil {
            for _, file := range fs {
                // readlink, like linux command
		l, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%s", os.Getpid(), file.Name()))
		if err == nil {
                    if strings.HasPrefix(l, "/data/app/") && strings.Contains(l, "<your-package-name>") && strings.HasSuffix(l, ".apk") {
                        res, err := apkverifier.Verify(l, nil)
                        if err == nil {
                            for _, cert := range res.SignerCerts {
                                for _, crt := range cert {
                                    // replace this by your function that creates the master key based on the raw signature.
                                    // Important: 
                                    // don't break the loop
                                    // don't check if the signature is Okay
                                    // a hacker can keep your signature and add another. Use all signatures to make your master key used to decrypt data (master key must created first on your desktop pc with bash script or go binary, and use it to encrypt important files from your desktop and put on your Android Project)
                                    fmt.Printf("Signature: %v\n", crt.Raw)
                                    // masterKeyCalculator(crt.Raw)
                                }
                            }	
                        }
                    break
                }
            }
        }
    }
}

I hope these examples help you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.